@@ -31,58 +31,76 @@ extern "C" {
31
31
#include < shellapi.h> // CommandLineToArgvW()
32
32
#include < appnotify.h>
33
33
34
- // Pop up an out of memory message, returns to Windows
35
- static BOOL OutOfMemory (void )
34
+ static int OutOfMemory (void )
36
35
{
37
36
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 ;
39
44
}
40
45
41
- /* Gets the arguments with GetCommandLine, converts them to argc and argv
42
- and calls SDL_main */
43
46
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 *)
45
48
{
46
- LPWSTR *argvw ;
47
- char **argv ;
48
- int i, argc, result ;
49
+ int result, argc ;
50
+ LPWSTR *argvw = NULL ;
51
+ char **argv = NULL ;
49
52
HRESULT hr;
50
53
XTaskQueueHandle taskQueue;
51
54
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.
56
57
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().
61
61
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;
72
69
}
73
70
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;
77
75
}
78
76
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;
82
94
}
95
+ argv[argc] = NULL ;
96
+
97
+ argvw = NULL ;
98
+
99
+ caller_argc = argc;
100
+ caller_argv = argv;
83
101
}
84
- argv[i] = NULL ;
85
- LocalFree (argvw);
102
+
103
+ // !!! FIXME: This function does not currently properly deinitialize GDK resources on failure.
86
104
87
105
hr = XGameRuntimeInitialize ();
88
106
@@ -100,6 +118,7 @@ int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *)
100
118
xblArgs.queue = taskQueue;
101
119
SDL_snprintf (scidBuffer, 64 , " 00000000-0000-0000-0000-0000%08X" , titleid);
102
120
xblArgs.scid = scidBuffer;
121
+ // !!! FIXME: XblInitialize() should have a corresponding call to XblCleanup() according to the docs.
103
122
hr = XblInitialize (&xblArgs);
104
123
} else {
105
124
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 *)
111
130
return -1 ;
112
131
}
113
132
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
116
134
117
135
GDK_UnregisterChangeNotifications ();
118
136
@@ -138,12 +156,10 @@ int SDL_RunApp(int, char**, SDL_main_func mainFunction, void *)
138
156
result = -1 ;
139
157
}
140
158
141
- // Free argv, to avoid memory leak
142
- for (i = 0 ; i < argc; ++i) {
143
- HeapFree (GetProcessHeap (), 0 , argv[i]);
144
- }
159
+ cleanup:
160
+
145
161
HeapFree (GetProcessHeap (), 0 , argv);
162
+ LocalFree (argvw);
146
163
147
164
return result;
148
165
}
149
-
0 commit comments