Fix for SpartanJ/ecode#461. Used CreateProcess is the Unicode version ("CreateProcessW"), and binary paths are now resolved as absolute paths in Windows.

This commit is contained in:
Martín Lucas Golini
2025-05-11 19:45:42 -03:00
parent af381e400a
commit 058b9cab4d
5 changed files with 203 additions and 42 deletions

View File

@@ -134,10 +134,10 @@ bool Process::create( const std::string& command, const std::vector<std::string>
if ( mProcess )
return false;
std::vector<const char*> strings;
std::string rcommand;
mProcess = eeMalloc( sizeof( subprocess_s ) );
memset( mProcess, 0, sizeof( subprocess_s ) );
if ( !environment.empty() ) {
std::string rcommand;
if ( FileSystem::fileExists( command ) ) {
rcommand = command;
} else {
@@ -188,7 +188,16 @@ bool Process::create( const std::string& command, const std::vector<std::string>
return ret;
}
strings.push_back( command.c_str() );
if ( ( options & UseAbsolutePath ) && command.find_first_of( "\\/" ) == std::string::npos ) {
rcommand = Sys::which( command );
if ( !rcommand.empty() )
strings.push_back( rcommand.c_str() );
else
strings.push_back( command.c_str() );
} else {
strings.push_back( command.c_str() );
}
for ( size_t i = 0; i < cmdArr.size(); ++i )
strings.push_back( cmdArr[i].c_str() );
strings.push_back( NULL );

View File

@@ -39,6 +39,7 @@
/* disable warning: '__cplusplus' is not defined as a preprocessor macro,
* replacing with '0' for '#if/#elif' */
#pragma warning(disable : 4668)
#define _WINBASE_
#endif
#include <stdio.h>
@@ -282,9 +283,9 @@ struct subprocess_security_attributes_s {
struct subprocess_startup_info_s {
unsigned long cb;
char *lpReserved;
char *lpDesktop;
char *lpTitle;
wchar_t* lpReserved;
wchar_t* lpDesktop;
wchar_t* lpTitle;
unsigned long dwX;
unsigned long dwY;
unsigned long dwXSize;
@@ -301,6 +302,48 @@ struct subprocess_startup_info_s {
void *hStdError;
};
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef int BOOL;
typedef wchar_t* LPWSTR;
typedef const wchar_t* LPCWSTR;
typedef unsigned char* LPBYTE;
typedef void* HANDLE;
typedef void* LPVOID;
typedef char CHAR;
#define OFS_MAXPATHNAME 128
typedef struct _OFSTRUCT {
BYTE cBytes;
BYTE fFixedDisk;
WORD nErrCode;
WORD Reserved1;
WORD Reserved2;
CHAR szPathName[OFS_MAXPATHNAME];
} OFSTRUCT, *LPOFSTRUCT, *POFSTRUCT;
typedef struct _STARTUPINFOW {
DWORD cb;
LPWSTR lpReserved;
LPWSTR lpDesktop;
LPWSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
LPBYTE lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFOW, *LPSTARTUPINFOW;
struct subprocess_overlapped_s {
uintptr_t Internal;
uintptr_t InternalHigh;
@@ -354,6 +397,74 @@ __declspec(dllimport) int __stdcall GetOverlappedResult(void *, LPOVERLAPPED,
unsigned long *, int);
__declspec(dllimport) int __stdcall FlushFileBuffers(void *);
__declspec(dllimport) int __stdcall CreateProcessW(
const wchar_t* lpApplicationName, wchar_t* lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
const wchar_t* lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation );
#ifndef CP_UTF8
#define CP_UTF8 65001 // UTF-8 translation
#endif
#ifndef MB_ERR_INVALID_CHARS
#define MB_ERR_INVALID_CHARS 0x00000008 // error for invalid chars
#endif
#ifndef CREATE_UNICODE_ENVIRONMENT
#define CREATE_UNICODE_ENVIRONMENT 0x00000400
#endif
__declspec( dllimport ) int __stdcall MultiByteToWideChar(
unsigned int CodePage, // UINT
unsigned long dwFlags, // DWORD
const char* lpMultiByteStr, // LPCCH -> const char*
int cbMultiByte,
wchar_t* lpWideCharStr, // LPWSTR -> wchar_t*
int cchWideChar );
wchar_t* Utf8ToUtf16_C( const char* utf8Str ) {
if ( utf8Str == NULL ) {
return NULL;
}
int wideCharCount = MultiByteToWideChar(
CP_UTF8,
MB_ERR_INVALID_CHARS,
utf8Str,
-1,
NULL,
0
);
if ( wideCharCount == 0 ) {
return NULL;
}
wchar_t* wideStr = (wchar_t*)malloc( wideCharCount * sizeof( wchar_t ) );
if ( wideStr == NULL ) {
return NULL;
}
int result = MultiByteToWideChar( CP_UTF8,
MB_ERR_INVALID_CHARS, // Use same flags
utf8Str, -1,
wideStr, // Destination buffer
wideCharCount // Size of destination buffer in WCHARs
);
if ( result == 0 ) {
free( wideStr );
return NULL;
}
return wideStr;
}
#if defined(_DLL)
#define SUBPROCESS_DLLIMPORT __declspec(dllimport)
#else
@@ -500,15 +611,15 @@ int subprocess_create_ex(const char *const commandLine[], int options,
subprocess_size_t len;
int i, j;
int need_quoting;
unsigned long flags = 0;
unsigned long flags = CREATE_UNICODE_ENVIRONMENT;
const unsigned long startFUseStdHandles = 0x00000100;
const unsigned long handleFlagInherit = 0x00000001;
const unsigned long createNoWindow = 0x08000000;
struct subprocess_subprocess_information_s processInfo;
struct subprocess_security_attributes_s saAttr = {sizeof(saAttr),
SUBPROCESS_NULL, 1};
char *used_environment = SUBPROCESS_NULL;
struct subprocess_startup_info_s startInfo = {0,
wchar_t *used_environment = SUBPROCESS_NULL;
STARTUPINFOW startInfo = { 0,
SUBPROCESS_NULL,
SUBPROCESS_NULL,
SUBPROCESS_NULL,
@@ -537,36 +648,57 @@ int subprocess_create_ex(const char *const commandLine[], int options,
if (subprocess_option_inherit_environment !=
(options & subprocess_option_inherit_environment)) {
if (SUBPROCESS_NULL == environment) {
used_environment = SUBPROCESS_CONST_CAST(char *, "\0\0");
used_environment = SUBPROCESS_NULL;
} else {
// We always end with two null terminators.
len = 2;
if ( environment[0] == NULL ) {
used_environment = SUBPROCESS_NULL;
} else {
size_t total_wchars_for_env = 0;
for ( i = 0; environment[i] != NULL; i++ ) {
int wchars_for_str = MultiByteToWideChar( CP_UTF8, 0, environment[i], -1, NULL, 0 );
if ( wchars_for_str == 0 ) {
if ( environment[i][0] != '\0' )
return -1;
wchars_for_str = 1;
}
total_wchars_for_env += wchars_for_str;
}
total_wchars_for_env += 1;
for (i = 0; environment[i]; i++) {
for (j = 0; '\0' != environment[i][j]; j++) {
len++;
}
used_environment = (wchar_t*)malloc( total_wchars_for_env * sizeof( wchar_t ) );
if ( !used_environment )
return -1;
// For the null terminator too.
len++;
}
wchar_t* current_env_ptr = used_environment;
size_t remaining_wchar_capacity = total_wchars_for_env;
for ( i = 0; environment[i] != NULL; i++ ) {
int wchars_for_str = MultiByteToWideChar( CP_UTF8, 0, environment[i], -1, NULL, 0 );
if ( wchars_for_str == 0 ) {
if ( environment[i][0] != '\0' )
return -1;
wchars_for_str = 1;
}
used_environment = SUBPROCESS_CAST(char *, _alloca(len));
if ( wchars_for_str > remaining_wchar_capacity ) {
return -1;
}
// Re-use len for the insertion position
len = 0;
int wchars_written = MultiByteToWideChar(
CP_UTF8, 0, environment[i], -1, current_env_ptr, remaining_wchar_capacity );
if ( wchars_written == 0 ||
wchars_written != wchars_for_str ) {
return -1;
}
current_env_ptr += wchars_written;
remaining_wchar_capacity -= wchars_written;
}
for (i = 0; environment[i]; i++) {
for (j = 0; '\0' != environment[i][j]; j++) {
used_environment[len++] = environment[i][j];
}
used_environment[len++] = '\0';
}
// End with the two null terminators.
used_environment[len++] = '\0';
used_environment[len++] = '\0';
if ( remaining_wchar_capacity > 0 ) {
*current_env_ptr = L'\0';
} else {
return -1;
}
}
}
} else {
if (SUBPROCESS_NULL != environment) {
@@ -744,20 +876,30 @@ int subprocess_create_ex(const char *const commandLine[], int options,
commandLineCombined[len] = '\0';
if (!CreateProcessA(
wchar_t* commandLineCombinedW = Utf8ToUtf16_C( commandLineCombined );
wchar_t* working_directoryW = Utf8ToUtf16_C( working_directory );
if (!CreateProcessW(
SUBPROCESS_NULL,
commandLineCombined, // command line
commandLineCombinedW, // command line
SUBPROCESS_NULL, // process security attributes
SUBPROCESS_NULL, // primary thread security attributes
1, // handles are inherited
flags, // creation flags
used_environment, // used environment
working_directory, // set process working directory
SUBPROCESS_PTR_CAST(LPSTARTUPINFOA,
used_environment, // used environment
working_directoryW, // set process working directory
SUBPROCESS_PTR_CAST( LPSTARTUPINFOW,
&startInfo), // STARTUPINFO pointer
SUBPROCESS_PTR_CAST(LPPROCESS_INFORMATION, &processInfo))) {
free( commandLineCombinedW );
free( used_environment );
free( working_directoryW );
return -1;
}
free( commandLineCombinedW );
free( used_environment );
free( working_directoryW );
out_process->hProcess = processInfo.hProcess;

View File

@@ -40,10 +40,13 @@ int Git::git( const std::string& args, const std::string& projectDir, std::strin
Clock clock;
buf.clear();
Process p;
p.create( mGitPath, args,
if ( !p.create( mGitPath, args,
Process::CombinedStdoutStderr | Process::Options::NoWindow |
Process::Options::EnableAsync | Process::Options::InheritEnvironment,
{ { "LC_ALL", "en_US.UTF-8" } }, projectDir.empty() ? mProjectPath : projectDir );
{ { "LC_ALL", "en_US.UTF-8" } },
projectDir.empty() ? mProjectPath : projectDir ) ) {
return EXIT_FAILURE;
}
p.readAllStdOut( buf );
int retCode = 0;
p.join( &retCode );

View File

@@ -1310,8 +1310,11 @@ bool LSPClientServer::start() {
}
if ( !cmd.empty() ) {
bool ret = mProcess.create( cmd, Process::getDefaultOptions() | Process::EnableAsync,
mLSP.env, mRootPath );
auto flags = Process::getDefaultOptions() | Process::EnableAsync;
#if EE_PLATFORM == EE_PLATFORM_WIN
flags |= Process::UseAbsolutePath;
#endif
bool ret = mProcess.create( cmd, flags, mLSP.env, mRootPath );
if ( ret ) {
if ( mProcess.isAlive() ) {
mUsingProcess = true;