main: Unify command line handling between standard Windows and GDK targets.
Reference Issue #14510.
This commit is contained in:
@@ -26,6 +26,8 @@
|
|||||||
|
|
||||||
#include "../../video/SDL_surface_c.h"
|
#include "../../video/SDL_surface_c.h"
|
||||||
|
|
||||||
|
#include <shellapi.h> // CommandLineToArgvW()
|
||||||
|
|
||||||
#include <objbase.h> // for CoInitialize/CoUninitialize (Win32 only)
|
#include <objbase.h> // for CoInitialize/CoUninitialize (Win32 only)
|
||||||
#ifdef HAVE_ROAPI_H
|
#ifdef HAVE_ROAPI_H
|
||||||
#include <roapi.h> // For RoInitialize/RoUninitialize (Win32 only)
|
#include <roapi.h> // 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);
|
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)
|
#endif // defined(SDL_PLATFORM_WINDOWS)
|
||||||
|
|||||||
@@ -209,6 +209,9 @@ extern SDL_AudioFormat SDL_WaveFormatExToSDLFormat(WAVEFORMATEX *waveformat);
|
|||||||
// WideCharToMultiByte, but with some WinXP management.
|
// 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);
|
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++
|
// Ends C function definitions when using C++
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,54 +30,16 @@ extern "C" {
|
|||||||
#include <shellapi.h> // CommandLineToArgvW()
|
#include <shellapi.h> // CommandLineToArgvW()
|
||||||
#include <appnotify.h>
|
#include <appnotify.h>
|
||||||
|
|
||||||
// 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"
|
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;
|
(void)reserved;
|
||||||
char **argv = _argv;
|
|
||||||
int argc = _argc;
|
|
||||||
|
|
||||||
if (!argv) {
|
void *heap_allocated = NULL;
|
||||||
// Get the arguments with GetCommandLine, convert them to argc and argv
|
const char *args_error = WIN_CheckDefaultArgcArgv(&argc, &argv, &heap_allocated);
|
||||||
LPWSTR *argvw = CommandLineToArgvW(GetCommandLineW(), &argc);
|
if (args_error) {
|
||||||
if (argvw == NULL) {
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", args_error, NULL);
|
||||||
return OutOfMemory();
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = -1;
|
int result = -1;
|
||||||
@@ -135,12 +97,8 @@ int SDL_RunApp(int _argc, char **_argv, SDL_main_func mainFunction, void *reserv
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free argv, to avoid memory leak
|
if (heap_allocated) {
|
||||||
if (allocated_argv) {
|
HeapFree(GetProcessHeap(), 0, heap_allocated);
|
||||||
for (int i = 0; i < argc; ++i) {
|
|
||||||
HeapFree(GetProcessHeap(), 0, allocated_argv[i]);
|
|
||||||
}
|
|
||||||
HeapFree(GetProcessHeap(), 0, allocated_argv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -27,82 +27,22 @@
|
|||||||
/* Win32-specific SDL_RunApp(), which does most of the SDL_main work,
|
/* 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 */
|
based on SDL_windows_main.c, placed in the public domain by Sam Lantinga 4/13/98 */
|
||||||
|
|
||||||
#include <shellapi.h> // CommandLineToArgvW()
|
int MINGW32_FORCEALIGN SDL_RunApp(int argc, char *argv[], SDL_main_func mainFunction, void *reserved)
|
||||||
|
|
||||||
static int OutOfMemory(void)
|
|
||||||
{
|
{
|
||||||
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;
|
(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.
|
int result = -1;
|
||||||
// Otherwise, we take a NULL argv as an instruction for SDL to parse the command line into an argv.
|
void *heap_allocated = NULL;
|
||||||
// On Windows, when SDL provides the main entry point, argv is always NULL.
|
const char *args_error = WIN_CheckDefaultArgcArgv(&argc, &argv, &heap_allocated);
|
||||||
if (caller_argv && caller_argc >= 0) {
|
if (args_error) {
|
||||||
result = mainFunction(caller_argc, caller_argv);
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", args_error, NULL);
|
||||||
} else {
|
} 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();
|
SDL_SetMainReady();
|
||||||
result = mainFunction(argc, argv);
|
result = mainFunction(argc, argv);
|
||||||
|
if (heap_allocated) {
|
||||||
cleanup:
|
HeapFree(GetProcessHeap(), 0, heap_allocated);
|
||||||
HeapFree(GetProcessHeap(), 0, argv);
|
}
|
||||||
LocalFree(argvw);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user