Improved error handling + larger command-line buffer, just to be sure.
[setup-launcher.git] / setup-launcher.c
blob6aae27759f375f242d1e1d00f9b0f2675c9dd136
1 /* ---------------------------------------------------------------------------------------------- */
2 /* Setup Launcher Utility */
3 /* Copyright(c) 2016-2020 LoRd_MuldeR <mulder2@gmx.de> */
4 /* */
5 /* Permission is hereby granted, free of charge, to any person obtaining a copy of this software */
6 /* and associated documentation files (the "Software"), to deal in the Software without */
7 /* restriction, including without limitation the rights to use, copy, modify, merge, publish, */
8 /* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the */
9 /* Software is furnished to do so, subject to the following conditions: */
10 /* */
11 /* The above copyright notice and this permission notice shall be included in all copies or */
12 /* substantial portions of the Software. */
13 /* */
14 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING */
15 /* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
16 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, */
17 /* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */
18 /* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
19 /* ---------------------------------------------------------------------------------------------- */
21 #if !defined(BUILD_EXE) && !defined(BUILD_DLL)
22 #error Nothing to do!
23 #endif
24 #if defined(BUILD_EXE) && defined(BUILD_DLL)
25 #error Inconsistent build flags!
26 #endif
28 #include <Windows.h>
30 //---------------------------------------------------------------------------
31 // COMMON FUNCTIONS
32 //---------------------------------------------------------------------------
34 #define MAX_PATHLEN 32768U
35 #define MAX_CMDNLEN 131072U
37 #define MAX(A,B) \
38 ({ \
39 const __typeof__((A)) _a = (A); \
40 const __typeof__((B)) _b = (B); \
41 _a > _b ? _a : _b; \
44 #define APPEND(BUFF,STR,BUFF_SIZE) do \
45 { \
46 if(contains_space((STR))) \
47 { \
48 append((BUFF), L"\"", (BUFF_SIZE)); \
49 append((BUFF), STR, (BUFF_SIZE)); \
50 append((BUFF), L"\"", (BUFF_SIZE)); \
51 } \
52 else \
53 { \
54 append((BUFF), STR, (BUFF_SIZE)); \
55 } \
56 } \
57 while(0)
59 static wchar_t *append(wchar_t *const buffer, const wchar_t *const str, const size_t buff_size)
61 const size_t current_len = wcslen(buffer);
62 if(current_len < buff_size)
64 wcsncat(buffer, str, buff_size - current_len);
65 buffer[buff_size - 1U] = L'\0';
67 return buffer;
70 static size_t last_index(const wchar_t *const haystack, const wchar_t needle)
72 const wchar_t *ptr;
73 if(ptr = wcsrchr(haystack, needle))
75 return ptr - haystack;
77 return 0U;
80 static BOOL contains_space(const wchar_t *str)
82 for(; *str != L'\0'; ++str)
84 if(iswspace(*str))
86 return TRUE;
89 return FALSE;
92 static void get_absolut_path(const wchar_t *const path, wchar_t *const full_path, const size_t max_len)
94 const DWORD ret = GetFullPathNameW(path, max_len, full_path, NULL);
95 if(!((ret > 0U) && (ret < max_len)))
97 wcsncpy(full_path, path, max_len);
98 full_path[max_len - 1U] = L'\0';
102 static BOOL file_exists(const wchar_t *const path)
104 const DWORD attributes = GetFileAttributesW(path);
105 return ((attributes != INVALID_FILE_ATTRIBUTES) && (!(attributes & FILE_ATTRIBUTE_DIRECTORY)));
108 static BOOL is_elevated(void)
110 BOOL elevated = FALSE;
111 HANDLE hToken;
112 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
114 TOKEN_ELEVATION elevation;
115 DWORD cbSize = sizeof(TOKEN_ELEVATION);
116 if(GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &cbSize))
118 elevated = elevation.TokenIsElevated;
120 CloseHandle(hToken);
122 return elevated;
125 static BOOL get_executable_path(const wchar_t *ptr, wchar_t *const out, const size_t max_len)
127 BOOL flag = FALSE;
128 const size_t limit = max_len - 1U;
129 size_t len = 0U;
130 while ((*ptr != L'\0') && iswspace(*ptr))
132 ++ptr;
134 for (; *ptr != L'\0'; ++ptr)
136 if(*ptr == L'"')
138 flag = (!flag);
140 else
142 if((!flag) && iswspace(*ptr))
144 break;
146 if(len < limit)
148 out[len++] = *ptr;
150 else
152 out[len] = L'\0';
153 return FALSE;
157 out[len] = L'\0';
158 return (len > 0U);
161 static const wchar_t *get_parameters(const wchar_t *cmdLine)
163 BOOL flag = FALSE;
164 if(!cmdLine)
166 return NULL;
168 while ((*cmdLine != L'\0') && iswspace(*cmdLine))
170 ++cmdLine;
172 for (; *cmdLine != L'\0'; ++cmdLine)
174 if(*cmdLine == L'"')
176 flag = (!flag);
178 else
180 if((!flag) && iswspace(*cmdLine))
182 break;
186 while ((*cmdLine != L'\0') && iswspace(*cmdLine))
188 ++cmdLine;
190 return cmdLine;
193 static void remove_file_extension(wchar_t *const path)
195 const size_t pos_dot = last_index(path, L'.');
196 const size_t pos_sep = MAX(last_index(path, L'/'), last_index(path, L'\\'));
197 if((pos_dot > 0U) && ((pos_sep == 0U) || (pos_dot > pos_sep)))
199 path[pos_dot] = L'\0';
203 static BOOL exec_shell(const wchar_t *const verb, const wchar_t *const file, const wchar_t *const params, const BOOL await, DWORD *const exit_code)
205 SHELLEXECUTEINFOW shExecInfo;
206 SecureZeroMemory(&shExecInfo, sizeof(SHELLEXECUTEINFOW));
207 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
208 shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
209 shExecInfo.lpVerb = verb;
210 shExecInfo.lpFile = file;
211 shExecInfo.lpParameters = params;
212 shExecInfo.nShow = SW_SHOW;
213 if(ShellExecuteExW(&shExecInfo))
215 if(exit_code)
217 *exit_code = 0U;
219 if(await && (shExecInfo.hProcess != NULL))
221 WaitForSingleObject(shExecInfo.hProcess, INFINITE);
222 if(exit_code)
224 GetExitCodeProcess(shExecInfo.hProcess, exit_code);
226 CloseHandle(shExecInfo.hProcess);
228 return TRUE;
230 if(exit_code)
232 *exit_code = 1U;
234 return FALSE;
237 static void error_message(const wchar_t *const message)
239 MessageBoxW(NULL, message, L"Setup Launcher", MB_ICONERROR|MB_SYSTEMMODAL);
242 //---------------------------------------------------------------------------
243 // EXE
244 //---------------------------------------------------------------------------
246 #ifdef BUILD_EXE
248 static wchar_t executable_path[MAX_PATHLEN];
249 static wchar_t executable_absolut_path[MAX_PATHLEN];
250 static wchar_t library_path[MAX_PATHLEN];
251 static wchar_t rundll32_path[MAX_PATHLEN];
252 static wchar_t parameters[MAX_CMDNLEN];
254 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nCmdShow)
256 /*get command-line arguments*/
257 const wchar_t *const cmdLineArgs = get_parameters(GetCommandLineW());
258 if((!cmdLineArgs) || (!(*cmdLineArgs)))
260 error_message(L"Required parameters are missing!\n\nUsage:\nsetup-launcher.exe <setup.exe> [PARAMETERS]");
261 return 1;
264 /*get executable path*/
265 if(!get_executable_path(cmdLineArgs, executable_path, MAX_PATHLEN))
267 error_message(L"Error: Failed to determine the executable path!");
270 /*get absolout executable path*/
271 get_absolut_path(executable_path, executable_absolut_path, MAX_PATHLEN);
273 /*make sure the executable exists*/
274 if(!file_exists(executable_absolut_path))
276 error_message(L"Error: Setup executable could not be found!");
277 return 0x490;
280 /*take the fast path, iff already elevated*/
281 if(is_elevated())
283 DWORD exit_code;
284 if(!exec_shell(NULL, executable_absolut_path, get_parameters(cmdLineArgs), TRUE, &exit_code))
286 error_message(L"Error: Failed to launch the setup program!");
287 return 0x2E4;
289 return exit_code;
292 /*get path of RunDLL EXE file*/
293 DWORD ret = GetSystemDirectoryW(rundll32_path, MAX_PATHLEN);
294 if((ret < 1U) || (ret >= MAX_PATHLEN))
296 error_message(L"Error: Failed to determine system directory path!");
297 return 1;
299 append(rundll32_path, L"\\rundll32.exe", MAX_PATHLEN);
301 /*get path of launcher DLL*/
302 ret = GetModuleFileNameW(NULL, library_path, MAX_PATHLEN);
303 if((ret < 1U) || (ret >= MAX_PATHLEN))
305 error_message(L"Error: Failed to determine executable path!");
306 return 1;
308 remove_file_extension(library_path);
309 append(library_path, L".dll", MAX_PATHLEN);
311 /*make sure the launcher DLL exists*/
312 if(!file_exists(library_path))
314 error_message(L"Error: Launcher library file could not be found!");
315 return 0x485;
318 /*build parameter string for RunDLL*/
319 parameters[0U] = L'\0';
320 APPEND(parameters, library_path, MAX_CMDNLEN);
321 append(parameters, L",startup ", MAX_CMDNLEN);
322 APPEND(parameters, executable_absolut_path, MAX_CMDNLEN);
324 /*append additional parameters, if any*/
325 const wchar_t *const extraArgs = get_parameters(cmdLineArgs);
326 if(extraArgs && (*extraArgs))
328 append(parameters, L" ", MAX_CMDNLEN);
329 append(parameters, extraArgs, MAX_CMDNLEN);
332 /*now actually call the launcher DLL*/
333 DWORD exit_code;
334 for(;;)
336 if(!exec_shell(L"runas", rundll32_path, parameters, TRUE, &exit_code))
338 if(MessageBoxW(NULL, L"Failed to launch installer. Please try again!", L"Setup Launcher", MB_SYSTEMMODAL | MB_ICONEXCLAMATION | MB_RETRYCANCEL) != IDRETRY)
340 return 0x2E4;
343 else
345 return exit_code; /*success*/
350 #endif //BUILD_EXE
352 //---------------------------------------------------------------------------
353 // DLL
354 //---------------------------------------------------------------------------
356 #ifdef BUILD_DLL
358 static wchar_t executable_path[MAX_PATHLEN];
359 static wchar_t executable_absolut_path[MAX_PATHLEN];
361 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
363 switch (fdwReason)
365 case DLL_PROCESS_ATTACH:
366 case DLL_THREAD_ATTACH:
367 case DLL_THREAD_DETACH:
368 case DLL_PROCESS_DETACH:
369 break;
371 return TRUE;
374 void CALLBACK startup(HWND hwnd, HINSTANCE hAppInstance, LPSTR lpCmdLine, int nCmdShow)
376 error_message(L"ANSI entry-point not supported. Please use Unicode entry-point!");
377 ExitProcess((DWORD)(-1));
380 void CALLBACK startupW(HWND hwnd, HINSTANCE hAppInstance, LPWSTR lpCmdLine, int nCmdShow)
382 /*get executable path*/
383 if(!get_executable_path(lpCmdLine, executable_path, MAX_PATHLEN))
385 error_message(L"Error: Failed to determine the executable path!");
388 /*get absolout executable path*/
389 get_absolut_path(executable_path, executable_absolut_path, MAX_PATHLEN);
391 /*make sure the executable exists*/
392 if(!file_exists(executable_absolut_path))
394 error_message(L"Error: Setup executable could not be found!");
395 ExitProcess(0x490);
398 /*now actually launch the executable*/
399 DWORD exit_code;
400 if(!exec_shell(NULL, executable_absolut_path, get_parameters(lpCmdLine), TRUE, &exit_code))
402 error_message(L"Error: Failed to launch the setup program!");
403 ExitProcess(0x2E4);
406 ExitProcess(exit_code);
409 #endif //BUILD_DLL