diff --git a/src/core/windows/SDL_windows.c b/src/core/windows/SDL_windows.c index 6bcc8f5ee3..c64711d403 100644 --- a/src/core/windows/SDL_windows.c +++ b/src/core/windows/SDL_windows.c @@ -26,6 +26,8 @@ #include "../../video/SDL_surface_c.h" +#include // CommandLineToArgvW() + #include // for CoInitialize/CoUninitialize (Win32 only) #ifdef HAVE_ROAPI_H #include // For RoInitialize/RoUninitialize (Win32 only) @@ -650,4 +652,72 @@ int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, return WideCharToMultiByte(CodePage, dwFlags, lpWideCharStr, cchWideChar, lpMultiByteStr, cbMultiByte, lpDefaultChar, lpUsedDefaultChar); } +const char *WIN_CheckDefaultArgcArgv(int *pargc, char ***pargv, void **pallocated) +{ + // If the provided argv is valid, we pass it to the main function as-is, since it's probably what the user wants. + // Otherwise, we take a NULL argv as an instruction for SDL to parse the command line into an argv. + // On Windows, when SDL provides the main entry point, argv is always NULL. + + const char *out_of_mem_str = "Out of memory - aborting"; + const char *proc_err_str = "Error processing command line arguments - aborting"; + + *pallocated = NULL; + + if (*pargv) { + return NULL; // just go with what was provided, no error message. + } + + // We need to be careful about how we allocate/free memory here. We can't use SDL_alloc()/SDL_free() + // because the application might have used SDL_SetMemoryFunctions() to change the allocator. + LPWSTR *argvw = NULL; + char **argv = NULL; + + const LPWSTR command_line = GetCommandLineW(); + + // Because of how the Windows command line is structured, we know for sure that the buffer size required to + // store all argument strings converted to UTF-8 (with null terminators) is guaranteed to be less than or equal + // to the size of the original command line string converted to UTF-8. + const int argdata_size = WideCharToMultiByte(CP_UTF8, 0, command_line, -1, NULL, 0, NULL, NULL); // Includes the null terminator + if (!argdata_size) { + return proc_err_str; + } + + int argc = -1; + argvw = CommandLineToArgvW(command_line, &argc); + if (!argvw || argc < 0) { + return out_of_mem_str; + } + + // Allocate argv followed by the argument string buffer as one contiguous allocation. + argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv) + argdata_size); + if (!argv) { + LocalFree(argvw); + return out_of_mem_str; + } + + char *argdata = ((char *)argv) + (argc + 1) * sizeof(*argv); + int argdata_index = 0; + + for (int i = 0; i < argc; ++i) { + const int bytes_written = WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, argdata + argdata_index, argdata_size - argdata_index, NULL, NULL); + if (!bytes_written) { + HeapFree(GetProcessHeap(), 0, argv); + LocalFree(argvw); + return proc_err_str; + } + argv[i] = argdata + argdata_index; + argdata_index += bytes_written; + } + + argv[argc] = NULL; + + LocalFree(argvw); + + *pargc = argc; + *pallocated = argv; + *pargv = argv; + + return NULL; // no error string. +} + #endif // defined(SDL_PLATFORM_WINDOWS) diff --git a/src/core/windows/SDL_windows.h b/src/core/windows/SDL_windows.h index 7dfbc7daf6..8305aef831 100644 --- a/src/core/windows/SDL_windows.h +++ b/src/core/windows/SDL_windows.h @@ -209,6 +209,9 @@ extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat); // WideCharToMultiByte, but with some WinXP management. extern int WIN_WideCharToMultiByte(UINT CodePage, DWORD dwFlags, LPCWCH lpWideCharStr, int cchWideChar, LPSTR lpMultiByteStr, int cbMultiByte, LPCCH lpDefaultChar, LPBOOL lpUsedDefaultChar); +// parse out command lines from OS if argv is NULL, otherwise pass through unchanged. `*pallocated` must be HeapFree'd by caller if not NULL on successful return. Returns NULL on success, error string on problems. +const char *WIN_CheckDefaultArgcArgv(int *pargc, char ***pargv, void **pallocated); + // Ends C function definitions when using C++ #ifdef __cplusplus } diff --git a/src/main/gdk/SDL_sysmain_runapp.cpp b/src/main/gdk/SDL_sysmain_runapp.cpp index c4b4b905c1..3eeac97dfb 100644 --- a/src/main/gdk/SDL_sysmain_runapp.cpp +++ b/src/main/gdk/SDL_sysmain_runapp.cpp @@ -30,54 +30,16 @@ extern "C" { #include // CommandLineToArgvW() #include -// Pop up an out of memory message, returns to Windows -static BOOL OutOfMemory(void) -{ - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Out of memory - aborting", NULL); - return FALSE; -} - extern "C" -int SDL_RunApp(int _argc, char **_argv, SDL_main_func mainFunction, void *reserved) +int SDL_RunApp(int argc, char **argv, SDL_main_func mainFunction, void *reserved) { - char **allocated_argv = NULL; - char **argv = _argv; - int argc = _argc; + (void)reserved; - if (!argv) { - // Get the arguments with GetCommandLine, convert them to argc and argv - LPWSTR *argvw = CommandLineToArgvW(GetCommandLineW(), &argc); - if (argvw == NULL) { - return OutOfMemory(); - } - - // Note that we need to be careful about how we allocate/free memory here. - // If the application calls SDL_SetMemoryFunctions(), we can't rely on - // SDL_free() to use the same allocator after SDL_main() returns. - - argv = allocated_argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv)); - if (argv == NULL) { - return OutOfMemory(); - } - for (int i = 0; i < argc; ++i) { - const int utf8size = WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, NULL, 0, NULL, NULL); - if (!utf8size) { // uhoh? - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments", NULL); - return -1; - } - - argv[i] = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, utf8size); // this size includes the null-terminator character. - if (!argv[i]) { - return OutOfMemory(); - } - - if (WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, argv[i], utf8size, NULL, NULL) == 0) { // failed? uhoh! - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments", NULL); - return -1; - } - } - argv[argc] = NULL; - LocalFree(argvw); + void *heap_allocated = NULL; + const char *args_error = WIN_CheckDefaultArgcArgv(&argc, &argv, &heap_allocated); + if (args_error) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", args_error, NULL); + return -1; } int result = -1; @@ -135,12 +97,8 @@ int SDL_RunApp(int _argc, char **_argv, SDL_main_func mainFunction, void *reserv #endif } - // Free argv, to avoid memory leak - if (allocated_argv) { - for (int i = 0; i < argc; ++i) { - HeapFree(GetProcessHeap(), 0, allocated_argv[i]); - } - HeapFree(GetProcessHeap(), 0, allocated_argv); + if (heap_allocated) { + HeapFree(GetProcessHeap(), 0, heap_allocated); } return result; diff --git a/src/main/windows/SDL_sysmain_runapp.c b/src/main/windows/SDL_sysmain_runapp.c index 4ece8a4c70..a53042049b 100644 --- a/src/main/windows/SDL_sysmain_runapp.c +++ b/src/main/windows/SDL_sysmain_runapp.c @@ -27,82 +27,22 @@ /* Win32-specific SDL_RunApp(), which does most of the SDL_main work, based on SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98 */ -#include // CommandLineToArgvW() - -static int OutOfMemory(void) +int MINGW32_FORCEALIGN SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void *reserved) { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Out of memory - aborting", NULL); - return -1; -} - -static int ErrorProcessingCommandLine(void) -{ - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments - aborting", NULL); - return -1; -} - -int MINGW32_FORCEALIGN SDL_RunApp(int caller_argc, char *caller_argv[], SDL_main_func mainFunction, void * reserved) -{ - int result; (void)reserved; - // If the provided argv is valid, we pass it to the main function as-is, since it's probably what the user wants. - // Otherwise, we take a NULL argv as an instruction for SDL to parse the command line into an argv. - // On Windows, when SDL provides the main entry point, argv is always NULL. - if (caller_argv && caller_argc >= 0) { - result = mainFunction(caller_argc, caller_argv); + int result = -1; + void *heap_allocated = NULL; + const char *args_error = WIN_CheckDefaultArgcArgv(&argc, &argv, &heap_allocated); + if (args_error) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", args_error, NULL); } else { - // We need to be careful about how we allocate/free memory here. We can't use SDL_alloc()/SDL_free() - // because the application might have used SDL_SetMemoryFunctions() to change the allocator. - LPWSTR *argvw = NULL; - char **argv = NULL; - - const LPWSTR command_line = GetCommandLineW(); - - // Because of how the Windows command line is structured, we know for sure that the buffer size required to - // store all argument strings converted to UTF-8 (with null terminators) is guaranteed to be less than or equal - // to the size of the original command line string converted to UTF-8. - const int argdata_size = WideCharToMultiByte(CP_UTF8, 0, command_line, -1, NULL, 0, NULL, NULL); // Includes the null terminator - if (!argdata_size) { - result = ErrorProcessingCommandLine(); - goto cleanup; - } - - int argc; - argvw = CommandLineToArgvW(command_line, &argc); - if (!argvw || argc < 0) { - result = OutOfMemory(); - goto cleanup; - } - - // Allocate argv followed by the argument string buffer as one contiguous allocation. - argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv) + argdata_size); - if (!argv) { - result = OutOfMemory(); - goto cleanup; - } - char *argdata = ((char *)argv) + (argc + 1) * sizeof(*argv); - int argdata_index = 0; - - for (int i = 0; i < argc; ++i) { - const int bytes_written = WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, argdata + argdata_index, argdata_size - argdata_index, NULL, NULL); - if (!bytes_written) { - result = ErrorProcessingCommandLine(); - goto cleanup; - } - argv[i] = argdata + argdata_index; - argdata_index += bytes_written; - } - argv[argc] = NULL; - SDL_SetMainReady(); result = mainFunction(argc, argv); - - cleanup: - HeapFree(GetProcessHeap(), 0, argv); - LocalFree(argvw); + if (heap_allocated) { + HeapFree(GetProcessHeap(), 0, heap_allocated); + } } - return result; }