Fix create pseudo-console spawn issues in Windows (SpartanJ/ecode#856).

This commit is contained in:
Martín Lucas Golini
2026-03-20 12:41:57 -03:00
parent 047ce7abdd
commit 89c11fbd74
2 changed files with 40 additions and 49 deletions

View File

@@ -1,7 +1,9 @@
#ifndef EE_CONTAINERS_HPP
#define EE_CONTAINERS_HPP
#ifdef EEPP_NO_THIRDPARTY_CONTAINERS
#include <eepp/config.hpp>
#if defined( EEPP_NO_THIRDPARTY_CONTAINERS ) || ( defined( EE_DEBUG ) && defined( EE_COMPILER_MSVC ) )
#include <unordered_map>
#include <unordered_set>
#else
@@ -11,7 +13,7 @@
namespace EE {
#ifdef EEPP_NO_THIRDPARTY_CONTAINERS
#if defined( EEPP_NO_THIRDPARTY_CONTAINERS ) || ( defined( EE_DEBUG ) && defined( EE_COMPILER_MSVC ) )
template <typename Key, typename Value> using UnorderedMap = std::unordered_map<Key, Value>;

View File

@@ -248,42 +248,6 @@ static std::wstring stringToWideString( const std::string& str ) {
#define HANDLE_WIN_ERR( err ) HRESULT_FROM_WIN32( err ), PrintWinApiError( err )
static HRESULT InitializeStartupInfoAttachedToPseudoConsole( STARTUPINFOEXW* pStartupInfo,
HPCON hpc ) {
// Prepare Startup Information structure
STARTUPINFOEXW si;
ZeroMemory( &si, sizeof( si ) );
si.StartupInfo.cb = sizeof( STARTUPINFOEXW );
// Discover the size required for the list
SIZE_T bytesRequired;
InitializeProcThreadAttributeList( NULL, 1, 0, &bytesRequired );
// Allocate memory to represent the list
si.lpAttributeList =
(PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc( GetProcessHeap(), 0, bytesRequired );
if ( !si.lpAttributeList ) {
return E_OUTOFMEMORY;
}
// Initialize the list memory location
if ( !InitializeProcThreadAttributeList( si.lpAttributeList, 1, 0, &bytesRequired ) ) {
HeapFree( GetProcessHeap(), 0, si.lpAttributeList );
return HRESULT_FROM_WIN32( GetLastError() );
}
// Set the pseudoconsole information into the list
if ( !UpdateProcThreadAttribute( si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
hpc, sizeof( hpc ), NULL, NULL ) ) {
HeapFree( GetProcessHeap(), 0, si.lpAttributeList );
return HRESULT_FROM_WIN32( GetLastError() );
}
*pStartupInfo = si;
return S_OK;
}
Process::Process( AutoHandle&& hProcess, void* lpAttributeList, int pid ) :
mStatus( ProcessStatus::RUNNING ),
mExitCode( 1 ),
@@ -393,11 +357,13 @@ std::unique_ptr<Process> Process::createWithPipe( const std::string& program,
oss << ' ' << arg;
}
std::wstring commandLine = stringToWideString( oss.str() );
// TODO: If workingDirectory is relative, make it absolute (relative to the
// current process working directory)
std::vector<wchar_t> cmdLineMutable(commandLine.begin(), commandLine.end());
cmdLineMutable.push_back(0);
std::wstring workingDir = stringToWideString( workingDirectory );
if ( CreateProcessW( NULL, (LPWSTR)commandLine.c_str(), NULL, NULL, TRUE, 0, NULL,
if ( CreateProcessW( NULL, (LPWSTR)cmdLineMutable.data(), NULL, NULL, TRUE, 0, NULL,
workingDir.empty() ? NULL : workingDir.c_str(), &siStartInfo,
&piProcInfo ) ) {
std::unique_ptr<Pipe> pipe = std::unique_ptr<Pipe>(
@@ -434,6 +400,12 @@ Process::createWithPseudoTerminal( const std::string& program, const std::vector
AutoHandle hThread{};
STARTUPINFOEXW startupInfo{};
startupInfo.StartupInfo.cb = sizeof( STARTUPINFOEXW );
startupInfo.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
startupInfo.StartupInfo.hStdInput = INVALID_HANDLE_VALUE;
startupInfo.StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
startupInfo.StartupInfo.hStdError = INVALID_HANDLE_VALUE;
PROCESS_INFORMATION piClient{};
std::ostringstream oss;
@@ -442,19 +414,32 @@ Process::createWithPseudoTerminal( const std::string& program, const std::vector
oss << ' ' << arg;
}
std::wstring commandLine = stringToWideString( oss.str() );
// TODO: If workingDirectory is relative, make it absolute (relative to the
// current process working directory)
std::wstring workingDir = stringToWideString( workingDirectory );
if ( ( hr = InitializeStartupInfoAttachedToPseudoConsole( &startupInfo,
pseudoTerminal.mPHPC ) ) != S_OK ) {
Log::error( "InitializeStartupInfoAttachedToPseudoConsole failed." );
PrintErrorResult( hr );
SIZE_T bytesRequired = 0;
InitializeProcThreadAttributeList( NULL, 1, 0, &bytesRequired );
startupInfo.lpAttributeList =
(PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc( GetProcessHeap(), 0, bytesRequired );
if ( !startupInfo.lpAttributeList ) {
Log::error( "HeapAlloc failed for attribute list." );
goto fail;
}
if ( !InitializeProcThreadAttributeList( startupInfo.lpAttributeList, 1, 0, &bytesRequired ) ) {
Log::error( "InitializeProcThreadAttributeList failed." );
goto fail;
}
if ( !UpdateProcThreadAttribute( startupInfo.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
pseudoTerminal.mPHPC, sizeof( HPCON ), NULL, NULL ) ) {
Log::error( "UpdateProcThreadAttribute failed." );
goto fail;
}
hr = CreateProcessW( NULL, // No module name - use Command Line
(wchar_t*)commandLine.c_str(), // Command Line
commandLine.data(), // Must be a mutable buffer to prevent ERROR_INSUFFICIENT_BUFFER
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Inherit handles
@@ -480,6 +465,10 @@ Process::createWithPseudoTerminal( const std::string& program, const std::vector
return std::unique_ptr<Process>(
new Process( std::move( hProcess ), startupInfo.lpAttributeList, piClient.dwProcessId ) );
fail:
if ( startupInfo.lpAttributeList ) {
DeleteProcThreadAttributeList( startupInfo.lpAttributeList );
HeapFree( GetProcessHeap(), 0, startupInfo.lpAttributeList );
}
return std::unique_ptr<Process>();
}
@@ -489,4 +478,4 @@ int Process::pid() {
}} // namespace eterm::System
#endif
#endif