Display banner window while starting up.
[setup-launcher.git] / setup-launcher.c
blob4c35b8e18509de69045e67e4c5a6334a29d72258
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 RECT_W(X) (((X).right > (X).left) ? ((X).right - (X).left) : 0)
38 #define RECT_H(X) (((X).bottom > (X).top) ? ((X).bottom - (X).top) : 0)
40 #define TITLE(STR) \
41 ({ \
42 const wchar_t *const _str = (STR); \
43 (_str && (*_str)) ? _str : L"Setup Launcher"; \
46 #define MAX(A,B) \
47 ({ \
48 const __typeof__((A)) _a = (A); \
49 const __typeof__((B)) _b = (B); \
50 _a > _b ? _a : _b; \
53 #define APPEND(BUFF,STR,BUFF_SIZE) do \
54 { \
55 if(contains_space((STR))) \
56 { \
57 append((BUFF), L"\"", (BUFF_SIZE)); \
58 append((BUFF), STR, (BUFF_SIZE)); \
59 append((BUFF), L"\"", (BUFF_SIZE)); \
60 } \
61 else \
62 { \
63 append((BUFF), STR, (BUFF_SIZE)); \
64 } \
65 } \
66 while(0)
68 static wchar_t *append(wchar_t *const buffer, const wchar_t *const str, const size_t buff_size)
70 const size_t current_len = wcslen(buffer);
71 if(current_len < buff_size)
73 wcsncat(buffer, str, buff_size - current_len);
74 buffer[buff_size - 1U] = L'\0';
76 return buffer;
79 static size_t last_index(const wchar_t *const haystack, const wchar_t needle)
81 const wchar_t *ptr;
82 if(ptr = wcsrchr(haystack, needle))
84 return ptr - haystack;
86 return 0U;
89 static BOOL contains_space(const wchar_t *str)
91 for(; *str != L'\0'; ++str)
93 if(iswspace(*str))
95 return TRUE;
98 return FALSE;
101 static void get_absolut_path(const wchar_t *const path, wchar_t *const full_path, const size_t max_len, const wchar_t **const file_name)
103 const DWORD ret = GetFullPathNameW(path, max_len, full_path, (LPWSTR*)file_name);
104 if(!((ret > 0U) && (ret < max_len)))
106 wcsncpy(full_path, path, max_len);
107 full_path[max_len - 1U] = L'\0';
111 static BOOL file_exists(const wchar_t *const path)
113 const DWORD attributes = GetFileAttributesW(path);
114 return ((attributes != INVALID_FILE_ATTRIBUTES) && (!(attributes & FILE_ATTRIBUTE_DIRECTORY)));
117 static BOOL is_elevated(void)
119 const DWORD version = GetVersion();
120 if(((DWORD)LOBYTE(LOWORD(version))) > 5U)
122 BOOL elevated = FALSE;
123 HANDLE hToken;
124 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
126 DWORD cbSize = sizeof(TOKEN_ELEVATION_TYPE);
127 TOKEN_ELEVATION_TYPE elevation_type;
128 if(GetTokenInformation(hToken, TokenElevationType, &elevation_type, sizeof(TOKEN_ELEVATION_TYPE), &cbSize))
130 switch(elevation_type)
132 case TokenElevationTypeFull:
133 case TokenElevationTypeDefault:
134 elevated = TRUE;
135 break;
138 CloseHandle(hToken);
140 return elevated;
142 else
144 return TRUE; /*for WindowsXP support*/
148 static BOOL get_executable_path(const wchar_t *ptr, wchar_t *const out, const size_t max_len)
150 BOOL flag = FALSE;
151 const size_t limit = max_len - 1U;
152 size_t len = 0U;
153 while ((*ptr != L'\0') && iswspace(*ptr))
155 ++ptr;
157 for (; *ptr != L'\0'; ++ptr)
159 if(*ptr == L'"')
161 flag = (!flag);
163 else
165 if((!flag) && iswspace(*ptr))
167 break;
169 if(len < limit)
171 out[len++] = *ptr;
173 else
175 out[len] = L'\0';
176 return FALSE;
180 out[len] = L'\0';
181 return (len > 0U);
184 static const wchar_t *get_parameters(const wchar_t *cmdLine)
186 BOOL flag = FALSE;
187 if(!cmdLine)
189 return NULL;
191 while ((*cmdLine != L'\0') && iswspace(*cmdLine))
193 ++cmdLine;
195 for (; *cmdLine != L'\0'; ++cmdLine)
197 if(*cmdLine == L'"')
199 flag = (!flag);
201 else
203 if((!flag) && iswspace(*cmdLine))
205 break;
209 while ((*cmdLine != L'\0') && iswspace(*cmdLine))
211 ++cmdLine;
213 return cmdLine;
216 static void remove_file_extension(wchar_t *const path)
218 const size_t pos_dot = last_index(path, L'.');
219 const size_t pos_sep = MAX(last_index(path, L'/'), last_index(path, L'\\'));
220 if((pos_dot > 0U) && ((pos_sep == 0U) || (pos_dot > pos_sep)))
222 path[pos_dot] = L'\0';
226 static HWND create_banner(const HINSTANCE hInstance, const wchar_t *const title, WNDPROC wndproc)
228 HWND hwnd = CreateWindowExW(0, L"#32770", TITLE(title), WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME, CW_USEDEFAULT, CW_USEDEFAULT, 384, 96, NULL, NULL, hInstance, NULL);
229 if (hwnd != NULL)
231 RECT workRect, wndRect;
232 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)wndproc);
233 SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
234 if (SystemParametersInfoW(SPI_GETWORKAREA, 0, &workRect, 0) && GetWindowRect(hwnd, &wndRect))
236 MoveWindow(hwnd, (RECT_W(workRect)-RECT_W(wndRect))/2, (RECT_H(workRect)-RECT_H(wndRect))/2, RECT_W(wndRect), RECT_H(wndRect), TRUE);
239 return hwnd;
242 static void process_messages(const HWND hwnd)
244 MSG msg = {};
245 for (unsigned short i = 0U; i < 8U; ++i)
247 BOOL flag = FALSE;
248 if(i > 0U)
250 Sleep(1); /*delay*/
252 for (unsigned short k = 0U; k < 8192U; ++k)
254 if (PeekMessageW(&msg, hwnd, 0U, 0U, PM_REMOVE))
256 flag = TRUE;
257 TranslateMessage(&msg);
258 DispatchMessageW(&msg);
259 continue;
261 break;
263 if(!flag)
265 break; /*did not process any messages*/
270 static BOOL exec_shell(HWND *const hwnd, const wchar_t *const verb, const wchar_t *const file, const wchar_t *const params, const BOOL await, DWORD *const exit_code)
272 SHELLEXECUTEINFOW shExecInfo;
273 SecureZeroMemory(&shExecInfo, sizeof(SHELLEXECUTEINFOW));
274 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
275 shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
276 shExecInfo.lpVerb = verb;
277 shExecInfo.lpFile = file;
278 shExecInfo.lpParameters = params;
279 shExecInfo.hwnd = hwnd ? (*hwnd) : NULL;
280 shExecInfo.nShow = SW_SHOW;
281 if(ShellExecuteExW(&shExecInfo))
283 if(exit_code)
285 *exit_code = 0U;
287 if(await && (shExecInfo.hProcess != NULL))
289 if(hwnd && (*hwnd))
291 DestroyWindow(*hwnd);
292 *hwnd = NULL;
294 WaitForSingleObject(shExecInfo.hProcess, INFINITE);
295 if(exit_code)
297 GetExitCodeProcess(shExecInfo.hProcess, exit_code);
299 CloseHandle(shExecInfo.hProcess);
301 return TRUE;
303 if(exit_code)
305 *exit_code = 1U;
307 return FALSE;
310 static DWORD exec_shell_loop(HWND *const hwnd, const wchar_t *const verb, const wchar_t *const file_name, const wchar_t *const full_path, const wchar_t *const params)
312 DWORD exit_code;
313 for(;;)
315 if(hwnd && (*hwnd))
317 process_messages(*hwnd);
319 if(!exec_shell(hwnd, verb, full_path, params, TRUE, &exit_code))
321 if(MessageBoxW(hwnd ? (*hwnd) : NULL, L"Failed to launch setup program. Please try again!", TITLE(file_name), MB_SYSTEMMODAL | MB_ICONEXCLAMATION | MB_RETRYCANCEL) != IDRETRY)
323 return 0x2E4;
326 else
328 return exit_code; /*success*/
333 static void error_message(const HWND hwnd, const wchar_t *const message, const wchar_t *const title)
335 MessageBoxW(hwnd, message, TITLE(title), MB_ICONERROR | MB_SYSTEMMODAL);
338 //---------------------------------------------------------------------------
339 // EXE
340 //---------------------------------------------------------------------------
342 #ifdef BUILD_EXE
344 static wchar_t executable_path[MAX_PATHLEN];
345 static wchar_t executable_absolut_path[MAX_PATHLEN];
346 static wchar_t library_path[MAX_PATHLEN];
347 static wchar_t rundll32_path[MAX_PATHLEN];
348 static wchar_t parameters[MAX_CMDNLEN];
350 static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
352 PAINTSTRUCT ps;
353 HDC hdc;
354 switch(uMsg)
356 case WM_PAINT:
357 if(hdc = BeginPaint(hwnd, &ps))
359 FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
360 DrawTextExW(hdc, L"Setup is launching, please stay tuned...", -1, &ps.rcPaint, DT_CENTER|DT_SINGLELINE|DT_VCENTER, NULL);
361 EndPaint(hwnd, &ps);
363 case WM_CLOSE:
364 return 0; /*ignore WM_CLOSE msg*/
365 default:
366 return DefWindowProc(hwnd, uMsg, wParam, lParam);
370 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR pCmdLine, int nCmdShow)
372 /*get command-line arguments*/
373 const wchar_t *const cmdLineArgs = get_parameters(GetCommandLineW());
374 if((!cmdLineArgs) || (!(*cmdLineArgs)))
376 error_message(NULL, L"Required parameters are missing!\n\nUsage:\nsetup-launcher.exe <setup.exe> [PARAMETERS]", NULL);
377 return 1;
380 /*get executable path*/
381 if(!get_executable_path(cmdLineArgs, executable_path, MAX_PATHLEN))
383 error_message(NULL, L"Error: Failed to determine the executable path!", NULL);
386 /*get absolout executable path*/
387 const wchar_t *file_name;
388 get_absolut_path(executable_path, executable_absolut_path, MAX_PATHLEN, &file_name);
390 /*create banner window*/
391 HWND banner = create_banner(hInstance, file_name, WndProc);
392 if(banner != NULL)
394 ShowWindow(banner, SW_SHOWNORMAL);
395 process_messages(banner);
396 Sleep(25);
397 SetForegroundWindow(banner);
398 SetCapture(banner);
401 /*make sure the executable exists*/
402 if(!file_exists(executable_absolut_path))
404 error_message(banner, L"Error: Setup executable could not be found!", file_name);
405 return 0x490;
408 /*take the fast path, iff already elevated (or running on pre-Vista)*/
409 if(is_elevated())
411 return exec_shell_loop(&banner, NULL, file_name, executable_absolut_path, get_parameters(cmdLineArgs));
414 /*get path of RunDLL EXE file*/
415 DWORD ret = GetSystemDirectoryW(rundll32_path, MAX_PATHLEN);
416 if((ret < 1U) || (ret >= MAX_PATHLEN))
418 error_message(banner, L"Error: Failed to determine system directory path!", file_name);
419 return 1;
421 append(rundll32_path, L"\\rundll32.exe", MAX_PATHLEN);
423 /*get path of launcher DLL*/
424 ret = GetModuleFileNameW(NULL, library_path, MAX_PATHLEN);
425 if((ret < 1U) || (ret >= MAX_PATHLEN))
427 error_message(banner, L"Error: Failed to determine executable path!", file_name);
428 return 1;
430 remove_file_extension(library_path);
431 append(library_path, L".dll", MAX_PATHLEN);
433 /*make sure the launcher DLL exists*/
434 if(!file_exists(library_path))
436 error_message(banner, L"Error: Launcher library file could not be found!", file_name);
437 return 0x485;
440 /*build parameter string for RunDLL*/
441 parameters[0U] = L'\0';
442 APPEND(parameters, library_path, MAX_CMDNLEN);
443 append(parameters, L",startup ", MAX_CMDNLEN);
444 APPEND(parameters, executable_absolut_path, MAX_CMDNLEN);
446 /*append additional parameters, if any*/
447 const wchar_t *const extraArgs = get_parameters(cmdLineArgs);
448 if(extraArgs && (*extraArgs))
450 append(parameters, L" ", MAX_CMDNLEN);
451 append(parameters, extraArgs, MAX_CMDNLEN);
454 /*now actually call the launcher DLL*/
455 const DWORD exit_code = exec_shell_loop(&banner, L"runas", file_name, rundll32_path, parameters);
457 /*clean-up*/
458 if(banner != NULL)
460 DestroyWindow(banner);
463 return exit_code;
466 #endif //BUILD_EXE
468 //---------------------------------------------------------------------------
469 // DLL
470 //---------------------------------------------------------------------------
472 #ifdef BUILD_DLL
474 static wchar_t executable_path[MAX_PATHLEN];
475 static wchar_t executable_absolut_path[MAX_PATHLEN];
477 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
479 switch (fdwReason)
481 case DLL_PROCESS_ATTACH:
482 case DLL_THREAD_ATTACH:
483 case DLL_THREAD_DETACH:
484 case DLL_PROCESS_DETACH:
485 break;
487 return TRUE;
490 void CALLBACK startup(HWND hwnd, HINSTANCE hAppInstance, LPSTR lpCmdLine, int nCmdShow)
492 error_message(NULL, L"ANSI entry-point not supported. Please use Unicode entry-point!", NULL);
493 ExitProcess((DWORD)(-1));
496 void CALLBACK startupW(HWND hwnd, HINSTANCE hAppInstance, LPWSTR lpCmdLine, int nCmdShow)
498 /*get executable path*/
499 if(!get_executable_path(lpCmdLine, executable_path, MAX_PATHLEN))
501 error_message(NULL, L"Error: Failed to determine the executable path!", NULL);
504 /*get absolout executable path*/
505 const wchar_t *file_name;
506 get_absolut_path(executable_path, executable_absolut_path, MAX_PATHLEN, &file_name);
508 /*make sure the executable exists*/
509 if(!file_exists(executable_absolut_path))
511 error_message(NULL, L"Error: Setup executable could not be found!", file_name);
512 ExitProcess(0x490);
515 /*now actually launch the executable*/
516 ExitProcess(exec_shell_loop(NULL, NULL, file_name, executable_absolut_path, get_parameters(lpCmdLine)));
519 #endif //BUILD_DLL