Skip to content

Commit c6f979f

Browse files
committed
main: Update GDK argv handling to match new Windows implementation
1 parent dd53dbd commit c6f979f

File tree

1 file changed

+58
-42
lines changed

1 file changed

+58
-42
lines changed

src/main/gdk/SDL_sysmain_runapp.cpp

+58-42
Original file line numberDiff line numberDiff line change
@@ -31,58 +31,76 @@ extern "C" {
3131
#include <shellapi.h> // CommandLineToArgvW()
3232
#include <appnotify.h>
3333

34-
// Pop up an out of memory message, returns to Windows
35-
static BOOL OutOfMemory(void)
34+
static int OutOfMemory(void)
3635
{
3736
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Out of memory - aborting", NULL);
38-
return FALSE;
37+
return -1;
38+
}
39+
40+
static int ErrorProcessingCommandLine(void)
41+
{
42+
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments", NULL);
43+
return -1;
3944
}
4045

41-
/* Gets the arguments with GetCommandLine, converts them to argc and argv
42-
and calls SDL_main */
4346
extern "C"
44-
int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *)
47+
int SDL_RunApp(int caller_argc, char* caller_argv[], SDL_main_func mainFunction, void *)
4548
{
46-
LPWSTR *argvw;
47-
char **argv;
48-
int i, argc, result;
49+
int result, argc;
50+
LPWSTR *argvw = NULL;
51+
char **argv = NULL;
4952
HRESULT hr;
5053
XTaskQueueHandle taskQueue;
5154

52-
argvw = CommandLineToArgvW(GetCommandLineW(), &argc);
53-
if (argvw == NULL) {
54-
return OutOfMemory();
55-
}
55+
// Note that we need to be careful about how we allocate/free memory in this function. If the application calls
56+
// SDL_SetMemoryFunctions(), we can't rely on SDL_free() to use the same allocator after SDL_main() returns.
5657

57-
/* Note that we need to be careful about how we allocate/free memory here.
58-
* If the application calls SDL_SetMemoryFunctions(), we can't rely on
59-
* SDL_free() to use the same allocator after SDL_main() returns.
60-
*/
58+
if (!caller_argv || caller_argc < 0) {
59+
// If the passed argv is NULL or argc is negative, the user expects SDL to get the command line arguments
60+
// using GetCommandLineW() and convert them to argc and argv before calling mainFunction().
6161

62-
// Parse it into argv and argc
63-
argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv));
64-
if (argv == NULL) {
65-
return OutOfMemory();
66-
}
67-
for (i = 0; i < argc; ++i) {
68-
const int utf8size = WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, NULL, 0, NULL, NULL);
69-
if (!utf8size) { // uhoh?
70-
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments", NULL);
71-
return -1;
62+
// Because of how the Windows command line works, we know for sure that the buffer size required to store all
63+
// argument strings converted to UTF-8 (with null terminators) is guaranteed to be less than or equal to the
64+
// size of the original command line string converted to UTF-8.
65+
const int argdata_size = WideCharToMultiByte(CP_UTF8, 0, GetCommandLineW(), -1, NULL, 0, NULL, NULL); // Includes the null terminator
66+
if (!argdata_size) {
67+
result = ErrorProcessingCommandLine();
68+
goto cleanup;
7269
}
7370

74-
argv[i] = (char *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, utf8size); // this size includes the null-terminator character.
75-
if (!argv[i]) {
76-
return OutOfMemory();
71+
argvw = CommandLineToArgvW(GetCommandLineW(), &argc);
72+
if (!argvw || argc < 0) {
73+
result = OutOfMemory();
74+
goto cleanup;
7775
}
7876

79-
if (WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, argv[i], utf8size, NULL, NULL) == 0) { // failed? uhoh!
80-
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal Error", "Error processing command line arguments", NULL);
81-
return -1;
77+
// Allocate argv followed by the argument string buffer as one contiguous allocation.
78+
argv = (char **)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (argc + 1) * sizeof(*argv) + argdata_size);
79+
if (!argv) {
80+
result = OutOfMemory();
81+
goto cleanup;
82+
}
83+
char *argdata = ((char *)argv) + (argc + 1) * sizeof(*argv);
84+
int argdata_index = 0;
85+
86+
for (int i = 0; i < argc; ++i) {
87+
const int bytes_written = WideCharToMultiByte(CP_UTF8, 0, argvw[i], -1, argdata + argdata_index, argdata_size - argdata_index, NULL, NULL);
88+
if (!bytes_written) {
89+
result = ErrorProcessingCommandLine();
90+
goto cleanup;
91+
}
92+
argv[i] = argdata + argdata_index;
93+
argdata_index += bytes_written;
8294
}
95+
argv[argc] = NULL;
96+
97+
argvw = NULL;
98+
99+
caller_argc = argc;
100+
caller_argv = argv;
83101
}
84-
argv[i] = NULL;
85-
LocalFree(argvw);
102+
103+
// !!! FIXME: This function does not currently properly deinitialize GDK resources on failure.
86104

87105
hr = XGameRuntimeInitialize();
88106

@@ -100,6 +118,7 @@ int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *)
100118
xblArgs.queue = taskQueue;
101119
SDL_snprintf(scidBuffer, 64, "00000000-0000-0000-0000-0000%08X", titleid);
102120
xblArgs.scid = scidBuffer;
121+
// !!! FIXME: XblInitialize() should have a corresponding call to XblCleanup() according to the docs.
103122
hr = XblInitialize(&xblArgs);
104123
} else {
105124
SDL_SetError("[GDK] Unable to get titleid. Will not call XblInitialize. Check MicrosoftGame.config!");
@@ -111,8 +130,7 @@ int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *)
111130
return -1;
112131
}
113132

114-
// Run the application main() code
115-
result = SDL_CallMain(argc, argv, mainFunction);
133+
result = mainFunction(caller_argc, caller_argv); // No need for SDL_CallMain(); we already know that we have a valid argv
116134

117135
GDK_UnregisterChangeNotifications();
118136

@@ -138,12 +156,10 @@ int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *)
138156
result = -1;
139157
}
140158

141-
// Free argv, to avoid memory leak
142-
for (i = 0; i < argc; ++i) {
143-
HeapFree(GetProcessHeap(), 0, argv[i]);
144-
}
159+
cleanup:
160+
145161
HeapFree(GetProcessHeap(), 0, argv);
162+
LocalFree(argvw);
146163

147164
return result;
148165
}
149-

0 commit comments

Comments
 (0)