2025-11-28 00:35:46 +09:00

948 lines
36 KiB
C++

//+-------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: setup.cpp
//
//--------------------------------------------------------------------------
#define WIN // scope W32 API
#define MSI // scope MSI API
#include <windows.h>
#include <tchar.h>
#include <assert.h>
#include <strsafe.h>
// internet download
#include "wininet.h" // DeleteUrlCacheEntry, InternetCanonicalizeUrl
#include "urlmon.h" // URLDownloadToCacheFile
// package trust
#include "wintrust.h"
#include "softpub.h"
// msi installation
#include "msidefs.h"
#include "msiquery.h"
#include "msi.h"
// setup.exe
#include "common.h"
#include "setup.h"
#include "setupui.h"
#include "resource.h"
//--------------------------------------------------------------------------------------
// MSI API -- delay load
//--------------------------------------------------------------------------------------
#define MSI_DLL "msi.dll"
#define MSIAPI_MsiSetInternalUI "MsiSetInternalUI"
typedef INSTALLUILEVEL (WINAPI* PFnMsiSetInternalUI)(INSTALLUILEVEL dwUILevel, HWND *phWnd);
#define MSIAPI_MsiInstallProduct "MsiInstallProductA"
typedef UINT (WINAPI* PFnMsiInstallProduct)(LPCSTR szPackagePath, LPCSTR szCommandLine);
#define MSIAPI_MsiApplyPatch "MsiApplyPatchA"
typedef UINT (WINAPI* PFnMsiApplyPatch)(LPCSTR szPatchPackage, LPCSTR szInstallPackage, INSTALLTYPE eInstallType, LPCSTR szCommandLine);
#define MSIAPI_MsiReinstallProduct "MsiReinstallProductA"
typedef UINT (WINAPI* PFnMsiReinstallProduct)(LPCSTR szProduct, DWORD dwReinstallMode);
#define MSIAPI_MsiQueryProductState "MsiQueryProductStateA"
typedef INSTALLSTATE (WINAPI* PFnMsiQueryProductState)(LPCSTR szProduct);
#define MSIAPI_MsiOpenDatabase "MsiOpenDatabaseA"
typedef UINT (WINAPI* PFnMsiOpenDatabase)(LPCSTR szDatabasePath, LPCSTR szPersist, MSIHANDLE *phDatabase);
#define MSIAPI_MsiDatabaseOpenView "MsiDatabaseOpenViewA"
typedef UINT (WINAPI* PFnMsiDatabaseOpenView)(MSIHANDLE hDatabase, LPCSTR szQuery, MSIHANDLE *phView);
#define MSIAPI_MsiViewExecute "MsiViewExecute"
typedef UINT (WINAPI* PFnMsiViewExecute)(MSIHANDLE hView, MSIHANDLE hRecord);
#define MSIAPI_MsiViewFetch "MsiViewFetch"
typedef UINT (WINAPI* PFnMsiViewFetch)(MSIHANDLE hView, MSIHANDLE *phRecord);
#define MSIAPI_MsiRecordGetString "MsiRecordGetStringA"
typedef UINT (WINAPI* PFnMsiRecordGetString)(MSIHANDLE hRecord, unsigned int uiField, LPSTR szValue, DWORD *pcchValueBuf);
#define MSIAPI_MsiCloseHandle "MsiCloseHandle"
typedef UINT (WINAPI* PFnMsiCloseHandle)(MSIHANDLE h);
const DWORD lcidLOCALE_INVARIANT = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
/////////////////////////////////////////////////////////////////////////////
// WinMain -- Application Entry Point
//
extern "C" int __stdcall WinMain(HINSTANCE hInst, HINSTANCE hPrevInst , __in LPSTR lpszCmdLine, int nCmdShow)
{
//-----------------------------------------------------------------------------------------------------------------
// VARIABLES
//
//-----------------------------------------------------------------------------------------------------------------
UINT uiRet = ERROR_SUCCESS;
HRESULT hr = S_OK;
char *szMsiFile = 0;
char *szBaseURL = 0;
char *szInstallPath = 0;
char *szMsiCacheFile = 0;
char *szOperation = 0;
char *szProductName = 0;
char *szMinimumMsi = 0;
char *szProperties = 0;
char *szInstProperties = 0;
char *szTempPath = 0;
char *szFilePart = 0;
char *szBase = 0;
char *szUpdate = 0;
char *szRegisteredMsiFolder = 0;
char *szMsiDllLocation = 0;
char szAppTitle[MAX_STR_CAPTION] = {0};
char szError[MAX_STR_LENGTH] = {0};
char szText[MAX_STR_CAPTION] = {0};
char szBanner[MAX_STR_LENGTH] = {0};
char szAction[MAX_STR_LENGTH] = {0};
char szUserPrompt[MAX_STR_LENGTH] = {0};
char szProductCode[MAX_LENGTH_GUID] = {0};
char szModuleFile[MAX_PATH] = {0};
DWORD dwModuleFileSize = MAX_PATH;
DWORD dwMsiFileSize = 0;
DWORD dwBaseURLSize = 0;
DWORD cchInstallPath = 0;
DWORD dwMsiCacheFileSize = 0;
DWORD dwOperationSize = 0;
DWORD dwProductNameSize = 0;
DWORD dwMinimumMsiSize = 0;
DWORD dwPropertiesSize = 0;
DWORD cchInstProperties = 0;
DWORD cchTempPath = 0;
DWORD dwLastError = 0;
DWORD cchReturn = 0;
DWORD dwBaseUpdateSize = 0;
DWORD dwUpdateSize = 0;
DWORD dwResult = 0;
DWORD dwType = 0;
DWORD dwProductCodeSize = MAX_LENGTH_GUID;
DWORD dwRegisteredMsiFolderSize = 0;
DWORD dwMsiDllLocationSize = 0;
ULONG ulMsiMinVer = 0;
char *szStopScan = NULL;
bool fDelayRebootReq = false;
bool fPatch = false;
bool fQFE = false;
bool fOSSupported = false;
emEnum emExecMode = emPreset;
HKEY hInstallerKey = 0;
HMODULE hMsi = 0;
PFnMsiSetInternalUI pfnMsiSetInternalUI = 0;
PFnMsiInstallProduct pfnMsiInstallProduct = 0;
PFnMsiApplyPatch pfnMsiApplyPatch = 0;
PFnMsiReinstallProduct pfnMsiReinstallProduct = 0;
PFnMsiQueryProductState pfnMsiQueryProductState = 0;
PFnMsiOpenDatabase pfnMsiOpenDatabase = 0;
PFnMsiDatabaseOpenView pfnMsiDatabaseOpenView = 0;
PFnMsiViewExecute pfnMsiViewExecute = 0;
PFnMsiViewFetch pfnMsiViewFetch = 0;
PFnMsiRecordGetString pfnMsiRecordGetString = 0;
PFnMsiCloseHandle pfnMsiCloseHandle = 0;
MSIHANDLE hDatabase = 0;
MSIHANDLE hView = 0;
MSIHANDLE hRec = 0;
INSTALLSTATE isProduct = INSTALLSTATE_UNKNOWN;
const char * szAdminImagePath = 0;
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
// create our UI object
CDownloadUI DownloadUI;
// Load our AppTitle (caption)
WIN::LoadString(hInst, IDS_APP_TITLE, szAppTitle, sizeof(szAppTitle)/sizeof(char));
// Obtain path we are running from
if (0 == WIN::GetModuleFileName(hInst, szModuleFile, dwModuleFileSize))
{
// No UI displayed. Silent failure.
uiRet = GetLastError();
goto CleanUp;
}
DebugMsg("[Info] we are running from --> %s\n", szModuleFile);
// Figure out what we want to do
emExecMode = GetExecutionMode (lpszCmdLine);
if (emVerify == emExecMode)
{
//
// We don't want any UI to be displayed in this case. The return value
// from the exe is the result of the verification. Therefore, this
// should be done before initializing the UI.
//
uiRet = VerifyFileSignature (szModuleFile, lpszCmdLine);
if (ERROR_BAD_ARGUMENTS != uiRet)
goto CleanUp;
}
if (ERROR_BAD_ARGUMENTS == uiRet || emHelp == emExecMode)
{
DisplayUsage(hInst, NULL, szAppTitle);
goto CleanUp;
}
//
// NOTE:
// Delay handling admin. installs until we have determined if we are
// patching an existing install or if we are doing a default install.
//
// initialize our UI object with desktop as parent
DownloadUI.Initialize(hInst, /* hwndParent = */ 0, szAppTitle);
// Check if we are installing on an OS that supports Windows Installer 3.0
fOSSupported = IsOSSupported();
if(!fOSSupported)
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_OS_NOT_SUPPORTED);
uiRet = ERROR_INSTALL_FAILURE;
goto CleanUp;
}
HANDLE hMutex = 0;
// only run one instance at a time
if (AlreadyInProgress(hMutex))
{
// silently return - correct return code ?
uiRet = ERROR_INSTALL_ALREADY_RUNNING;
goto CleanUp;
}
// determine operation, default (if not present) is INSTALL
if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_OPERATION, &szOperation, dwOperationSize)))
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
goto CleanUp;
}
if (ERROR_SUCCESS != uiRet)
{
// set operation to default which is install
if (szOperation)
delete [] szOperation;
szOperation = new char[lstrlen(szDefaultOperation) + 1];
if (!szOperation)
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
goto CleanUp;
}
if (FAILED(StringCchCopy(szOperation, lstrlen(szDefaultOperation) + 1, szDefaultOperation)))
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INTERNAL_ERROR);
uiRet = ERROR_INSTALL_FAILURE;
goto CleanUp;
}
}
// obtain name of product
if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_PRODUCTNAME, &szProductName, dwProductNameSize)))
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
goto CleanUp;
}
if (ERROR_SUCCESS != uiRet)
{
// use default
if (szProductName)
delete [] szProductName;
szProductName = new char[MAX_STR_CAPTION];
if (!szProductName)
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
goto CleanUp;
}
WIN::LoadString(hInst, IDS_DEFAULT_PRODUCT, szProductName, MAX_STR_CAPTION);
}
// set banner text
WIN::LoadString(hInst, IDS_BANNER_TEXT, szText, MAX_STR_CAPTION);
StringCchPrintf(szBanner, sizeof(szBanner), szText, szProductName);
if (irmCancel == DownloadUI.SetBannerText(szBanner))
{
ReportUserCancelled(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_INSTALL_USEREXIT;
goto CleanUp;
}
// Determine if this is a patch or a normal install.
if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_DATABASE, &szMsiFile, dwMsiFileSize)))
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
goto CleanUp;
}
else if (ERROR_SUCCESS != uiRet)
{
// look for patch
if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_PATCH, &szMsiFile, dwMsiFileSize)))
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
goto CleanUp;
}
else if (ERROR_SUCCESS != uiRet)
{
PostResourceNotFoundError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, ISETUPPROPNAME_DATABASE);
goto CleanUp;
}
fPatch = true;
}
//
// If we are here, this is either an admin. install or a default install.
// File signature verification, help and other invalid parameters have
// already been taken care of above.
//
if (emAdminInstall == emExecMode)
{
uiRet = GetAdminInstallInfo (fPatch, lpszCmdLine, &szAdminImagePath);
if (ERROR_BAD_ARGUMENTS == uiRet)
{
DisplayUsage(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
goto CleanUp;
}
}
//
// At this point, the validation of the commandline arguments is complete
// and we have all the information we need.
//
// obtain minimum required MSI version
if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_MINIMUM_MSI, &szMinimumMsi, dwMinimumMsiSize)))
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
goto CleanUp;
}
else if (ERROR_SUCCESS != uiRet)
{
PostResourceNotFoundError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, ISETUPPROPNAME_MINIMUM_MSI);
goto CleanUp;
}
// make sure required Msi version is a valid value -- must be >= 150
ulMsiMinVer = strtoul(szMinimumMsi, &szStopScan, 10);
if (!szStopScan || (szStopScan == szMinimumMsi) || (*szStopScan != 0) || ulMsiMinVer < MINIMUM_SUPPORTED_MSI_VERSION)
{
// invalid minimum version string
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INVALID_VER_STR, szMinimumMsi, MINIMUM_SUPPORTED_MSI_VERSION);
uiRet = ERROR_INVALID_PARAMETER;
goto CleanUp;
}
DebugMsg("[Resource] Minimum Msi Value = %d\n", ulMsiMinVer);
// compare minimum required MSI version to that which is on the machine
if (IsMsiUpgradeNecessary(ulMsiMinVer))
{
DebugMsg("[Info] Upgrade of Windows Installer is requested\n");
// make sure this is admin -- must have admin priviledges to upgrade Windows Installer
if (!IsAdmin())
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_REQUIRES_ADMIN_PRIV);
uiRet = ERROR_INSTALL_FAILURE;
goto CleanUp;
}
// Ask the user if they want to upgrade the installer
WIN::LoadString(hInst, IDS_ALLOW_MSI_UPDATE, szUserPrompt, MAX_STR_LENGTH);
if (IDYES != WIN::MessageBox(DownloadUI.GetCurrentWindow(), szUserPrompt, szAppTitle, MB_YESNO|MB_ICONQUESTION))
{
// user decided to cancel
ReportUserCancelled(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_INSTALL_USEREXIT;
goto CleanUp;
}
if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_UPDATE, &szUpdate, dwUpdateSize)))
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
goto CleanUp;
}
else if (ERROR_SUCCESS != uiRet)
{
PostResourceNotFoundError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, ISETUPPROPNAME_UPDATE);
goto CleanUp;
}
// determine if we need to download the Windows Installer update package from the web -- based on presence of UPDATELOCATION property
if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_UPDATELOCATION, &szBase, dwBaseUpdateSize)))
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
goto CleanUp;
}
else if (ERROR_SUCCESS == uiRet)
{
// presence of UPDATELOCATION property indicates assumption of URL source
if (ERROR_SUCCESS != (uiRet = DownloadAndUpgradeMsi(hInst, &DownloadUI, szAppTitle, szBase, szUpdate, szModuleFile, ulMsiMinVer)))
{
if (ERROR_SUCCESS_REBOOT_REQUIRED == uiRet)
{
// successful, but must reboot at end
fDelayRebootReq = true;
}
else
goto CleanUp;
}
}
else
{
// lack of UPDATELOCATION property indicates assumption of Media source
if (ERROR_SUCCESS != (uiRet = UpgradeMsi(hInst, &DownloadUI, szAppTitle, szModuleFile, szUpdate, ulMsiMinVer)))
{
if (ERROR_SUCCESS_REBOOT_REQUIRED == uiRet)
{
// successful, but must reboot at end
fDelayRebootReq = true;
}
else
goto CleanUp;
}
}
}
DebugMsg("[Info] Windows Installer has been upgraded, or was already correct version\n");
// perform some extra authoring validation
if (fPatch
&& CSTR_EQUAL != CompareString(lcidLOCALE_INVARIANT, NORM_IGNORECASE, szOperation, -1, szMinPatchOperation, -1)
&& CSTR_EQUAL != CompareString(lcidLOCALE_INVARIANT, NORM_IGNORECASE, szOperation, -1, szMajPatchOperation, -1)
&& CSTR_EQUAL != CompareString(lcidLOCALE_INVARIANT, NORM_IGNORECASE, szOperation, -1, szDefaultOperation, -1))
{
// wrong operation
DebugMsg("[Error] Operation %s is not valid for a patch\n", szOperation);
PostFormattedError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INVALID_OPERATION, szOperation);
uiRet = ERROR_INVALID_PARAMETER;
goto CleanUp;
}
else if (!fPatch
&& CSTR_EQUAL != CompareString(lcidLOCALE_INVARIANT, NORM_IGNORECASE, szOperation, -1, szInstallOperation, -1)
&& CSTR_EQUAL != CompareString(lcidLOCALE_INVARIANT, NORM_IGNORECASE, szOperation, -1, szInstallUpdOperation, -1)
&& CSTR_EQUAL != CompareString(lcidLOCALE_INVARIANT, NORM_IGNORECASE, szOperation, -1, szDefaultOperation, -1))
{
// wrong operation
DebugMsg("[Error] Operation %s is not valid for a package\n", szOperation);
PostFormattedError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INVALID_OPERATION, szOperation);
uiRet = ERROR_INVALID_PARAMETER;
goto CleanUp;
}
// by now we either have a MSI or a MSP
if (CSTR_EQUAL == CompareString(lcidLOCALE_INVARIANT, NORM_IGNORECASE, szOperation, -1, szMinPatchOperation, -1)
|| CSTR_EQUAL == CompareString(lcidLOCALE_INVARIANT, NORM_IGNORECASE, szOperation, -1, szInstallUpdOperation, -1)
|| (fPatch && CSTR_EQUAL == CompareString(lcidLOCALE_INVARIANT, NORM_IGNORECASE, szOperation, -1, szDefaultOperation, -1)))
fQFE = true;
// obtain base URL
if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_BASEURL, &szBaseURL, dwBaseURLSize)))
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
goto CleanUp;
}
else if (ERROR_SUCCESS == uiRet)
{
// presence of BASEURL property indicates assumption of URL source . . .
// generate the path to the installation package == baseURL + msiFile
// note: msiFile is a relative path
cchTempPath = lstrlen(szBaseURL) + lstrlen(szMsiFile) + 2; // 1 for slash, 1 for null
szTempPath = new char[cchTempPath ];
if (!szTempPath)
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_OUTOFMEMORY;
goto CleanUp;
}
if (FAILED(StringCchCopy(szTempPath, cchTempPath, szBaseURL)))
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INTERNAL_ERROR);
uiRet = ERROR_INSTALL_FAILURE;
goto CleanUp;
}
// check for trailing slash on szBaseURL
char *pch = szBaseURL + lstrlen(szBaseURL) + 1; // put at null terminator
pch = CharPrev(szBaseURL, pch);
if (*pch != '/')
{
if (FAILED(StringCchCat(szTempPath, cchTempPath, szUrlPathSep)))
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INTERNAL_ERROR);
uiRet = ERROR_INSTALL_FAILURE;
goto CleanUp;
}
}
if (FAILED(StringCchCat(szTempPath, cchTempPath, szMsiFile)))
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INTERNAL_ERROR);
uiRet = ERROR_INSTALL_FAILURE;
goto CleanUp;
}
// canocialize the URL path
cchInstallPath = cchTempPath*2;
szInstallPath = new char[cchInstallPath];
if (!szInstallPath)
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_OUTOFMEMORY;
goto CleanUp;
}
dwLastError = 0; // success
if (!InternetCanonicalizeUrl(szTempPath, szInstallPath, &cchInstallPath, 0))
{
dwLastError = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == dwLastError)
{
// try again
delete [] szInstallPath;
szInstallPath = new char[cchInstallPath];
if (!szInstallPath)
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_OUTOFMEMORY;
goto CleanUp;
}
dwLastError = 0; // reset to success for 2nd attempt
if (!InternetCanonicalizeUrl(szTempPath, szInstallPath, &cchInstallPath, 0))
dwLastError = GetLastError();
}
}
if (0 != dwLastError)
{
// error -- invalid path/Url
PostFormattedError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INVALID_PATH, szTempPath);
uiRet = dwLastError;
goto CleanUp;
}
// set action text for download
WIN::LoadString(hInst, IDS_DOWNLOADING_PACKAGE, szText, MAX_STR_CAPTION);
StringCchPrintf(szAction, sizeof(szAction), szText, szMsiFile);
if (irmCancel == DownloadUI.SetActionText(szAction))
{
ReportUserCancelled(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_INSTALL_USEREXIT;
goto CleanUp;
}
// download the msi file so we can attempt a trust check -- must be local for WinVerifyTrust
DebugMsg("[Info] Downloading msi file %s for WinVerifyTrust check\n", szInstallPath);
szMsiCacheFile = new char[MAX_PATH];
dwMsiCacheFileSize = MAX_PATH;
if (!szMsiCacheFile)
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_OUTOFMEMORY;
goto CleanUp;
}
hr = WIN::URLDownloadToCacheFile(NULL, szInstallPath, szMsiCacheFile, dwMsiCacheFileSize, 0, /* IBindStatusCallback = */ &CDownloadBindStatusCallback(&DownloadUI));
if (DownloadUI.HasUserCanceled())
{
ReportUserCancelled(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_INSTALL_USEREXIT;
goto CleanUp;
}
if (FAILED(hr))
{
// error during download -- probably because file not found (or lost connection)
PostFormattedError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_NOMSI, szInstallPath);
uiRet = ERROR_FILE_NOT_FOUND;
goto CleanUp;
}
DebugMsg("[Info] Msi file was cached to %s\n", szMsiCacheFile);
// set action text for trust verification
WIN::LoadString(hInst, IDS_VALIDATING_SIGNATURE, szText, MAX_STR_CAPTION);
StringCchPrintf(szAction, sizeof(szAction), szText, szMsiFile);
if (irmCancel == DownloadUI.SetActionText(szAction))
{
ReportUserCancelled(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_INSTALL_USEREXIT;
goto CleanUp;
}
// perform trust check
itvEnum itv = IsPackageTrusted(szModuleFile, szMsiCacheFile, DownloadUI.GetCurrentWindow());
if (itvWintrustNotOnMachine == itv)
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_NO_WINTRUST);
uiRet = ERROR_CALL_NOT_IMPLEMENTED;
goto CleanUp;
}
else if (itvUnTrusted == itv)
{
PostFormattedError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_UNTRUSTED, szInstallPath);
uiRet = HRESULT_CODE(TRUST_E_SUBJECT_NOT_TRUSTED);
goto CleanUp;
}
}
else
{
// lack of BASEURL property indicates assumption of Media source
// generate the path to the Msi file = szModuleFile + msiFile
// note: msiFile is a relative path
cchTempPath = lstrlen(szModuleFile) + lstrlen(szMsiFile) + 2; // 1 for null terminator, 1 for back slash
szTempPath = new char[cchTempPath];
if (!szTempPath)
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_OUTOFMEMORY;
goto CleanUp;
}
// find 'setup.exe' in the path so we can remove it
if (0 == GetFullPathName(szModuleFile, cchTempPath, szTempPath, &szFilePart))
{
uiRet = GetLastError();
PostFormattedError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INVALID_PATH, szTempPath);
goto CleanUp;
}
if (szFilePart)
*szFilePart = '\0';
if (FAILED(StringCchCat(szTempPath, cchTempPath, szMsiFile)))
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INTERNAL_ERROR);
uiRet = ERROR_INSTALL_FAILURE;
goto CleanUp;
}
cchInstallPath = 2*cchTempPath;
szInstallPath = new char[cchInstallPath];
if (!szInstallPath)
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_OUTOFMEMORY;
goto CleanUp;
}
// normalize the path
cchReturn = GetFullPathName(szTempPath, cchInstallPath, szInstallPath, &szFilePart);
if (cchReturn > cchInstallPath)
{
// try again, with larger buffer
delete [] szInstallPath;
cchInstallPath = cchReturn;
szInstallPath = new char[cchInstallPath];
if (!szInstallPath)
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_OUTOFMEMORY;
goto CleanUp;
}
cchReturn = GetFullPathName(szTempPath, cchInstallPath, szInstallPath, &szFilePart);
}
if (0 == cchReturn)
{
// error -- invalid path
PostFormattedError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INVALID_PATH, szTempPath);
uiRet = dwLastError;
goto CleanUp;
}
// no download is necessary -- but we can check for the file's existence
DWORD dwFileAttrib = GetFileAttributes(szInstallPath);
if (0xFFFFFFFF == dwFileAttrib)
{
// package is missing
PostFormattedError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_NOMSI, szInstallPath);
uiRet = ERROR_FILE_NOT_FOUND;
goto CleanUp;
}
}
//
// good to go -- terminate our UI and let the Windows Installer take over
//
// retrieve the optional command line PROPERTY = VALUE strings if available
if (ERROR_OUTOFMEMORY == (uiRet = SetupLoadResourceString(hInst, ISETUPPROPNAME_PROPERTIES, &szProperties, dwPropertiesSize)))
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_OUTOFMEMORY;
goto CleanUp;
}
else if (ERROR_SUCCESS != uiRet)
{
// PROPERTY=VALUE pairs not specified
if (szProperties)
delete [] szProperties;
szProperties = NULL;
}
DownloadUI.Terminate();
//
// perform install
//
hMsi = LoadLibrary(MSI_DLL);
if (hMsi)
{
pfnMsiSetInternalUI = (PFnMsiSetInternalUI)GetProcAddress(hMsi, MSIAPI_MsiSetInternalUI);
pfnMsiInstallProduct = (PFnMsiInstallProduct)GetProcAddress(hMsi, MSIAPI_MsiInstallProduct);
pfnMsiApplyPatch = (PFnMsiApplyPatch)GetProcAddress(hMsi, MSIAPI_MsiApplyPatch);
pfnMsiReinstallProduct = (PFnMsiReinstallProduct)GetProcAddress(hMsi, MSIAPI_MsiReinstallProduct);
pfnMsiQueryProductState = (PFnMsiQueryProductState)GetProcAddress(hMsi, MSIAPI_MsiQueryProductState);
pfnMsiOpenDatabase = (PFnMsiOpenDatabase)GetProcAddress(hMsi, MSIAPI_MsiOpenDatabase);
pfnMsiDatabaseOpenView = (PFnMsiDatabaseOpenView)GetProcAddress(hMsi, MSIAPI_MsiDatabaseOpenView);
pfnMsiViewExecute = (PFnMsiViewExecute)GetProcAddress(hMsi, MSIAPI_MsiViewExecute);
pfnMsiViewFetch = (PFnMsiViewFetch)GetProcAddress(hMsi, MSIAPI_MsiViewFetch);
pfnMsiRecordGetString = (PFnMsiRecordGetString)GetProcAddress(hMsi, MSIAPI_MsiRecordGetString);
pfnMsiCloseHandle = (PFnMsiCloseHandle)GetProcAddress(hMsi, MSIAPI_MsiCloseHandle);
}
if (!hMsi || !pfnMsiSetInternalUI || !pfnMsiInstallProduct || !pfnMsiApplyPatch || !pfnMsiReinstallProduct || !pfnMsiQueryProductState
|| !pfnMsiDatabaseOpenView || !pfnMsiViewExecute || !pfnMsiViewFetch || !pfnMsiRecordGetString || !pfnMsiCloseHandle)
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_FAILED_TO_UPGRADE_MSI);
uiRet = ERROR_INSTALL_FAILURE;
goto CleanUp;
}
DebugMsg("[Info] Setting Internal UI level to FULL...\n");
pfnMsiSetInternalUI(INSTALLUILEVEL_FULL, 0);
if (!fPatch)
{
// performing install or reinstall/recache
DebugMsg("[Info] Calling MsiInstallProduct with szInstallPath = %s", szInstallPath);
DebugMsg(" and szCommandLine = %s\n", szProperties ? szProperties : "{null}");
// default operation for a package is INSTALL
if (fQFE)
{
// check to see if this product is already installed
if (ERROR_SUCCESS == pfnMsiOpenDatabase(szMsiCacheFile ? szMsiCacheFile : szInstallPath, MSIDBOPEN_READONLY, &hDatabase)
&& ERROR_SUCCESS == pfnMsiDatabaseOpenView(hDatabase, sqlProductCode, &hView)
&& ERROR_SUCCESS == pfnMsiViewExecute(hView, 0)
&& ERROR_SUCCESS == pfnMsiViewFetch(hView, &hRec)
&& ERROR_SUCCESS == pfnMsiRecordGetString(hRec, 1, szProductCode, &dwProductCodeSize))
{
isProduct = pfnMsiQueryProductState(szProductCode);
DebugMsg("[Info] MsiQueryProductState returned %d\n", isProduct);
if (INSTALLSTATE_ADVERTISED != isProduct && INSTALLSTATE_DEFAULT != isProduct)
{
// product is unknown, so this will be a first time install
DebugMsg("[Info] The product code '%s' is unknown. Will use first time install logic...\n", szProductCode);
fQFE = false;
}
else
{
// product is known, use QFE syntax
DebugMsg("[Info] The product code '%s' is known. Will use QFE recache and reinstall upgrade logic...\n", szProductCode);
}
}
else
{
// some failure occurred when processing the product code, so treat as non-QFE
DebugMsg("[Info] Unable to process product code. Will treat as first time install...\n");
fQFE = false;
}
if (hDatabase)
pfnMsiCloseHandle(hDatabase);
if (hView)
pfnMsiCloseHandle(hView);
if (hRec)
pfnMsiCloseHandle(hRec);
}
//
// Set up the properties to be passed into MSIInstallProduct
//
if (fQFE && !szProperties)
cchInstProperties = lstrlen (szDefaultInstallUpdCommandLine);
else if (szProperties)
cchInstProperties = lstrlen (szProperties);
if (emAdminInstall == emExecMode)
cchInstProperties += lstrlen (szAdminInstallProperty);
szInstProperties = new char[cchInstProperties + 1];
if (! szInstProperties)
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_OUTOFMEMORY;
goto CleanUp;
}
if (fQFE && !szProperties)
{
if (FAILED(StringCchCopy(szInstProperties, cchInstProperties + 1, szDefaultInstallUpdCommandLine)))
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INTERNAL_ERROR);
uiRet = ERROR_INSTALL_FAILURE;
goto CleanUp;
}
}
else if (szProperties)
{
if (FAILED(StringCchCopy(szInstProperties, cchInstProperties + 1, szProperties)))
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INTERNAL_ERROR);
uiRet = ERROR_INSTALL_FAILURE;
goto CleanUp;
}
}
else
szInstProperties[0] = '\0';
if (emAdminInstall == emExecMode)
{
if (FAILED(StringCchCat(szInstProperties, cchInstProperties + 1, szAdminInstallProperty)))
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INTERNAL_ERROR);
uiRet = ERROR_INSTALL_FAILURE;
goto CleanUp;
}
}
uiRet = pfnMsiInstallProduct(szInstallPath, szInstProperties);
if (ERROR_SUCCESS != uiRet)
{
// attempt to display an error message stored in msi.dll
PostMsiError(hInst, hMsi, DownloadUI.GetCurrentWindow(), szAppTitle, uiRet);
}
DebugMsg("[Info] MsiInstallProduct returned %d\n", uiRet);
}
else
{
// default Operation for a patch is MINPATCH
// if szProperties is NULL, use our default value for QFE patches
if (!szProperties && fQFE)
{
DebugMsg("[Info] Patch is a MINPATCH (small or minor update patch) so using default command line '%s'\n", szDefaultMinPatchCommandLine);
szProperties = new char[lstrlen(szDefaultMinPatchCommandLine) + 1];
if (!szProperties)
{
ReportErrorOutOfMemory(hInst, DownloadUI.GetCurrentWindow(), szAppTitle);
uiRet = ERROR_OUTOFMEMORY;
goto CleanUp;
}
if (FAILED(StringCchCopy(szProperties, lstrlen(szDefaultMinPatchCommandLine) + 1, szDefaultMinPatchCommandLine)))
{
PostError(hInst, DownloadUI.GetCurrentWindow(), szAppTitle, IDS_INTERNAL_ERROR);
uiRet = ERROR_INSTALL_FAILURE;
goto CleanUp;
}
}
if (emAdminInstall == emExecMode)
{
// performing a patch
DebugMsg("[Info] Calling MsiApplyPatch with szPatchPackage = %s", szMsiCacheFile);
DebugMsg(" and szInstallPackage = %s and eInstallType = INSTALLTYPE_NETWORK_IMAGE", szAdminImagePath);
DebugMsg(" and szCommandLine = %s\n", szProperties ? szProperties : "{null}");
uiRet = pfnMsiApplyPatch(szMsiCacheFile, szAdminImagePath, INSTALLTYPE_NETWORK_IMAGE, szProperties);
}
else
{
// performing a patch
DebugMsg("[Info] Calling MsiApplyPatch with szPatchPackage = %s", szInstallPath);
DebugMsg(" and szInstallPackage = {null} and eInstallType = INSTALLTYPE_DEFAULT");
DebugMsg(" and szCommandLine = %s\n", szProperties ? szProperties : "{null}");
uiRet = pfnMsiApplyPatch(szInstallPath, NULL, INSTALLTYPE_DEFAULT, szProperties);
}
if (ERROR_SUCCESS != uiRet)
{
// attempt to display an error message stored in msi.dll
PostMsiError(hInst, hMsi, DownloadUI.GetCurrentWindow(), szAppTitle, uiRet);
}
DebugMsg("[Info] MsiApplyPatch returned %d\n", uiRet);
}
CleanUp:
if (szMsiFile)
delete [] szMsiFile;
if (szBaseURL)
delete [] szBaseURL;
if (szInstallPath)
delete [] szInstallPath;
if (szMsiCacheFile)
{
WIN::DeleteUrlCacheEntry(szMsiCacheFile);
delete [] szMsiCacheFile;
}
if (szProductName)
delete [] szProductName;
if (szMinimumMsi)
delete [] szMinimumMsi;
if (szProperties)
delete [] szProperties;
if (szTempPath)
delete [] szTempPath;
if (szBase)
delete [] szBase;
if (szUpdate)
delete [] szUpdate;
if (szRegisteredMsiFolder)
delete [] szRegisteredMsiFolder;
if (szMsiDllLocation)
delete [] szMsiDllLocation;
if (szOperation)
delete [] szOperation;
if(hMutex)
CloseHandle(hMutex);
if (hMsi)
FreeLibrary(hMsi);
DebugMsg("[Info] Setup exit code is %d\n", uiRet);
if (fDelayRebootReq)
{
// need to reboot machine for updating Windows Installer
WIN::LoadString(hInst, IDS_REBOOT_REQUIRED, szAction, MAX_STR_LENGTH);
if (IDYES == MessageBox(NULL, szAction, szAppTitle, MB_YESNO|MB_ICONQUESTION))
{
// must first aquire system shutdown privileges on NT/Win2K
AcquireShutdownPrivilege();
// initiate system shutdown for reboot
WIN::ExitWindowsEx(EWX_REBOOT, PCLEANUI | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION);
}
}
return uiRet;
}