Created
October 5, 2014 03:49
-
-
Save juntalis/62400b02ba1862dab0f7 to your computer and use it in GitHub Desktop.
Executable wrapper for allowing only a single instance of a program to run, using a configurable number of args for the identity.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Preconfigure | |
#define _UNICODE 1 | |
#define _CONSOLE 1 | |
#include <pch.h> | |
#include <crc16.h> | |
struct exe_args_t | |
{ | |
HANDLE hMutex; | |
size_t szExecutable; | |
tchar* lpExecutable; | |
int iArgsCount; | |
tchar** lpArgs; | |
# ifdef _DEBUG | |
tchar* lpMutexKey; | |
# endif | |
}; | |
static tchar* stprintf_alloc(tchar* message, ...); | |
static tchar* vstprintf_alloc(tchar* message, va_list args); | |
static noret fatal_message(u32 dwError, tchar* sPrefix, tchar* sCode, bool blDealloc) | |
{ | |
# ifdef WITH_MSGBOX | |
tchar* lpDisplayBuf = stprintf_alloc(_T("%s: FATAL:%s"), sPrefix, sCode); | |
MessageBox(NULL, (const tchar*)lpDisplayBuf, _T("Fatal Error"), MB_OK | MB_ICONERROR); | |
xfree(lpDisplayBuf); | |
# else | |
_tprintf(_T("%s: FATAL:%s\n"), sPrefix, sCode); | |
# endif | |
if(blDealloc) xfree(sCode); | |
ExitProcess(dwError); | |
} | |
static void show_message(tchar* message, ...) | |
{ | |
va_list args = NULL; | |
tchar* lpMessage = NULL; | |
// Allocate buffer for our resulting format string. | |
va_start(args, message); | |
lpMessage = vstprintf_alloc(message, args); | |
va_end(args); | |
# ifdef WITH_MSGBOX | |
MessageBox(NULL, (const tchar*)lpMessage, _T("Single Instance App"), MB_OK | MB_ICONINFORMATION); | |
# else | |
_tprintf(_T("%s\n"), lpMessage); | |
# endif | |
xfree(lpMessage); | |
} | |
#define check_not_code(ERRCODE,CODE) \ | |
if(CODE) \ | |
fatal_message(ERRCODE, _T("[") __TFILE__ _T(":") XSTR(__LINE__) _T("]"), XSTR(CODE), false) | |
#define check_code(ERRCODE,CODE) check_not_code(ERRCODE, !(CODE)) | |
#define xcheckn(CODE) check_not_code(1, CODE) | |
#define xcheck(CODE) check_code(1, CODE) | |
static inline void* xalloc(size_t szBuf) | |
{ | |
void* p = NULL; | |
xcheck(p = malloc(szBuf)); | |
memset(p, 0, szBuf); | |
return p; | |
} | |
#define typalloc(TYPE) ((TYPE*)xalloc(sizeof(TYPE))) | |
#define aalloc(TYPE,COUNT) ((TYPE*)xalloc(((size_t)COUNT) * sizeof(TYPE))) | |
#define salloc(TYPE,COUNT) aalloc(TYPE,(COUNT+1)) | |
#define tcsalloc(COUNT) salloc(tchar,COUNT) | |
#define xfatalme(ERRCODE,MSG,DEALLOC) fatal_message(ERRCODE, _T("[") __TFILE__ _T(":") XSTR(__LINE__) _T("]"), MSG, DEALLOC) | |
// Only used once or twice, thus the inline | |
static tchar* vstprintf_alloc(tchar* message, va_list args) | |
{ | |
tchar *lpResult; | |
size_t szDisplayBuf; | |
// Check the resulting size of the buffer. | |
szDisplayBuf = (size_t)_vsctprintf((const tchar*)message, args) + 1; | |
// Allocate our buffer. | |
if(!(lpResult = (tchar*)calloc(szDisplayBuf, sizeof(tchar)))) { | |
return NULL; | |
} | |
// Finally, fill in the message. | |
_vsntprintf(lpResult, szDisplayBuf, (const tchar*)message, args); | |
return lpResult; | |
} | |
// Only used once or twice, thus the inline | |
static tchar* stprintf_alloc(tchar* message, ...) | |
{ | |
va_list args = NULL; | |
tchar* lpResult = NULL; | |
// Allocate buffer for our resulting format string. | |
va_start(args, message); | |
lpResult = vstprintf_alloc(message, args); | |
va_end(args); | |
return lpResult; | |
} | |
static tchar* tcstrim(tchar *str) | |
{ | |
tchar *start = str; | |
// Immediately return if null arg. | |
if(IS_BAD_STR(str)) return str; | |
// Trim leading space | |
while(_istspace(*start)) start++; | |
// All spaces. | |
if(*start == _T('\0')) { | |
memset((void*)str, 0, (size_t)(x2ptr(start) - x2ptr(str))); | |
start = str; | |
} else { | |
tchar* pos, | |
*end = (pos = (tchar*)(start + _tcslen((const tchar*)start) - 1)); | |
while(pos > start && _istspace(*pos)) pos--; | |
memset((void*)++pos, 0, end - pos); | |
} | |
return start; | |
} | |
// Only called from one function, thus the inline. | |
// Most likely going to be removed. | |
static inline noret error_in_error_message(u32 dwErr, u32 dwErrErr, tchar* sPrefix, tchar* sMessage, va_list args) | |
{ | |
tchar* sErrMsg = NULL, *sErrFinal = NULL; | |
sErrMsg = vstprintf_alloc(sMessage, args); | |
sErrFinal = stprintf_alloc( | |
_T("Error Code 0x%08X occurred while trying to print info about another error: [0x%08X] %s"), | |
dwErrErr, | |
dwErr, | |
sErrMsg | |
); | |
xfree(sErrMsg); | |
fatal_message(dwErrErr, sPrefix, sErrFinal, true); | |
} | |
#define xerror(...) error_message(ERROR_SUCCESS, __VA_ARGS__) | |
#define xerrorn(ERRCODE,...) error_message(ERRCODE, __VA_ARGS__) | |
#define xerrorfn(FN) error_message(ERROR_SUCCESS, XSTR(FN) _T("() failed.")) | |
#define xfatal(...) { \ | |
error_message(ERROR_SUCCESS, __VA_ARGS__); \ | |
ExitProcess(1); \ | |
} | |
#define xfataln(ERRCODE,...) { \ | |
error_message(ERRCODE, __VA_ARGS__); \ | |
ExitProcess(ERRCODE); \ | |
} | |
#define xfatalfn(FN) { \ | |
error_message(ERROR_SUCCESS, XSTR(FN) _T("() failed.")); \ | |
ExitProcess(1); \ | |
} | |
static void error_message(u32 dwErr, tchar* sMessage, ...) | |
{ | |
tchar *lpDisplayBuf = NULL; | |
u32 dwErrErr = ERROR_SUCCESS; | |
va_list vlArgs = NULL; | |
va_start(vlArgs, sMessage); | |
lpDisplayBuf = vstprintf_alloc(sMessage, vlArgs); | |
va_end(vlArgs); | |
if(dwErr == ERROR_SUCCESS) dwErr = GetLastError(); | |
if(dwErr != ERROR_SUCCESS) { | |
tchar *lpErrMsg = NULL, *lpTemp = NULL; | |
FormatMessage( | |
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_IGNORE_INSERTS, | |
NULL, dwErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (tchar*)&lpErrMsg, 0, NULL | |
); | |
lpTemp = stprintf_alloc(_T("%s - Windows Error [0x%08X]: %s"), lpDisplayBuf, dwErr, lpErrMsg); | |
xfree(lpDisplayBuf); | |
lpDisplayBuf = lpTemp; | |
LocalFree((HLOCAL)lpErrMsg); | |
} | |
# ifdef WITH_MSGBOX | |
MessageBox(NULL, (const tchar*)lpDisplayBuf, _T("Windows Error"), MB_OK | MB_ICONERROR); | |
# else | |
_tprintf(_T("FATAL: %s\n"), lpDisplayBuf); | |
# endif | |
xfree(lpDisplayBuf); | |
} | |
#ifndef GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | |
# define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 0x00000002 | |
#endif | |
#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | |
# define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004 | |
#endif | |
static HMODULE GetModuleNoRefEx(const tchar* sName, u32 dwFlags) | |
{ | |
HMODULE hMod = NULL; | |
if(!(dwFlags & GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT)) | |
dwFlags |= GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; | |
return GetModuleHandleEx(dwFlags, sName, &hMod) ? hMod : NULL; | |
} | |
static HMODULE GetModuleNoRef(const tchar* sName) | |
{ | |
return GetModuleNoRefEx(sName, 0); | |
} | |
#ifdef BUILD_ARCH_X86 | |
static bool is_wow64(void) | |
{ | |
__asm | |
{ | |
mov ax, cs | |
shr eax, 5 | |
} | |
} | |
static int without_wow64_redirection(int(__cdecl* lpExecFunc)(void*), void* userdata) | |
{ | |
int r; | |
if(!is_wow64()) { | |
r = lpExecFunc(userdata); | |
} else { | |
PVOID lpRevert = NULL; | |
HMODULE hKernel32 = NULL; | |
BOOL (WINAPI* lpDisableWow64FsRedirection)(PVOID*); | |
BOOL (WINAPI *lpRevertWow64FsRedirection)(PVOID); | |
check_code(1, hKernel32 = GetModuleNoRef(_T("kernel32.dll"))); | |
*((FARPROC*)&lpDisableWow64FsRedirection) = GetProcAddress(hKernel32, "Wow64DisableWow64FsRedirection"); | |
*((FARPROC*)&lpRevertWow64FsRedirection) = GetProcAddress(hKernel32, "Wow64RevertWow64FsRedirection"); | |
if(!lpDisableWow64FsRedirection || !lpRevertWow64FsRedirection) { | |
xfatalfn(GetProcAddress); | |
} | |
if(!lpDisableWow64FsRedirection(&lpRevert)) { | |
xfatalfn(Wow64DisableWow64FsRedirection); | |
} | |
r = lpExecFunc(userdata); | |
lpRevertWow64FsRedirection(lpRevert); | |
} | |
return r; | |
} | |
#else | |
static int without_wow64_redirection(int(__cdecl* lpExecFunc)(void*), void* userdata) | |
{ | |
return lpExecFunc(userdata); | |
} | |
#endif | |
/** String Duplication */ | |
static tchar* xtcsndup(tchar* s, size_t size) | |
{ | |
tchar* p; | |
size_t szlen; | |
if (!s) { return NULL; } | |
if ((szlen = _tcslen((const tchar*)s)) < size) size = szlen; | |
p = tcsalloc(size); | |
memcpy((void*)p, (const void*)s, size * sizeof(tchar)); | |
return p; | |
} | |
/** Check if arg contains a space */ | |
static bool hasspace(tchar* arg, size_t szLen) | |
{ | |
size_t i = 0; | |
for(; i < szLen; i++) | |
if(_istspace(arg[i])) | |
return true; | |
return false; | |
} | |
#if 0 | |
/** Quote if necessary */ | |
static tchar* quotearg(tchar* arg, size_t* szArgLen) | |
{ | |
tchar* result = NULL; | |
// Check for EMPTY args. If found, don't both adding quotes. | |
if(!arg || !(*szArgLen = _tcslen(arg))) { | |
return tcsalloc(0); | |
} | |
if(!hasspace(arg, *szArgLen)) { | |
return _tcsdup(arg); | |
} | |
*szArgLen += 2; | |
result = tcsalloc(szArgLen); | |
// Now set result to the value of arg, with surrounding quotes. | |
if(!(_sntprintf(result, szArgLen, _T("\"%s\""), arg))) { | |
xerror(_T("Failed quoting arguments!")); | |
} | |
return result; | |
} | |
#endif | |
#ifdef _DEBUG | |
static inline tchar* quote(tchar* arg, size_t szLen) | |
{ | |
tchar* sBuf = tcsalloc(szLen + 2); | |
_stprintf(sBuf, _T("\"%s\""), arg); | |
return sBuf; | |
} | |
/** Fake execution */ | |
static int execute_args_wait(void* lpData) | |
{ | |
int i = 0; | |
struct exe_args_t* lpLaunchArgs = (struct exe_args_t*)lpData; | |
_tprintf(_T("Mutex Key: %s\n"), lpLaunchArgs->lpMutexKey); | |
_tprintf(_T("Command Line:\n")); | |
_tprintf(_T(" [%d] => %s\n"), 0, lpLaunchArgs->lpExecutable); | |
if(!lpLaunchArgs->iArgsCount) { return 0; } | |
for(; i < lpLaunchArgs->iArgsCount; i++) { | |
size_t szLen; | |
tchar* arg = lpLaunchArgs->lpArgs[i]; | |
if(szLen = _tcslen(arg)) { | |
if(hasspace(arg, szLen)) { | |
arg = quote(lpLaunchArgs->lpArgs[i], szLen); | |
szLen = (size_t)-1; | |
} | |
} | |
_tprintf(_T(" [%d] => %s\n"), i+1, arg); | |
if(szLen == (size_t)-1) { | |
xfree(arg); | |
} | |
} | |
return 0; | |
} | |
#else | |
// Caller responsible for cleanup. | |
static tchar* build_cmdline(struct exe_args_t* lpLaunchArgs) | |
{ | |
int i = 0; | |
tchar *sCmdLine = NULL; | |
size_t szCmdLine = 0; | |
bool* lpblNeedsSpaces = aalloc(bool, lpLaunchArgs->iArgsCount + 1); | |
lpblNeedsSpaces[0] = hasspace(lpLaunchArgs->lpExecutable, lpLaunchArgs->szExecutable); | |
szCmdLine = *lpblNeedsSpaces ? lpLaunchArgs->szExecutable + 3 : lpLaunchArgs->szExecutable + 1; | |
for(; i < lpLaunchArgs->iArgsCount; i++) { | |
size_t szLen; | |
//tchar* argvTrimmed = tcstrim(argv[i]); | |
szLen = _tcslen(lpLaunchArgs->lpArgs[i]); | |
if(!szLen) continue; | |
if(lpblNeedsSpaces[i+1] = hasspace(lpLaunchArgs->lpArgs[i], szLen)) { | |
szCmdLine += szLen + 3; | |
} else { | |
szCmdLine += szLen + 1; | |
} | |
} | |
// There's an unnecessary space accounted for. | |
sCmdLine = tcsalloc(--szCmdLine); | |
if(*lpblNeedsSpaces) { | |
_stprintf(sCmdLine, _T("\"%s\""), lpLaunchArgs->lpExecutable); | |
} else { | |
_tcscpy(sCmdLine, lpLaunchArgs->lpExecutable); | |
} | |
for(i = 0; i < lpLaunchArgs->iArgsCount; i++) { | |
if(lpblNeedsSpaces[i+1]) { | |
_tcscat(sCmdLine, _T(" \"")); | |
_tcscat(sCmdLine, lpLaunchArgs->lpArgs[i]); | |
_tcscat(sCmdLine, _T("\"")); | |
} else { | |
_tcscat(sCmdLine, _T(" ")); | |
_tcscat(sCmdLine, lpLaunchArgs->lpArgs[i]); | |
} | |
} | |
xfree(lpblNeedsSpaces); | |
return sCmdLine; | |
} | |
static LPPROCESS_INFORMATION execute_process(tchar* sCmdLine) | |
{ | |
STARTUPINFO si; | |
LPPROCESS_INFORMATION ppi = typalloc(PROCESS_INFORMATION); | |
// Zero out structs and set sizes. | |
ZeroMemory(&si, sizeof(si)); | |
si.cb = sizeof(si); | |
/* Program launching. */ | |
if (!CreateProcess( | |
NULL, /* No module name (use command line) */ | |
sCmdLine, /* Command line */ | |
NULL, /* Process handle not inheritable */ | |
NULL, /* Thread handle not inheritable */ | |
FALSE, /* Set handle inheritance to FALSE */ | |
0, /* No creation flags */ | |
NULL, /* Use parent's environment block */ | |
NULL, /* Use parent's starting directory */ | |
&si, /* Pointer to STARTUPINFO structure */ | |
ppi /* Pointer to PROCESS_INFORMATION structure */ | |
)) { | |
xfree(ppi); | |
xfree(sCmdLine); | |
xfatalfn(CreateProcess); | |
ExitProcess(1); | |
} | |
//ResumeThread(ppi->hThread); | |
xfree(sCmdLine); | |
return ppi; | |
} | |
// Cleans up a call to execute_process. | |
static inline int cleanup_execute(LPPROCESS_INFORMATION ppi) | |
{ | |
DWORD dwRes; | |
if(!GetExitCodeProcess(ppi->hProcess, &dwRes)) { | |
xfatalfn(GetExitCodeProcess); | |
} | |
MAYBE_CLOSE_HANDLE(ppi->hThread); | |
MAYBE_CLOSE_HANDLE(ppi->hProcess); | |
xfree(ppi); | |
return (int)dwRes; | |
} | |
static inline int execute_wait(tchar* sCmdLine) | |
{ | |
LPPROCESS_INFORMATION ppi; | |
if(!(ppi = execute_process(sCmdLine))) return 1; | |
// Wait until the application actually finishes. | |
WaitForSingleObject(ppi->hProcess, INFINITE); | |
return cleanup_execute(ppi); | |
} | |
// Caller responsible for cleanup. | |
static int execute_args_wait(void* lpData) | |
{ | |
int r; | |
struct exe_args_t* lpLaunchArgs = (struct exe_args_t*)lpData; | |
tchar *sCmdLine = build_cmdline(lpLaunchArgs); | |
r = execute_wait(sCmdLine); | |
return r; | |
} | |
#endif | |
/** Automatically allocate the necessary buffer, then return the result of SearchPath */ | |
static tchar* alloc_search_path(tchar* exename, u32* lpszExecutable) | |
{ | |
tchar* lpFilePart = NULL, | |
* lpPathBuffer = NULL; | |
if(!( *lpszExecutable = SearchPath(NULL, exename, _T(".exe"), 0, NULL, &lpFilePart) )) { | |
xfatalfn(SearchPath); | |
} | |
lpPathBuffer = aalloc(tchar, *lpszExecutable); | |
if(!( *lpszExecutable = SearchPath(NULL, exename, _T(".exe"), *lpszExecutable, lpPathBuffer, &lpFilePart) )) { | |
xfree(lpPathBuffer); | |
lpPathBuffer = NULL; | |
xfatalfn(SearchPath); | |
} | |
return lpPathBuffer; | |
} | |
#define READ_FLAGS_ATTRS FILE_ATTRIBUTE_NORMAL | |
#define DOS_SIGNATURE_SIZE RTL_FIELD_SIZE(IMAGE_DOS_HEADER, e_magic) | |
#define NT_SIGNATURE_SIZE RTL_FIELD_SIZE(IMAGE_NT_HEADERS32, Signature) | |
#define NT_HEADERS_ADDR_OFFSET FIELD_OFFSET(IMAGE_DOS_HEADER, e_lfanew) | |
#define OPTIONAL_HEADER_OFFSET FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) | |
#define CHECKSUM32_OFFSET FIELD_OFFSET(IMAGE_OPTIONAL_HEADER32, CheckSum) | |
#define CHECKSUM64_OFFSET FIELD_OFFSET(IMAGE_OPTIONAL_HEADER64, CheckSum) | |
static inline bool read_from_origin_offset(HANDLE hFile, s32 lOffset, void* lpBuffer, u32 dwBytesToRead, u32 dwOrigin) | |
{ | |
u32 dwBytesRead = 0; | |
if(SetFilePointer(hFile, lOffset, NULL, dwOrigin) == INVALID_SET_FILE_POINTER) { | |
xerrorfn(SetFilePointer); | |
return false; | |
} else if(!ReadFile(hFile, lpBuffer, dwBytesToRead, &dwBytesRead, NULL)) { | |
xerror(_T("Failed to read %d bytes from PE file!"), dwBytesToRead); | |
return false; | |
} | |
return true; | |
} | |
static u32 pe_checksum(tchar* lpExePath) | |
{ | |
// Yeah, I'm lazy. | |
#define read_offset_origin(OFFSET,RETVAR,ORIGIN) \ | |
read_from_origin_offset(hFile, OFFSET, (void*)&(RETVAR), sizeof(RETVAR), ORIGIN) | |
#define read_at(OFFSET,RETVAR) \ | |
read_offset_origin(OFFSET, RETVAR, FILE_BEGIN) | |
#define read_offset(OFFSET,RETVAR) \ | |
read_offset_origin(OFFSET, RETVAR, FILE_CURRENT) | |
#define cleanup(MSG,...) \ | |
CloseHandle(hFile); \ | |
xfatal(MSG, __VA_ARGS__ ) | |
HANDLE hFile; | |
s32 lOffset = 0; | |
u16 wTempStorage = 0; | |
u32 dwChecksum, dwNtHeaders, dwTempStorage = 0; | |
// First open our PE for reading. | |
if(IS_INVALID_HANDLE( | |
hFile = CreateFile(lpExePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, READ_FLAGS_ATTRS, NULL) | |
)) { | |
xfatal(_T("Failed to open file: %s"), lpExePath); | |
// Read IMAGE_DOS_HEADER.e_magic | |
} else if(!ReadFile(hFile, (void*)&wTempStorage, sizeof(wTempStorage), &dwTempStorage, NULL) || (wTempStorage != IMAGE_DOS_SIGNATURE)) { | |
cleanup(_T("%s is not a valid PE file!"), lpExePath); | |
// Read the offset to our IMAGE_NT_HEADERS from IMAGE_DOS_HEADER.lfanew | |
} else if( | |
(!read_at(NT_HEADERS_ADDR_OFFSET, dwNtHeaders) || (dwNtHeaders <= NT_HEADERS_ADDR_OFFSET)) || | |
(!read_at(dwNtHeaders, dwTempStorage) || (dwTempStorage != IMAGE_NT_SIGNATURE)) | |
) { | |
cleanup(_T("No PE header found in file: %s [lOffset=0x%08X, dwTempStorage=0x%08X]"), lpExePath, lOffset, dwTempStorage); | |
// Read our IMAGE_OPTIONAL_HEADER.Magic to determine build architecture of our executable. | |
} else if(!read_offset(OPTIONAL_HEADER_OFFSET - sizeof(dwNtHeaders), wTempStorage)) { | |
cleanup(_T("Failed to read IMAGE_OPTIONAL_HEADER.Magic for PE: %s"), lpExePath); | |
} | |
if(wTempStorage == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { | |
lOffset = CHECKSUM32_OFFSET; | |
} else if(wTempStorage == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { | |
lOffset = CHECKSUM64_OFFSET; | |
} else { | |
cleanup(_T("Unrecognized value 0x%04X found for IMAGE_OPTIONAL_HEADER.Magic of PE: %s"), wTempStorage, lpExePath); | |
} | |
// Finally, read our image checksum. | |
if(!read_offset(lOffset, dwChecksum)) { | |
cleanup(_T("Failed to read checksum for PE: %s"), lpExePath); | |
} | |
CloseHandle(hFile); | |
return dwChecksum; | |
# undef cleanup | |
# undef read_offset | |
# undef read_at | |
# undef read_offset_origin | |
} | |
#ifdef _UNICODE | |
static u32 uCodePage = 0; | |
static inline int xwcstombs(const tchar* lpwBuffer, size_t szwBuffer, char* lpcBuffer, size_t szcBuffer) | |
{ | |
return WideCharToMultiByte(uCodePage, 0, lpwBuffer, szwBuffer, lpcBuffer, szcBuffer, NULL, NULL); | |
} | |
#endif | |
#define MUTEX_KEY_BASE _T("singleinst.exec-") | |
#define MUTEX_KEY_BASE_LEN TLEN(MUTEX_KEY_BASE) | |
// TODO: Reuse the args lengths during launch instead of recalculating it. | |
static inline tchar* generate_mutex_key(u32 dwChecksum, int iArgIds, tchar* argv[]) | |
{ | |
int i = 0; | |
tchar* lpMutexKey = NULL, *lpCurrent; | |
size_t szMutexKey = MUTEX_KEY_BASE_LEN; | |
uCodePage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; | |
// Includes trailing 0 | |
szMutexKey = TLEN(MUTEX_KEY_BASE); | |
szMutexKey += 8 + (8 * iArgIds); | |
lpMutexKey = tcsalloc(szMutexKey); | |
_tcscpy(lpMutexKey, MUTEX_KEY_BASE); | |
_stprintf(lpMutexKey, MUTEX_KEY_BASE _T("%08X"), dwChecksum); | |
lpCurrent = lpMutexKey + MUTEX_KEY_BASE_LEN + 7; | |
for(; i < iArgIds; i++) { | |
crc_t crcArg; | |
char* lpcArg; | |
size_t szArg = _tcslen(argv[i]); | |
# ifdef _UNICODE | |
lpcArg = salloc(char, szArg); | |
xwcstombs((const tchar*)argv[i], -1, lpcArg, szArg * sizeof(char)); | |
# else | |
lpcArg = argv[i]; | |
# endif | |
crcArg = crc_init(); | |
crcArg = crc_update(crcArg, (unsigned char *)lpcArg, szArg); | |
crcArg = crc_finalize(crcArg); | |
_stprintf(lpCurrent, _T("%08X"), crcArg); | |
lpCurrent += 8; | |
# ifdef _UNICODE | |
xfree(lpcArg); | |
# endif | |
} | |
return lpMutexKey; | |
} | |
#ifdef SUBSYSTEM_CONSOLE | |
int _tmain(int argc, tchar* argv[]) | |
{ | |
#else | |
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpOriginalCmdLine, int nCmdShow) | |
{ | |
# define argc __argc | |
# define argv __targv | |
#endif | |
u32 dwChecksum; | |
tchar *lpMutexKey; | |
int r = 0, iArgIds, nargc; | |
struct exe_args_t* lpLaunchArgs = NULL; | |
if(argc < 3) { | |
show_message(_T("Usage: %s <number-of-identifying-args> <program> [arg1] [arg2] ... [argN]"), argv[0]); | |
return 0; | |
} | |
nargc = argc - 2; | |
if(!(iArgIds = _tstoi((const tchar*)argv[1]))) { | |
xfatal(_T("Failed to parse identifying arg count from value: %s"), argv[1]); | |
} else if(nargc < iArgIds) { | |
xfatal(_T("Expected at least %d arguments, but only found %d."), iArgIds, nargc); | |
} | |
// Locate the executable path and generate a checksum key using the following | |
// formula: | |
// | |
// MUTEX_KEY_BASE + hex(execute-pe-checksum) + crc | |
// | |
iArgIds--; | |
lpLaunchArgs = typalloc(struct exe_args_t); | |
lpLaunchArgs->lpExecutable = alloc_search_path(argv[2], &(lpLaunchArgs->szExecutable)); | |
dwChecksum = pe_checksum(lpLaunchArgs->lpExecutable); | |
if(iArgIds > 0) { | |
lpMutexKey = generate_mutex_key(dwChecksum, iArgIds, &(argv[3])); | |
} else { | |
lpMutexKey = generate_mutex_key(dwChecksum, iArgIds, NULL); | |
} | |
if(IS_INVALID_HANDLE(lpLaunchArgs->hMutex = CreateMutex(NULL, TRUE, lpMutexKey))) { | |
xfree(lpMutexKey); | |
goto cleanup; | |
} | |
# ifdef _DEBUG | |
lpLaunchArgs->lpMutexKey = lpMutexKey; | |
# else | |
xfree(lpMutexKey); | |
# endif | |
lpLaunchArgs->iArgsCount = nargc - 1; | |
if(lpLaunchArgs->iArgsCount > 0) { | |
lpLaunchArgs->lpArgs = &(argv[3]); | |
} else { | |
lpLaunchArgs->lpArgs = NULL; | |
} | |
r = without_wow64_redirection(&execute_args_wait, (void*)lpLaunchArgs); | |
# ifdef _DEBUG | |
xfree(lpLaunchArgs->lpMutexKey); | |
# endif | |
cleanup: | |
MAYBE_CLOSE_MUTEX(lpLaunchArgs->hMutex); | |
xfree(lpLaunchArgs->lpExecutable); | |
xfree(lpLaunchArgs); | |
return r; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment