Browse Source

Improve installer process handling.

(cherry picked from commit ad728785ba31b02ad4d518ec4f9a6508ca27b96d)
hoikas/newpatcher-1
Adam Johnson 5 years ago committed by rarified
parent
commit
115d308d10
  1. 92
      MOULOpenSourceClientPlugin/Plasma20/Sources/Plasma/Apps/plUruLauncher/SelfPatcher.cpp

92
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 #define PATCHER_FLAG_INSTALLER 0x10
typedef bool(*FVerifyReturnCode)(DWORD);
/***************************************************************************** /*****************************************************************************
* *
* Private Data * Private Data
@ -144,9 +146,12 @@ class plSelfPatcher : public hsThread
void IDownloadFile(PatcherWork*& wk); void IDownloadFile(PatcherWork*& wk);
void IVerifyFile(PatcherWork*& wk); void IVerifyFile(PatcherWork*& wk);
void IIssueManifestRequest(PatcherWork*& wk); void IIssueManifestRequest(PatcherWork*& wk);
HANDLE ICreateProcess(const wchar* path, const wchar* args, bool forceShell=false) const; HANDLE ICreateProcess(const wchar* path, const wchar* args, bool forceShell=false) const;
void IInstallDep(PatcherWork*& wk); 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 IRun();
void IQuit(); void IQuit();
@ -516,7 +521,7 @@ HANDLE plSelfPatcher::ICreateProcess(const wchar* path, const wchar* args, bool
si.cb = sizeof(si); si.cb = sizeof(si);
wchar cmdline[MAX_PATH]; 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, BOOL result = CreateProcessW(path,
cmdline, cmdline,
NULL, NULL,
@ -576,12 +581,16 @@ void plSelfPatcher::IInstallDep(PatcherWork*& wk)
SetText("Installing updates..."); SetText("Installing updates...");
AsyncSleep(100); 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* extension = PathFindExtension(wk->fFileName);
wchar* process;
wchar args[MAX_PATH]; wchar args[MAX_PATH];
args[0] = 0; 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 // 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. // 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. // 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")) if (StrStrI(filename, L"vcredist"))
StrPack(args, L" /norestart", arrsize(args)); StrPack(args, L" /norestart", arrsize(args));
process = wk->fFileName; validateptr = IValidateExeReturnCode;
} else if (extension && StrCmpI(extension, L".msi") == 0) { } else if (extension && StrCmpI(extension, L".msi") == 0) {
StrPrintf(args, arrsize(args), L"/i %s /qr /norestart", wk->fFileName); StrPrintf(args, arrsize(args), L"/i \"%s\" /qr /norestart", process);
process = L"msiexec"; StrCopy(process, L"msiexec", arrsize(process));
validateptr = IValidateMsiReturnCode;
forceShell = true; forceShell = true;
} else { } else {
LogMsg(kLogError, L"plSelfPatcher::IInstallDep: Invalid extension '%s' for installer '%s'", 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); LogMsg(kLogDebug, L"plSelfPatcher::IInstallDep: Installing '%s %s'.", process, args);
HANDLE hProcess = ICreateProcess(process, args, forceShell); HANDLE hProcess = ICreateProcess(process, args, forceShell);
if (hProcess) { if (hProcess) {
if (IWaitProcess(hProcess) != ERROR_SUCCESS) if (IWaitProcess(hProcess, validateptr)) {
IDequeueWork(wk);
} else {
PathDeleteFile(wk->fFileName); PathDeleteFile(wk->fFileName);
IFatalError(L"Failed to install update.");
}
CloseHandle(hProcess); CloseHandle(hProcess);
IDequeueWork(wk);
} else { } else {
IFatalError(L"Failed to run installer."); 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 // 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 // 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 // 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) { if (quitWk->fType == kQuit) {
LogMsg(kLogPerf, "plSelfPatcher::IWaitProcess: Got shutdown during wait, attempting to terminate process."); LogMsg(kLogPerf, "plSelfPatcher::IWaitProcess: Got shutdown during wait, attempting to terminate process.");
TerminateProcess(hProcess, 1); TerminateProcess(hProcess, 1);
returncode = -1; return false;
break;
} }
} else if (idx == kWaitProcess) { } else if (idx == kWaitProcess) {
if (verify) {
DWORD returncode = 0;
GetExitCodeProcess(hProcess, &returncode); GetExitCodeProcess(hProcess, &returncode);
switch (returncode) { return verify(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;
} }
break; return true;
} else { } else {
FATAL("Invalid wait index"); FATAL("Invalid wait index");
return false;
} }
} else if (waitStatus == WAIT_FAILED) { } else if (waitStatus == WAIT_FAILED) {
wchar* error = FormatSystemError(); wchar* error = FormatSystemError();
LogMsg(kLogError, "plSelfPatcher::IWaitProcess: WaitForMultipleObjects failed! %s", error); LogMsg(kLogError, L"plSelfPatcher::IWaitProcess: WaitForMultipleObjects failed! %s", error);
LocalFree(error); LocalFree(error);
IFatalError(L"Internal Error."); IFatalError(L"Internal Error.");
returncode = -1; return false;
break; } else {
LogMsg(kLogError, "plSelfPatcher::IWaitProcess: Unhandled WaitForMultipleObjects result 0x%x", waitStatus);
return false;
} }
AsyncSleep(10); AsyncSleep(10);
} while (1); } 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;
}
} }
//============================================================================ //============================================================================

Loading…
Cancel
Save