void StartupRunner::_SpawnProcess(LPTSTR ptzCommandLine, DWORD dwFlags) { ASSERT(!(dwFlags & ERK_WAITFOR_QUIT && dwFlags & ERK_WAITFOR_IDLE)); // // The following cases need to be supported: // // 1. "C:\Program Files\App\App.exe" -params // 2. C:\Program Files\App\App.exe -params // 3. App.exe -params (App.exe is in %path% or HKLM->REGSTR_PATH_APPPATHS) // and all the above cases without arguments. // // Note that 'App.exe' may contain spaces too // // CreateProcess handles 1 and 2, ShellExecuteEx handles 1 and 3. // So if the first token doesn't contain path characters (':' or '\') // ShellExecuteEx is used. That's really ugly but it *should* work. // TCHAR tzToken[MAX_LINE_LENGTH] = { 0 }; LPCTSTR ptzArgs = NULL; GetToken(ptzCommandLine, tzToken, &ptzArgs, FALSE); HANDLE hProcess = NULL; if (strchr(tzToken, _T('\\')) || strchr(tzToken, _T(':'))) { hProcess = _CreateProcess(ptzCommandLine); } else { hProcess = _ShellExecuteEx(tzToken, ptzArgs); } if (hProcess != NULL) { if (dwFlags & ERK_WAITFOR_QUIT) { WaitForSingleObject(hProcess, INFINITE); } else if (dwFlags & ERK_WAITFOR_IDLE) { WaitForInputIdle(hProcess, INFINITE); } CloseHandle(hProcess); } else { TRACE("StartupRunner failed to launch '%s'", ptzCommandLine); } }
void StartupRunner::_SpawnProcess(LPTSTR ptzCommandLine, DWORD dwFlags) { ASSERT(!(dwFlags & ERK_WAITFOR_QUIT && dwFlags & ERK_WAITFOR_IDLE)); // // The following cases need to be supported: // // 1. "C:\Program Files\App\App.exe" -params // 2. C:\Program Files\App\App.exe -params // 3. App.exe -params (App.exe is in %path% or HKLM->REGSTR_PATH_APPPATHS) // and all the above cases without arguments. // // Note that 'App.exe' may contain spaces too // // CreateProcess handles 1 and 2, ShellExecuteEx handles 1 and 3. // // ShellExecuteEx works with UAC. CreateProcess does not. // Therefore, we have to attempt to emulate CreateProcess's path parsing // when we detect case 2. // TCHAR tzToken[MAX_LINE_LENGTH] = { 0 }; LPTSTR ptzArgs = nullptr; GetTokenW(ptzCommandLine, tzToken, const_cast<LPCTSTR*>(&ptzArgs), FALSE); HANDLE hProcess = nullptr; // If the first character is a quote, assume that the first token is the complete path. // If the first token does not contain a \ or the first token does not contain a :, assume it's a relative path. if (*_tcsspnp(ptzCommandLine, _T(" \t")) == _T('"') || !_tcschr(tzToken, _T('\\')) || !_tcschr(tzToken, _T(':'))) { hProcess = _ShellExecuteEx(tzToken, ptzArgs); } else { // This is an approximation of how CreateProcess determines which file to launch. ptzArgs = ptzCommandLine; do { ptzArgs = _tcschr(ptzArgs, _T(' ')); if (ptzArgs != nullptr) { *ptzArgs++ = _T('\0'); } if (PathFileExists(ptzCommandLine) && PathIsDirectory(ptzCommandLine) == FALSE) { hProcess = _ShellExecuteEx(ptzCommandLine, ptzArgs); break; } if (ptzArgs != nullptr) { *(ptzArgs-1) = _T(' '); } } while (ptzArgs != nullptr); } if (hProcess != nullptr) { if (dwFlags & ERK_WAITFOR_QUIT) { WaitForSingleObject(hProcess, INFINITE); } else if (dwFlags & ERK_WAITFOR_IDLE) { WaitForInputIdle(hProcess, INFINITE); } CloseHandle(hProcess); } #ifdef _DEBUG else { TCHAR tzError[4096]; DescriptionFromHR(HrGetLastError(), tzError, _countof(tzError)); TRACE("StartupRunner failed to launch '%ls', %ls", ptzCommandLine, tzError); } #endif }