diff --git a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp index cefbf2fd..90d7eccd 100644 --- a/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp +++ b/MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp @@ -54,6 +54,8 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #define PATCHER_FLAG_INSTALLER 0x10 +typedef bool(*FVerifyReturnCode)(DWORD); + /***************************************************************************** * * Private Data @@ -144,9 +146,12 @@ class plSelfPatcher : public hsThread void IDownloadFile(PatcherWork*& wk); void IVerifyFile(PatcherWork*& wk); void IIssueManifestRequest(PatcherWork*& wk); + HANDLE ICreateProcess(const wchar* path, const wchar* args, bool forceShell=false) const; void IInstallDep(PatcherWork*& wk); - DWORD IWaitProcess(HANDLE hProcess); + bool IWaitProcess(HANDLE hProcess, FVerifyReturnCode verify); + static bool IValidateExeReturnCode(DWORD returncode); + static bool IValidateMsiReturnCode(DWORD returncode); void IRun(); void IQuit(); @@ -516,7 +521,7 @@ HANDLE plSelfPatcher::ICreateProcess(const wchar* path, const wchar* args, bool si.cb = sizeof(si); wchar cmdline[MAX_PATH]; - StrPrintf(cmdline, arrsize(cmdline), L"%s %s", path, args); + StrPrintf(cmdline, arrsize(cmdline), L"\"%s\" %s", path, args); BOOL result = CreateProcessW(path, cmdline, NULL, @@ -576,12 +581,16 @@ void plSelfPatcher::IInstallDep(PatcherWork*& wk) SetText("Installing updates..."); AsyncSleep(100); - bool forceShell = false; + wchar process[MAX_PATH]; + PathGetCurrentDirectory(process, arrsize(process)); + PathAddFilename(process, process, wk->fFileName, arrsize(process)); wchar* extension = PathFindExtension(wk->fFileName); - wchar* process; wchar args[MAX_PATH]; args[0] = 0; + bool forceShell = false; + FVerifyReturnCode validateptr = NULL; + // Apply arguments to the process to ensure it doesn't do weird stuff like start a big UI // Creative OpenAL (oalinst.exe) uses '/s' for silent. // The small DirectX 9.0c web installer (dxwebsetup.exe) uses "/q" and pops up an error on invalid args. @@ -599,10 +608,11 @@ void plSelfPatcher::IInstallDep(PatcherWork*& wk) if (StrStrI(filename, L"vcredist")) StrPack(args, L" /norestart", arrsize(args)); - process = wk->fFileName; + validateptr = IValidateExeReturnCode; } else if (extension && StrCmpI(extension, L".msi") == 0) { - StrPrintf(args, arrsize(args), L"/i %s /qr /norestart", wk->fFileName); - process = L"msiexec"; + StrPrintf(args, arrsize(args), L"/i \"%s\" /qr /norestart", process); + StrCopy(process, L"msiexec", arrsize(process)); + validateptr = IValidateMsiReturnCode; forceShell = true; } else { LogMsg(kLogError, L"plSelfPatcher::IInstallDep: Invalid extension '%s' for installer '%s'", @@ -614,20 +624,21 @@ void plSelfPatcher::IInstallDep(PatcherWork*& wk) LogMsg(kLogDebug, L"plSelfPatcher::IInstallDep: Installing '%s %s'.", process, args); HANDLE hProcess = ICreateProcess(process, args, forceShell); if (hProcess) { - if (IWaitProcess(hProcess) != ERROR_SUCCESS) + if (IWaitProcess(hProcess, validateptr)) { + IDequeueWork(wk); + } else { PathDeleteFile(wk->fFileName); + IFatalError(L"Failed to install update."); + } CloseHandle(hProcess); - IDequeueWork(wk); } else { IFatalError(L"Failed to run installer."); } } //============================================================================ -DWORD plSelfPatcher::IWaitProcess(HANDLE hProcess) +bool plSelfPatcher::IWaitProcess(HANDLE hProcess, FVerifyReturnCode verify) { - DWORD returncode = ERROR_SUCCESS; - // Since we have taken over the worker thread, we need to listen for any very very important // requests added to the queue. The only one we care about is quit, the rest can just go to // HEY HEY! and we're safe to just swallow the notifies. We delete our own request to resume @@ -647,41 +658,60 @@ DWORD plSelfPatcher::IWaitProcess(HANDLE hProcess) if (quitWk->fType == kQuit) { LogMsg(kLogPerf, "plSelfPatcher::IWaitProcess: Got shutdown during wait, attempting to terminate process."); TerminateProcess(hProcess, 1); - returncode = -1; - break; + return false; } } else if (idx == kWaitProcess) { - GetExitCodeProcess(hProcess, &returncode); - switch (returncode) { - case ERROR_SUCCESS: - case ERROR_PRODUCT_VERSION: // It's already installed... - case ERROR_SUCCESS_REBOOT_REQUIRED: // LMFTFY s/REQUIRED/DESIRED/ - case ERROR_SUCCESS_RESTART_REQUIRED: - LogMsg(kLogDebug, "plSelfPatcher::IWaitProcess: Process finished successfully!"); - returncode = ERROR_SUCCESS; // makes life easier for us. - break; - default: - LogMsg(kLogError, "plSelfPatcher::IWaitProcess: Process failed! Returncode: %u", returncode); - IFatalError(L"Failed to install update."); - break; + if (verify) { + DWORD returncode = 0; + GetExitCodeProcess(hProcess, &returncode); + return verify(returncode); } - break; + return true; } else { FATAL("Invalid wait index"); + return false; } } else if (waitStatus == WAIT_FAILED) { wchar* error = FormatSystemError(); - LogMsg(kLogError, "plSelfPatcher::IWaitProcess: WaitForMultipleObjects failed! %s", error); + LogMsg(kLogError, L"plSelfPatcher::IWaitProcess: WaitForMultipleObjects failed! %s", error); LocalFree(error); IFatalError(L"Internal Error."); - returncode = -1; - break; + return false; + } else { + LogMsg(kLogError, "plSelfPatcher::IWaitProcess: Unhandled WaitForMultipleObjects result 0x%x", waitStatus); + return false; } AsyncSleep(10); } while (1); +} - return returncode; +//============================================================================ +bool plSelfPatcher::IValidateExeReturnCode(DWORD returncode) +{ + if (returncode != 1) { + LogMsg(kLogDebug, "plSelfPatcher::IValidateExeReturnCode: Process finished successfully! Returncode: %u", returncode); + return true; + } else { + LogMsg(kLogError, "plSelfPatcher::IValidateExeReturnCode: Process failed! Returncode: %u", returncode); + return false; + } +} + +//============================================================================ +bool plSelfPatcher::IValidateMsiReturnCode(DWORD returncode) +{ + switch (returncode) { + case ERROR_SUCCESS: + case ERROR_PRODUCT_VERSION: + case ERROR_SUCCESS_REBOOT_INITIATED: + case ERROR_SUCCESS_REBOOT_REQUIRED: + LogMsg(kLogDebug, "plSelfPatcher::IValidateMsiReturnCode: Process finished successfully!"); + return true; + default: + LogMsg(kLogError, "plSelfPatcher::IValidateMsiReturnCode: Process failed! Returncode: %u", returncode); + return false; + } } //============================================================================