634 lines
20 KiB
C++
634 lines
20 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: upgrdmsi.cpp
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "setup.h"
|
|
#include "resource.h"
|
|
|
|
// internet download
|
|
#include "wininet.h" // DeleteUrlCacheEntry, InternetCanonicalizeUrl
|
|
#include "urlmon.h" // URLDownloadToCacheFile
|
|
|
|
#include "wintrust.h" // WTD_UI_NONE
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include "strsafe.h"
|
|
|
|
#define WIN // scope W32 API
|
|
|
|
#define MSISIPAPI_DllRegisterServer "DllRegisterServer"
|
|
typedef HRESULT (WINAPI* PFnMsiSIPDllRegisterServer)();
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// IsMsiUpgradeNecessary
|
|
//
|
|
|
|
bool IsMsiUpgradeNecessary(ULONG ulReqMsiMinVer)
|
|
{
|
|
// attempt to load msi.dll in the system directory
|
|
|
|
char szSysMsiDll[MAX_PATH] = {0};
|
|
char szSystemFolder[MAX_PATH] = {0};
|
|
|
|
DWORD dwRet = WIN::GetSystemDirectory(szSystemFolder, MAX_PATH);
|
|
if (0 == dwRet || MAX_PATH < dwRet)
|
|
{
|
|
// failure or buffer too small; assume upgrade is necessary
|
|
DebugMsg("[Info] Can't obtain system directory; assuming upgrade is necessary");
|
|
return true;
|
|
}
|
|
|
|
if (FAILED(StringCchCopy(szSysMsiDll, sizeof(szSysMsiDll)/sizeof(szSysMsiDll[0]), szSystemFolder))
|
|
|| FAILED(StringCchCat(szSysMsiDll, sizeof(szSysMsiDll)/sizeof(szSysMsiDll[0]), "\\MSI.DLL")))
|
|
{
|
|
// failure to get path to msi.dll; assume upgrade is necessary
|
|
DebugMsg("[Info] Can't obtain msi.dll path; assuming upgrade is necessary");
|
|
return true;
|
|
}
|
|
|
|
HINSTANCE hinstMsiSys = LoadLibrary(szSysMsiDll);
|
|
if (0 == hinstMsiSys)
|
|
{
|
|
// can't load msi.dll; assume upgrade is necessary
|
|
DebugMsg("[Info] Can't load msi.dll; assuming upgrade is necessary");
|
|
|
|
return true;
|
|
}
|
|
FreeLibrary(hinstMsiSys);
|
|
|
|
// get version on msi.dll
|
|
DWORD dwInstalledMSVer;
|
|
dwRet = GetFileVersionNumber(szSysMsiDll, &dwInstalledMSVer, NULL);
|
|
if (ERROR_SUCCESS != dwRet)
|
|
{
|
|
// can't obtain version information; assume upgrade is necessary
|
|
DebugMsg("[Info] Can't obtain version information; assuming upgrade is necessary");
|
|
|
|
return true;
|
|
}
|
|
|
|
// compare version in system to the required minimum
|
|
ULONG ulInstalledVer = HIWORD(dwInstalledMSVer) * 100 + LOWORD(dwInstalledMSVer);
|
|
if (ulInstalledVer < ulReqMsiMinVer)
|
|
{
|
|
// upgrade is necessary
|
|
DebugMsg("[Info] Windows Installer upgrade is required. System Version = %d, Minimum Version = %d.\n", ulInstalledVer, ulReqMsiMinVer);
|
|
|
|
return true;
|
|
}
|
|
|
|
// no upgrade is necessary
|
|
DebugMsg("[Info] No upgrade is necessary. System version meets minimum requirements\n");
|
|
return false;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// UpgradeMsi
|
|
//
|
|
|
|
UINT UpgradeMsi(HINSTANCE hInst, CDownloadUI *piDownloadUI, LPCSTR szAppTitle, LPCSTR szBase, LPCSTR szUpdate, ULONG ulMinVer)
|
|
{
|
|
char *szTempPath = 0;
|
|
char *szUpdatePath = 0;
|
|
char *szFilePart = 0;
|
|
|
|
DWORD cchTempPath = 0;
|
|
DWORD cchUpdatePath = 0;
|
|
DWORD cchReturn = 0;
|
|
DWORD dwLastError = 0;
|
|
DWORD dwFileAttrib = 0;
|
|
UINT uiRet = 0;
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
// generate the path to the MSI update file = szBase + szUpdate
|
|
// note: szUpdate is a relative path
|
|
|
|
cchTempPath = lstrlen(szBase) + lstrlen(szUpdate) + 2; // 1 for null terminator, 1 for back slash
|
|
szTempPath = new char[cchTempPath];
|
|
if (!szTempPath)
|
|
{
|
|
ReportErrorOutOfMemory(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle);
|
|
uiRet = ERROR_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
memset((void*)szTempPath, 0x00, cchTempPath*sizeof(char));
|
|
|
|
// find 'setup.exe' in the path so we can remove it -- this is an already expanded path, that represents
|
|
// our current running location. It includes our executable name -- we want to find that and get rid of it
|
|
if (0 == GetFullPathName(szBase, cchTempPath, szTempPath, &szFilePart))
|
|
{
|
|
uiRet = GetLastError();
|
|
PostFormattedError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_INVALID_PATH, szTempPath);
|
|
goto CleanUp;
|
|
}
|
|
if (szFilePart)
|
|
*szFilePart = '\0';
|
|
|
|
hr = StringCchCat(szTempPath, cchTempPath, szUpdate);
|
|
if (FAILED(hr))
|
|
{
|
|
uiRet = HRESULT_CODE(hr);
|
|
PostFormattedError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_INVALID_PATH, szTempPath);
|
|
goto CleanUp;
|
|
}
|
|
|
|
cchUpdatePath = 2*cchTempPath;
|
|
szUpdatePath = new char[cchUpdatePath];
|
|
if (!szUpdatePath)
|
|
{
|
|
ReportErrorOutOfMemory(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle);
|
|
uiRet = ERROR_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// normalize the path
|
|
cchReturn = GetFullPathName(szTempPath, cchUpdatePath, szUpdatePath, &szFilePart);
|
|
if (cchReturn > cchUpdatePath)
|
|
{
|
|
// try again, with larger buffer
|
|
delete [] szUpdatePath;
|
|
cchUpdatePath = cchReturn;
|
|
szUpdatePath = new char[cchUpdatePath];
|
|
if (!szUpdatePath)
|
|
{
|
|
ReportErrorOutOfMemory(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle);
|
|
uiRet = ERROR_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
cchReturn = GetFullPathName(szTempPath, cchUpdatePath, szUpdatePath, &szFilePart);
|
|
}
|
|
if (0 == cchReturn)
|
|
{
|
|
uiRet = GetLastError();
|
|
PostFormattedError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_INVALID_PATH, szTempPath);
|
|
goto CleanUp;
|
|
}
|
|
|
|
// no download is necessary -- but we can check for the file's existence
|
|
dwFileAttrib = GetFileAttributes(szUpdatePath);
|
|
if (0xFFFFFFFF == dwFileAttrib)
|
|
{
|
|
// Update executable is missing
|
|
PostFormattedError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_NOUPDATE, szUpdatePath);
|
|
uiRet = ERROR_FILE_NOT_FOUND;
|
|
goto CleanUp;
|
|
}
|
|
|
|
uiRet = ValidateUpdate(hInst, piDownloadUI, szAppTitle, szUpdatePath, szUpdatePath, ulMinVer);
|
|
|
|
CleanUp:
|
|
if (szTempPath)
|
|
delete [] szTempPath;
|
|
if (szUpdatePath)
|
|
delete [] szUpdatePath;
|
|
|
|
return uiRet;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// DownloadAndUpgradeMsi
|
|
//
|
|
|
|
UINT DownloadAndUpgradeMsi(HINSTANCE hInst, CDownloadUI *piDownloadUI, LPCSTR szAppTitle, LPCSTR szUpdateLocation, LPCSTR szUpdate, LPCSTR szModuleFile, ULONG ulMinVer)
|
|
{
|
|
char *szTempPath = 0;
|
|
char *szUpdatePath = 0;
|
|
char *szUpdateCacheFile = 0;
|
|
const char *pch = 0;
|
|
|
|
DWORD cchTempPath = 0;
|
|
DWORD cchUpdatePath = 0;
|
|
DWORD cchUpdateCacheFile = 0;
|
|
DWORD dwLastError = 0;
|
|
UINT uiRet = 0;
|
|
HRESULT hr = 0;
|
|
DWORD Status = ERROR_SUCCESS;
|
|
|
|
char szDebugOutput[MAX_STR_LENGTH] = {0};
|
|
char szText[MAX_STR_CAPTION] = {0};
|
|
|
|
// generate the path to the update == UPDATELOCATION + szUpdate
|
|
// note: szUpdate is a relative path
|
|
cchTempPath = lstrlen(szUpdateLocation) + lstrlen(szUpdate) + 2; // 1 for slash, 1 for null
|
|
szTempPath = new char[cchTempPath];
|
|
if (!szTempPath)
|
|
{
|
|
ReportErrorOutOfMemory(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle);
|
|
uiRet = ERROR_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
memset((void*)szTempPath, 0x0, cchTempPath*sizeof(char));
|
|
hr = StringCchCopy(szTempPath, cchTempPath, szUpdateLocation);
|
|
if (FAILED(hr))
|
|
{
|
|
uiRet = HRESULT_CODE(hr);
|
|
PostFormattedError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_INVALID_PATH, szTempPath);
|
|
goto CleanUp;
|
|
}
|
|
|
|
// check for trailing slash on szUpdateLocation
|
|
pch = szUpdateLocation + lstrlen(szUpdateLocation) + 1; // put at null terminator
|
|
pch = CharPrev(szUpdateLocation, pch);
|
|
if (*pch != '/')
|
|
{
|
|
hr = StringCchCat(szTempPath, cchTempPath, szUrlPathSep);
|
|
if (FAILED(hr))
|
|
{
|
|
uiRet = HRESULT_CODE(hr);
|
|
PostFormattedError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_INVALID_PATH, szTempPath);
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
hr = StringCchCat(szTempPath, cchTempPath, szUpdate);
|
|
if (FAILED(hr))
|
|
{
|
|
uiRet = HRESULT_CODE(hr);
|
|
PostFormattedError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_INVALID_PATH, szTempPath);
|
|
goto CleanUp;
|
|
}
|
|
|
|
// canonicalize the URL path
|
|
cchUpdatePath = cchTempPath*2;
|
|
szUpdatePath = new char[cchUpdatePath];
|
|
if (!szUpdatePath)
|
|
{
|
|
ReportErrorOutOfMemory(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle);
|
|
uiRet = ERROR_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (!InternetCanonicalizeUrl(szTempPath, szUpdatePath, &cchUpdatePath, 0))
|
|
{
|
|
dwLastError = GetLastError();
|
|
if (ERROR_INSUFFICIENT_BUFFER == dwLastError)
|
|
{
|
|
// try again
|
|
delete [] szUpdatePath;
|
|
szUpdatePath = new char[cchUpdatePath];
|
|
if (!szUpdatePath)
|
|
{
|
|
ReportErrorOutOfMemory(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle);
|
|
uiRet = ERROR_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
dwLastError = 0; // reset to success for 2nd attempt
|
|
if (!InternetCanonicalizeUrl(szTempPath, szUpdatePath, &cchUpdatePath, 0))
|
|
dwLastError = GetLastError();
|
|
}
|
|
}
|
|
if (0 != dwLastError)
|
|
{
|
|
// error -- invalid path/Url
|
|
PostFormattedError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_INVALID_PATH, szTempPath);
|
|
uiRet = dwLastError;
|
|
goto CleanUp;
|
|
}
|
|
|
|
DebugMsg("[Info] Downloading update package from --> %s\n", szUpdatePath);
|
|
|
|
// set action text for download
|
|
WIN::LoadString(hInst, IDS_DOWNLOADING_UPDATE, szText, MAX_STR_CAPTION);
|
|
if (irmCancel == piDownloadUI->SetActionText(szText))
|
|
{
|
|
ReportUserCancelled(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle);
|
|
uiRet = ERROR_INSTALL_USEREXIT;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// download the Update file so we can run it -- must be local to execute
|
|
szUpdateCacheFile = new char[MAX_PATH];
|
|
cchUpdateCacheFile = MAX_PATH;
|
|
if (!szUpdateCacheFile)
|
|
{
|
|
ReportErrorOutOfMemory(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle);
|
|
uiRet = ERROR_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
hr = WIN::URLDownloadToCacheFile(NULL, szUpdatePath, szUpdateCacheFile, cchUpdateCacheFile, 0, /* IBindStatusCallback = */ &CDownloadBindStatusCallback(piDownloadUI));
|
|
if (piDownloadUI->HasUserCanceled())
|
|
{
|
|
ReportUserCancelled(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle);
|
|
uiRet = ERROR_INSTALL_USEREXIT;
|
|
goto CleanUp;
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
// error during download -- probably because file not found (or lost connection)
|
|
PostFormattedError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_NOUPDATE, szUpdatePath);
|
|
uiRet = ERROR_FILE_NOT_FOUND;
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
//
|
|
// Perform trust check on MSI. Note, this must be done in a separate process.
|
|
// This is because MSI 2.0 and higher register sip callbacks for verifying
|
|
// digital signatures on msi files. At this point, it is quite likely that
|
|
// the SIP callbacks have not been registered. So we don't want to load
|
|
// wintrust.dll into this process's image yet, otherwise it will remain unaware
|
|
// of the sip callbacks registered by WindowsInstaller-KB884016-x86.exe and will
|
|
// fail later when it tries to verify the signature on the msi file downloaded
|
|
// from the web.
|
|
//
|
|
Status = ExecuteVerifyUpdate(szModuleFile, szUpdateCacheFile);
|
|
if (TRUST_E_PROVIDER_UNKNOWN == Status)
|
|
{
|
|
PostError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_NO_WINTRUST);
|
|
uiRet = ERROR_CALL_NOT_IMPLEMENTED;
|
|
goto CleanUp;
|
|
}
|
|
else if (ERROR_SUCCESS != Status)
|
|
{
|
|
PostFormattedError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_UNTRUSTED, szUpdateCacheFile);
|
|
uiRet = HRESULT_CODE(TRUST_E_SUBJECT_NOT_TRUSTED);
|
|
goto CleanUp;
|
|
}
|
|
|
|
// continue other validations
|
|
uiRet = ValidateUpdate(hInst, piDownloadUI, szAppTitle, szUpdateCacheFile, szModuleFile, ulMinVer);
|
|
|
|
CleanUp:
|
|
if (szTempPath)
|
|
delete [] szTempPath;
|
|
if (szUpdatePath)
|
|
delete [] szUpdatePath;
|
|
if (szUpdateCacheFile)
|
|
{
|
|
WIN::DeleteUrlCacheEntry(szUpdateCacheFile);
|
|
delete [] szUpdateCacheFile;
|
|
}
|
|
|
|
return uiRet;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// IsUpdateRequiredVersion
|
|
//
|
|
// update package version is stamped as rmj.rmm.rup.rin
|
|
//
|
|
|
|
bool IsUpdateRequiredVersion(__in LPSTR szFilename, ULONG ulMinVer)
|
|
{
|
|
// get version of update package
|
|
DWORD dwUpdateMSVer;
|
|
DWORD dwRet = GetFileVersionNumber(szFilename, &dwUpdateMSVer, NULL);
|
|
if (ERROR_SUCCESS != dwRet)
|
|
{
|
|
// can't obtain version information; assume not proper version
|
|
DebugMsg("[Info] Can't obtain version information for update package; assuming it is not the proper version\n");
|
|
return false;
|
|
}
|
|
|
|
// compare version at source to required minimum
|
|
ULONG ulSourceVer = HIWORD(dwUpdateMSVer) * 100 + LOWORD(dwUpdateMSVer);
|
|
if (ulSourceVer < ulMinVer)
|
|
{
|
|
// source version won't get us to our minimum version
|
|
char szDebugOutput[MAX_STR_LENGTH] = {0};
|
|
DebugMsg("[Info] The update package is improper version for upgrade. Update package Version = %d, Minimum Version = %d.\n", ulSourceVer, ulMinVer);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ValidateUpdate
|
|
//
|
|
|
|
UINT ValidateUpdate(HINSTANCE hInst, CDownloadUI *piDownloadUI, LPCSTR szAppTitle, __in LPSTR szUpdatePath, LPCSTR szModuleFile, ULONG ulMinVer)
|
|
{
|
|
UINT uiRet = ERROR_SUCCESS;
|
|
|
|
char szShortPath[MAX_PATH] = {0};
|
|
|
|
// ensure Update is right version for Windows Installer upgrade
|
|
if (!IsUpdateRequiredVersion(szUpdatePath, ulMinVer))
|
|
{
|
|
// Update won't get us the right upgrade
|
|
PostFormattedError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_INCORRECT_UPDATE, szUpdatePath);
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
// upgrade msi
|
|
uiRet = ExecuteUpgradeMsi(szUpdatePath);
|
|
switch (uiRet)
|
|
{
|
|
case ERROR_SUCCESS:
|
|
case ERROR_SUCCESS_REBOOT_REQUIRED:
|
|
{
|
|
// nothing required at this time
|
|
break;
|
|
}
|
|
case ERROR_FILE_NOT_FOUND:
|
|
{
|
|
// Update executable not found
|
|
PostFormattedError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_NOUPDATE, szUpdatePath);
|
|
break;
|
|
}
|
|
default: // failure
|
|
{
|
|
// report error
|
|
PostError(hInst, piDownloadUI->GetCurrentWindow(), szAppTitle, IDS_FAILED_TO_UPGRADE_MSI);
|
|
break;
|
|
}
|
|
}
|
|
return uiRet;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ExecuteUpgradeMsi
|
|
//
|
|
|
|
DWORD ExecuteUpgradeMsi(__in LPSTR szUpgradeMsi)
|
|
{
|
|
DebugMsg("[Info] Running update package from --> %s\n", szUpgradeMsi);
|
|
|
|
DWORD dwResult = 0;
|
|
|
|
// build up CreateProcess structures
|
|
STARTUPINFO sui;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
memset((void*)&pi, 0x00, sizeof(PROCESS_INFORMATION));
|
|
memset((void*)&sui, 0x00, sizeof(STARTUPINFO));
|
|
sui.cb = sizeof(STARTUPINFO);
|
|
sui.dwFlags = STARTF_USESHOWWINDOW;
|
|
sui.wShowWindow = SW_SHOW;
|
|
|
|
//
|
|
// build command line and specify delayreboot option to Update
|
|
// three acounts for terminating null plus quotes for module
|
|
DWORD cchCommandLine = lstrlen(szUpgradeMsi) + lstrlen(szDelayReboot) + 3;
|
|
char *szCommandLine = new char[cchCommandLine];
|
|
|
|
if (!szCommandLine)
|
|
{
|
|
dwResult = ERROR_OUTOFMEMORY;
|
|
goto Return_ExecuteUpgradeMsi;
|
|
}
|
|
|
|
if (FAILED(StringCchCopy(szCommandLine, cchCommandLine, "\""))
|
|
|| FAILED(StringCchCat(szCommandLine, cchCommandLine, szUpgradeMsi))
|
|
|| FAILED(StringCchCat(szCommandLine, cchCommandLine, "\""))
|
|
|| FAILED(StringCchCat(szCommandLine, cchCommandLine, szDelayReboot)))
|
|
{
|
|
dwResult = ERROR_INSTALL_FAILURE;
|
|
goto Return_ExecuteUpgradeMsi;
|
|
}
|
|
|
|
//
|
|
// run update process
|
|
if(!WIN::CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL, &sui, &pi))
|
|
{
|
|
// failed to launch.
|
|
dwResult = GetLastError();
|
|
goto Return_ExecuteUpgradeMsi;
|
|
}
|
|
|
|
dwResult = WaitForProcess(pi.hProcess);
|
|
if(ERROR_SUCCESS != dwResult)
|
|
goto Return_ExecuteUpgradeMsi;
|
|
|
|
WIN::GetExitCodeProcess(pi.hProcess, &dwResult);
|
|
|
|
Return_ExecuteUpgradeMsi:
|
|
|
|
if (szCommandLine)
|
|
delete [] szCommandLine;
|
|
if (pi.hProcess)
|
|
WIN::CloseHandle(pi.hProcess);
|
|
if (pi.hThread)
|
|
WIN::CloseHandle(pi.hThread);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// ExecuteVerifyUpdate
|
|
//
|
|
DWORD ExecuteVerifyUpdate(LPCSTR szModuleFile, LPCSTR szUpdateCachePath)
|
|
{
|
|
DWORD dwResult = 0;
|
|
|
|
// build up CreateProcess structures
|
|
STARTUPINFO sui;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
memset((void*)&pi, 0x00, sizeof(PROCESS_INFORMATION));
|
|
memset((void*)&sui, 0x00, sizeof(STARTUPINFO));
|
|
sui.cb = sizeof(STARTUPINFO);
|
|
sui.dwFlags = STARTF_USESHOWWINDOW;
|
|
sui.wShowWindow = SW_SHOW;
|
|
|
|
//
|
|
// Build command line and specify delayreboot option to Update
|
|
// The nine extra characters are required for the following:
|
|
// 2 for the quotes enclosing the module path
|
|
// 2 for /v
|
|
// 2 for the spaces before and after /v
|
|
// 2 for the quotes enclosing the Update path
|
|
// 1 for the terminating null.
|
|
//
|
|
DWORD cchCommandLine = lstrlen(szModuleFile) + lstrlen(szUpdateCachePath) + 9;
|
|
char *szCommandLine = new char[cchCommandLine];
|
|
|
|
if (!szCommandLine)
|
|
{
|
|
dwResult = ERROR_OUTOFMEMORY;
|
|
goto Return_ExecuteVerifyUpdate;
|
|
}
|
|
|
|
if (FAILED(StringCchCopy(szCommandLine, cchCommandLine, "\""))
|
|
|| FAILED(StringCchCat(szCommandLine, cchCommandLine, szModuleFile))
|
|
|| FAILED(StringCchCat(szCommandLine, cchCommandLine, "\""))
|
|
|| FAILED(StringCchCat(szCommandLine, cchCommandLine, " /v \""))
|
|
|| FAILED(StringCchCat(szCommandLine, cchCommandLine, szUpdateCachePath))
|
|
|| FAILED(StringCchCat(szCommandLine, cchCommandLine, "\"")))
|
|
{
|
|
dwResult = ERROR_INSTALL_FAILURE;
|
|
goto Return_ExecuteVerifyUpdate;
|
|
}
|
|
|
|
//
|
|
// Run the verification process. We use a copy of ourselves to do this.
|
|
//
|
|
if(!WIN::CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL, &sui, &pi))
|
|
{
|
|
// failed to launch.
|
|
dwResult = GetLastError();
|
|
goto Return_ExecuteVerifyUpdate;
|
|
}
|
|
|
|
dwResult = WaitForProcess(pi.hProcess);
|
|
if(ERROR_SUCCESS != dwResult)
|
|
goto Return_ExecuteVerifyUpdate;
|
|
|
|
WIN::GetExitCodeProcess(pi.hProcess, &dwResult);
|
|
|
|
Return_ExecuteVerifyUpdate:
|
|
|
|
if (szCommandLine)
|
|
delete [] szCommandLine;
|
|
if (pi.hProcess)
|
|
WIN::CloseHandle(pi.hProcess);
|
|
if (pi.hThread)
|
|
WIN::CloseHandle(pi.hThread);
|
|
|
|
DebugMsg("[Info] Verification of Update returned %d\n", dwResult);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// WaitForProcess
|
|
//
|
|
|
|
DWORD WaitForProcess(HANDLE handle)
|
|
{
|
|
DWORD dwResult = NOERROR;
|
|
|
|
MSG msg;
|
|
memset((void*)&msg, 0x00, sizeof(MSG));
|
|
|
|
//loop forever to wait
|
|
while (true)
|
|
{
|
|
//wait for object
|
|
switch (WIN::MsgWaitForMultipleObjects(1, &handle, false, INFINITE, QS_ALLINPUT))
|
|
{
|
|
//success!
|
|
case WAIT_OBJECT_0:
|
|
goto Finish;
|
|
|
|
//not the process that we're waiting for
|
|
case (WAIT_OBJECT_0 + 1):
|
|
{
|
|
if (WIN::PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
|
|
{
|
|
WIN::TranslateMessage(&msg);
|
|
WIN::DispatchMessage(&msg);
|
|
}
|
|
|
|
break;
|
|
}
|
|
//did not return an OK; return error status
|
|
default:
|
|
{
|
|
dwResult = WIN::GetLastError();
|
|
goto Finish;
|
|
}
|
|
}
|
|
}
|
|
|
|
Finish:
|
|
return dwResult;
|
|
}
|