From 89c11fbd7433aa810b59ee7bbaceac1a091c2f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 20 Mar 2026 12:41:57 -0300 Subject: [PATCH] Fix create pseudo-console spawn issues in Windows (SpartanJ/ecode#856). --- include/eepp/core/containers.hpp | 6 +- .../eterm/src/eterm/system/process.cpp | 83 ++++++++----------- 2 files changed, 40 insertions(+), 49 deletions(-) diff --git a/include/eepp/core/containers.hpp b/include/eepp/core/containers.hpp index 10d52d1ad..db0524ed5 100644 --- a/include/eepp/core/containers.hpp +++ b/include/eepp/core/containers.hpp @@ -1,7 +1,9 @@ #ifndef EE_CONTAINERS_HPP #define EE_CONTAINERS_HPP -#ifdef EEPP_NO_THIRDPARTY_CONTAINERS +#include + +#if defined( EEPP_NO_THIRDPARTY_CONTAINERS ) || ( defined( EE_DEBUG ) && defined( EE_COMPILER_MSVC ) ) #include #include #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 using UnorderedMap = std::unordered_map; diff --git a/src/modules/eterm/src/eterm/system/process.cpp b/src/modules/eterm/src/eterm/system/process.cpp index 5096493c5..b003b7458 100644 --- a/src/modules/eterm/src/eterm/system/process.cpp +++ b/src/modules/eterm/src/eterm/system/process.cpp @@ -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::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 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 = std::unique_ptr( @@ -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( 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(); } @@ -489,4 +478,4 @@ int Process::pid() { }} // namespace eterm::System -#endif +#endif \ No newline at end of file