|
/** |
|
* Utility to detect when program cannot start because .NET or VC++ |
|
* runtime or some DLL is missing. |
|
* It hides the annoying message box: |
|
* "Application error... The application failed to initialize properly..." |
|
* |
|
* Usage: launcher-vc <app.exe> [arg for the app.exe] |
|
* |
|
* No popups should be displayed. If the app cannot initialize, |
|
* there will be a plain message printed saying so, and errorlevel will be set. |
|
* |
|
* The optional arg for the app is some parameter that causes the app |
|
* exit quickly in case it _can_ start, such as "/help" |
|
* ( note: only one arg is currently accepted ) |
|
* |
|
* Changelog: |
|
* pa 2013-04-29 Update to vc++ 2012 R2 |
|
* pa02 2010-07-29 - Created. tested on VC2005, XP SP3 |
|
*/ |
|
|
|
#define _CRT_SECURE_NO_WARNINGS 1 /* we know what we do. sometimes. */ |
|
|
|
#if 1 // ntstatus |
|
// Older VC versions do not have in windows.h (winnt.h) statuses that I need below. |
|
// So instead include ntstatus.h and define WIN32_NO_STATUS before windows.h. |
|
typedef signed long NTSTATUS; |
|
#include <ntstatus.h> |
|
#define WIN32_NO_STATUS 1 |
|
#endif // ntstatus |
|
|
|
#define WIN32_LEAN_AND_MEAN |
|
#include <windows.h> |
|
|
|
#ifndef STATUS_SXS_CANT_GEN_ACTCTX //+ values missing in old sdk: |
|
#define STATUS_SXS_CANT_GEN_ACTCTX 0xc0150002L |
|
#define STATUS_ORDINAL_NOT_FOUND 0xC0000138L |
|
#define STATUS_ENTRYPOINT_NOT_FOUND 0xC0000139L |
|
#define STATUS_DLL_INIT_FAILED 0xC0000142L |
|
#define STATUS_DLL_NOT_FOUND 0xC0000135L |
|
#endif //- |
|
|
|
#include <stdio.h> |
|
#include <malloc.h> |
|
|
|
#define EXST_ERR_PARAMS 1 |
|
#define EXST_ERR_PREP 2 |
|
#define EXST_ERR_START 3 |
|
#define EXST_ERR_OTHER 4 |
|
#define EXST_RUNAWAY (STILL_ACTIVE+1) |
|
|
|
|
|
static const int launchTimeoutMs = 500; //TODO tweak as needed if the app needs more time to init & exit |
|
|
|
static |
|
int launcher( __in wchar_t const *exe, __in_opt wchar_t const *arg, __out UINT32 *exstatus ) |
|
{ |
|
STARTUPINFOW si; |
|
PROCESS_INFORMATION pi; |
|
DWORD cflags = 0; |
|
BOOL b = FALSE; |
|
DWORD ex; |
|
|
|
auto prev_em = SetErrorMode( SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX|SEM_NOGPFAULTERRORBOX ); |
|
|
|
*exstatus = -1; |
|
memset( &pi, 0, sizeof(pi) ); |
|
memset( &si, 0, sizeof(si) ); |
|
si.cb = sizeof(STARTUPINFOW); |
|
|
|
LPWSTR cmdline = NULL; |
|
if ( arg ) { |
|
// paste this as 2nd token to the command line. 1st token must be prog name. TODO pass real app name? |
|
static const wchar_t *fakename = L"app.exe "; |
|
wchar_t *p = (wchar_t *)alloca( sizeof(wchar_t) * (wcslen(arg) + wcslen(fakename) + 3) ); |
|
if ( p ) { |
|
wcscpy( p, fakename ); |
|
wcscat( p, arg ); |
|
cmdline = p; |
|
} |
|
} |
|
|
|
__try { |
|
b = CreateProcessW( exe, cmdline, NULL, NULL, FALSE, cflags, NULL, NULL, &si, &pi ); |
|
} __finally { |
|
SetErrorMode( prev_em ); |
|
} |
|
|
|
if ( !b ) { |
|
*exstatus = GetLastError(); |
|
printf("Err CreateProcess: %u\n", *exstatus ); |
|
return EXST_ERR_PREP; |
|
} |
|
|
|
// CreateProcess worked. Now wait it to exit: |
|
ex = WaitForSingleObject(pi.hProcess, launchTimeoutMs); |
|
switch ( ex ) { |
|
case WAIT_OBJECT_0: |
|
if ( !GetExitCodeProcess( pi.hProcess, &ex ) ) { |
|
printf("Err GetExitCodeProcess: %u\n", GetLastError() ); |
|
*exstatus = ERROR_INVALID_HANDLE; //? |
|
return EXST_ERR_PREP; |
|
} |
|
break; |
|
case WAIT_TIMEOUT: |
|
ex = STILL_ACTIVE; |
|
break; |
|
default: |
|
printf("Error waiting for app exit: %#X gle=%u\n", ex, GetLastError()); |
|
ex = ERROR_INVALID_HANDLE; //? |
|
} |
|
|
|
if ( ex == STILL_ACTIVE ) { |
|
printf("App still runs: pid=%u\n", pi.dwProcessId ); |
|
} |
|
|
|
if ( pi.hProcess ) { |
|
CloseHandle( pi.hProcess ); |
|
} |
|
|
|
if ( pi.hThread ) { |
|
CloseHandle( pi.hThread ); |
|
} |
|
|
|
*exstatus = ex; |
|
|
|
return 0; |
|
} |
|
|
|
int wmain( int argc, wchar_t **argv ) |
|
{ |
|
UINT32 ret = EXST_ERR_OTHER; |
|
UINT32 exstatus = 0; |
|
|
|
if ( (argc < 2) || (argc > 3) ) { |
|
printf("App startup diagnostic utility r.2a (" __DATE__ ")\n"); |
|
printf("Usage: launcher <exe-to-run> [arg]\n"); |
|
return 1; |
|
} |
|
|
|
// printf( "args 0=[%ws] 1=[%ws]\n\n", argv[0], argv[1]); |
|
|
|
__try { |
|
ret = launcher( argv[1], argc > 2 ? argv[2] : NULL, &exstatus ); |
|
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) { |
|
ret = EXST_ERR_OTHER; |
|
} |
|
|
|
if ( ret != 0 ) { |
|
printf("Error launching specified program\n"); |
|
|
|
switch ( exstatus ) { |
|
case ERROR_SXS_CANT_GEN_ACTCTX: //14001 |
|
printf("VC++ 2005/2008 runtime or other SxS library/assembly is not installed properly" |
|
" (ERROR_SXS_CANT_GEN_ACTCTX)\n"); |
|
ret = EXST_ERR_START; // indicate that known startup error occurred |
|
break; |
|
case ERROR_DLL_NOT_FOUND: |
|
printf("Some required DLL(s) not found (ERROR_DLL_NOT_FOUND)\n"); |
|
ret = EXST_ERR_START; |
|
break; |
|
case ERROR_DLL_INIT_FAILED: |
|
printf("Some DLL failed initialization (ERROR_DLL_INIT_FAILED)\n"); |
|
ret = EXST_ERR_START; |
|
break; |
|
case ERROR_EXTENDED_ERROR: |
|
printf("A complicated error occurred (ERROR_EXTENDED_ERROR)\n"); |
|
ret = EXST_ERR_START; |
|
break; |
|
case ERROR_PATH_NOT_FOUND: |
|
case ERROR_FILE_NOT_FOUND: |
|
printf("File or path not found\n"); |
|
break; |
|
default: |
|
break; |
|
} |
|
|
|
return EXST_ERR_PREP; |
|
} |
|
|
|
// The app has been started, in sense that CreateProcess worked. |
|
// Loader errors occur in the context of the process itself. |
|
if ( exstatus == STILL_ACTIVE ) { |
|
printf("\nLauncher: the app still runs.............\n"); |
|
return EXST_RUNAWAY; |
|
} |
|
|
|
// it exited with some normal status |
|
if ( exstatus < 0x80000000 ) { |
|
printf("Launcher: the app has run and exited with status %u (0x%x)\n", exstatus, exstatus); |
|
return 0; |
|
} |
|
|
|
printf("\nLauncher: the app exited because of exception "); |
|
|
|
|
|
switch( exstatus) { |
|
|
|
case STATUS_SXS_CANT_GEN_ACTCTX: |
|
printf("STATUS_SXS_CANT_GEN_ACTCTX:\n" |
|
".NET or VC++ runtime or other required SxS library/assembly is not installed\n"); |
|
break; |
|
case STATUS_DLL_NOT_FOUND: |
|
printf("STATUS_DLL_NOT_FOUND: some required DLL(s) not found\n"); |
|
break; |
|
case STATUS_ENTRYPOINT_NOT_FOUND: |
|
printf("STATUS_ENTRYPOINT_NOT_FOUND: some required DLL is wrong/old version\n"); |
|
break; |
|
case STATUS_ORDINAL_NOT_FOUND: |
|
printf("STATUS_ORDINAL_NOT_FOUND: some required DLL is wrong/old version\n"); |
|
break; |
|
case STATUS_DLL_INIT_FAILED: |
|
printf("STATUS_DLL_INIT_FAILED: some DLL has failed its initialization\n"); |
|
break; |
|
case STATUS_BREAKPOINT: |
|
printf("STATUS_BREAKPOINT: assertion failed or debug breakpoint hit\n"); |
|
break; |
|
case STATUS_SINGLE_STEP: |
|
printf("STATUS_SINGLE_STEP: assertion failed or debug breakpoint hit\n"); |
|
break; |
|
default: |
|
printf( "0x%x (%d)\n", exstatus, exstatus); |
|
//TODO: print description |
|
} |
|
|
|
printf("The app may or may not start run.\n"); |
|
|
|
return EXST_ERR_START; |
|
} |