NDUPDATE_API BOOL WINAPI NdasUpdateDoUpdate( IN HWND hWndParent, IN LPCTSTR szBaseURL, IN PNDUPDATE_SYSTEM_INFO pSystemInformation) { HINSTANCE hResInst = _NdasUpdateDllInstance; NDUPDATE_UPDATE_INFO updateInformation = {0}; CDownloadUI downloadUI; TCHAR szUpdaterTitle[MAX_STR_CAPTION]; ::LoadString( hResInst, IDS_UPDATER_TITLE, szUpdaterTitle, MAX_STR_CAPTION); BOOL fSuccess = downloadUI.Initialize( hResInst, hWndParent, szUpdaterTitle); CString strBanner; strBanner.LoadString(IDS_STATUS_CHECK_UPDATE); downloadUI.SetBannerText(strBanner); fSuccess = ::NdasUpdateGetUpdateInfo( &CDownloadBindStatusCallback(&downloadUI), szBaseURL, pSystemInformation, &updateInformation); if (!fSuccess) { CString strDescr; strDescr.LoadString(IDS_UPDATER_ERROR_UPDATE_CHECK); CString strFormatted; strFormatted.FormatMessage( IDS_UPDATER_ERROR_FMT, (LPCTSTR)strDescr, ::GetLastError()); ::MessageBox( hWndParent, strFormatted, szUpdaterTitle, MB_OK | MB_ICONERROR); downloadUI.Terminate(); return FALSE; } strBanner.LoadString(IDS_STATUS_CHECK_UPDATE_DONE); downloadUI.SetBannerText(strBanner); if (!updateInformation.fNeedUpdate) { CString strMsg; strMsg.LoadString(IDS_NO_UPDATE); ::MessageBox( hWndParent, strMsg, szUpdaterTitle, MB_OK | MB_ICONINFORMATION); downloadUI.Terminate(); return TRUE; } CString strMsg; strMsg.LoadString(IDS_NEW_UPDATE); UINT_PTR uiResponse = ::MessageBox( hWndParent, strMsg, szUpdaterTitle, MB_YESNO | MB_ICONQUESTION); if (IDYES != uiResponse) { downloadUI.Terminate(); return TRUE; } strBanner.LoadString(IDS_STATUS_DOWNLOAD_UPDATE); downloadUI.SetBannerText(strBanner); TCHAR szDownloaded[MAX_PATH]; HRESULT hr = ::URLDownloadToCacheFile( NULL, updateInformation.szUpdateFileURL, szDownloaded, sizeof(szDownloaded), 0, &CDownloadBindStatusCallback(&downloadUI)); if (!downloadUI.HasUserCanceled() && FAILED(hr)) { CString strDescr; strDescr.LoadString(IDS_UPDATER_ERROR_DOWNLOAD); CString strFormatted; strFormatted.FormatMessage( IDS_UPDATER_ERROR_FMT, (LPCTSTR)strDescr, hr); ::MessageBox( hWndParent, strFormatted, szUpdaterTitle, MB_OK | MB_ICONERROR); downloadUI.Terminate(); return FALSE; } strBanner.LoadString(IDS_STATUS_DOWNLOAD_UPDATE_DONE); downloadUI.SetBannerText(strBanner); downloadUI.Terminate(); STARTUPINFO si = {0}; si.cb = sizeof(STARTUPINFO); PROCESS_INFORMATION pi = {0}; ::CreateProcess( szDownloaded, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); return TRUE; }
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; }