1 /* ---------------------------------------------------------------------------------------------- */
2 /* Setup Launcher Utility */
3 /* Copyright(c) 2016-2020 LoRd_MuldeR <mulder2@gmx.de> */
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: */
11 /* The above copyright notice and this permission notice shall be included in all copies or */
12 /* substantial portions of the Software. */
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)
24 #if defined(BUILD_EXE) && defined(BUILD_DLL)
25 #error Inconsistent build flags!
30 //---------------------------------------------------------------------------
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)
42 const wchar_t *const _str = (STR); \
43 (_str && (*_str)) ? _str : L"Setup Launcher"; \
48 const __typeof__((A)) _a = (A); \
49 const __typeof__((B)) _b = (B); \
53 #define APPEND(BUFF,STR,BUFF_SIZE) do \
55 if(contains_space((STR))) \
57 append((BUFF), L"\"", (BUFF_SIZE)); \
58 append((BUFF), STR, (BUFF_SIZE)); \
59 append((BUFF), L"\"", (BUFF_SIZE)); \
63 append((BUFF), STR, (BUFF_SIZE)); \
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';
79 static size_t last_index(const wchar_t *const haystack
, const wchar_t needle
)
82 if(ptr
= wcsrchr(haystack
, needle
))
84 return ptr
- haystack
;
89 static BOOL
contains_space(const wchar_t *str
)
91 for(; *str
!= L
'\0'; ++str
)
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
;
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
:
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
)
151 const size_t limit
= max_len
- 1U;
153 while ((*ptr
!= L
'\0') && iswspace(*ptr
))
157 for (; *ptr
!= L
'\0'; ++ptr
)
165 if((!flag
) && iswspace(*ptr
))
184 static const wchar_t *get_parameters(const wchar_t *cmdLine
)
191 while ((*cmdLine
!= L
'\0') && iswspace(*cmdLine
))
195 for (; *cmdLine
!= L
'\0'; ++cmdLine
)
203 if((!flag
) && iswspace(*cmdLine
))
209 while ((*cmdLine
!= L
'\0') && iswspace(*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
);
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
);
242 static void process_messages(const HWND hwnd
)
245 for (unsigned short i
= 0U; i
< 8U; ++i
)
252 for (unsigned short k
= 0U; k
< 8192U; ++k
)
254 if (PeekMessageW(&msg
, hwnd
, 0U, 0U, PM_REMOVE
))
257 TranslateMessage(&msg
);
258 DispatchMessageW(&msg
);
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
))
287 if(await
&& (shExecInfo
.hProcess
!= NULL
))
291 DestroyWindow(*hwnd
);
294 WaitForSingleObject(shExecInfo
.hProcess
, INFINITE
);
297 GetExitCodeProcess(shExecInfo
.hProcess
, exit_code
);
299 CloseHandle(shExecInfo
.hProcess
);
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
)
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
)
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 //---------------------------------------------------------------------------
340 //---------------------------------------------------------------------------
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
)
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
);
364 return 0; /*ignore WM_CLOSE msg*/
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
);
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
);
394 ShowWindow(banner
, SW_SHOWNORMAL
);
395 process_messages(banner
);
397 SetForegroundWindow(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
);
408 /*take the fast path, iff already elevated (or running on pre-Vista)*/
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
);
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
);
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
);
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
);
460 DestroyWindow(banner
);
468 //---------------------------------------------------------------------------
470 //---------------------------------------------------------------------------
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
)
481 case DLL_PROCESS_ATTACH
:
482 case DLL_THREAD_ATTACH
:
483 case DLL_THREAD_DETACH
:
484 case DLL_PROCESS_DETACH
:
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
);
515 /*now actually launch the executable*/
516 ExitProcess(exec_shell_loop(NULL
, NULL
, file_name
, executable_absolut_path
, get_parameters(lpCmdLine
)));