|
|
@ -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; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//============================================================================
|
|
|
|