extern "C" void DependencyUnregisterBundle( __in const BURN_REGISTRATION* pRegistration ) { HRESULT hr = S_OK; // Remove the bundle provider key. hr = DepUnregisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey); if (SUCCEEDED(hr)) { LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED, pRegistration->sczProviderKey); } else if (FAILED(hr) && E_FILENOTFOUND != hr) { LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_UNREGISTERED_FAILED, pRegistration->sczProviderKey, hr); } }
extern "C" HRESULT DependencyRegisterBundle( __in const BURN_REGISTRATION* pRegistration ) { HRESULT hr = S_OK; LPWSTR sczVersion = NULL; hr = FileVersionToStringEx(pRegistration->qwVersion, &sczVersion); ExitOnFailure(hr, "Failed to format the registration version string."); LogId(REPORT_VERBOSE, MSG_DEPENDENCY_BUNDLE_REGISTER, pRegistration->sczProviderKey, sczVersion); // Register the bundle provider key. hr = DepRegisterDependency(pRegistration->hkRoot, pRegistration->sczProviderKey, sczVersion, pRegistration->sczDisplayName, pRegistration->sczId, 0); ExitOnFailure(hr, "Failed to register the bundle dependency provider."); LExit: ReleaseStr(sczVersion); return hr; }
extern "C" HRESULT DependencyPlanPackageBegin( __in BOOL fPerMachine, __in BURN_PACKAGE* pPackage, __in BURN_PLAN* pPlan ) { HRESULT hr = S_OK; STRINGDICT_HANDLE sdIgnoredDependents = NULL; DEPENDENCY* rgDependents = NULL; UINT cDependents = 0; HKEY hkHive = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE; BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE; pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; // Make sure the package defines at least one provider. if (0 == pPackage->cDependencyProviders) { LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_SKIP_NOPROVIDERS, pPackage->sczId); ExitFunction1(hr = S_OK); } // Make sure the package is in the same scope as the bundle. if (fPerMachine != pPackage->fPerMachine) { LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_SKIP_WRONGSCOPE, pPackage->sczId, LoggingPerMachineToString(fPerMachine), LoggingPerMachineToString(pPackage->fPerMachine)); ExitFunction1(hr = S_OK); } // If we're uninstalling the package, check if any dependents are registered. if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) { // Build up a list of dependents to ignore, including the current bundle. hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents); ExitOnFailure(hr, "Failed to build the list of ignored dependents."); // Skip the dependency check if "ALL" was authored for IGNOREDEPENDENCIES. hr = DictKeyExists(sdIgnoredDependents, L"ALL"); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to check if \"ALL\" was set in IGNOREDEPENDENCIES."); } else { for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; hr = DepCheckDependents(hkHive, pProvider->sczKey, 0, sdIgnoredDependents, &rgDependents, &cDependents); if (E_FILENOTFOUND != hr) { ExitOnFailure1(hr, "Failed dependents check on package provider: %ls", pProvider->sczKey); } else { hr = S_OK; } } } } // Calculate the dependency actions before the package itself is planned. CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction); // If dependents were found, change the action to not uninstall the package. if (0 < cDependents) { LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId, cDependents); for (DWORD i = 0; i < cDependents; ++i) { const DEPENDENCY* pDependency = &rgDependents[i]; LogId(REPORT_VERBOSE, MSG_DEPENDENCY_PACKAGE_DEPENDENT, pDependency->sczKey, LoggingStringOrUnknownIfNull(pDependency->sczName)); } pPackage->fDependencyManagerWasHere = TRUE; pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; } else // use the calculated dependency actions as the provider actions if there are any non-imported providers { // that will need to be registered. BOOL fAllImportedProviders = TRUE; // assume all providers were imported. for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; if (!pProvider->fImported) { fAllImportedProviders = FALSE; break; } } if (!fAllImportedProviders) { pPackage->providerExecute = dependencyExecuteAction; pPackage->providerRollback = dependencyRollbackAction; } } // If the package will be removed, add its providers to the growing list in the plan. if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) { for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { const BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i]; hr = DepDependencyArrayAlloc(&pPlan->rgPlannedProviders, &pPlan->cPlannedProviders, pProvider->sczKey, NULL); ExitOnFailure1(hr, "Failed to add the package provider key \"%ls\" to the planned list.", pProvider->sczKey); } } pPackage->dependencyExecute = dependencyExecuteAction; pPackage->dependencyRollback = dependencyRollbackAction; LExit: ReleaseDependencyArray(rgDependents, cDependents); ReleaseDict(sdIgnoredDependents); return hr; }
extern "C" HRESULT MspEngineExecutePackage( __in_opt HWND hwndParent, __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_VARIABLES* pVariables, __in BOOL fRollback, __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; INSTALLUILEVEL uiLevel = pExecuteAction->mspTarget.pPackage->Msp.fDisplayInternalUI ? INSTALLUILEVEL_DEFAULT : static_cast<INSTALLUILEVEL>(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY); WIU_MSI_EXECUTE_CONTEXT context = { }; WIU_RESTART restart = WIU_RESTART_NONE; LPWSTR sczCachedDirectory = NULL; LPWSTR sczMspPath = NULL; LPWSTR sczPatches = NULL; LPWSTR sczProperties = NULL; LPWSTR sczObfuscatedProperties = NULL; // default to "verbose" logging DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; // get cached MSP paths for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) { LPCWSTR wzAppend = NULL; BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches."); if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action) { hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); // Best effort to set the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath); ExitOnFailure(hr, "Failed to build MSP path."); wzAppend = sczMspPath; } else // uninstall { wzAppend = pMspPackage->Msp.sczPatchCode; } if (NULL != sczPatches) { hr = StrAllocConcat(&sczPatches, L";", 0); ExitOnFailure(hr, "Failed to semi-colon delimit patches."); } hr = StrAllocConcat(&sczPatches, wzAppend, 0); ExitOnFailure(hr, "Failed to append patch."); } // Wire up the external UI handler and logging. hr = WiuInitializeExternalUI(pfnMessageHandler, uiLevel, hwndParent, pvContext, fRollback, &context); ExitOnFailure(hr, "Failed to initialize external UI handler."); //if (BURN_LOGGING_LEVEL_DEBUG == logLevel) //{ // dwLogMode | INSTALLLOGMODE_EXTRADEBUG; //} if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath) { hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, 0); ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath); } // set up properties hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE); ExitOnFailure(hr, "Failed to add properties to argument string."); hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); ExitOnFailure(hr, "Failed to add properties to obfuscated argument string."); LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode); // // Do the actual action. // switch (pExecuteAction->mspTarget.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_REPAIR: hr = StrAllocConcatSecure(&sczProperties, L" PATCH=\"", 0); ExitOnFailure(hr, "Failed to add PATCH property on install."); hr = StrAllocConcatSecure(&sczProperties, sczPatches, 0); ExitOnFailure(hr, "Failed to add patches to PATCH property on install."); hr = StrAllocConcatSecure(&sczProperties, L"\" REBOOT=ReallySuppress", 0); ExitOnFailure(hr, "Failed to add reboot suppression property on install."); hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart); ExitOnFailure(hr, "Failed to install MSP package."); break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall."); // Ignore all dependencies, since the Burn engine already performed the check. hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart); ExitOnFailure(hr, "Failed to uninstall MSP package."); break; } LExit: WiuUninitializeExternalUI(&context); ReleaseStr(sczCachedDirectory); ReleaseStr(sczMspPath); StrSecureZeroFreeString(sczProperties); ReleaseStr(sczObfuscatedProperties); ReleaseStr(sczPatches); switch (restart) { case WIU_RESTART_NONE: *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; break; case WIU_RESTART_REQUIRED: *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; break; case WIU_RESTART_INITIATED: *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; break; } // Best effort to clear the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); return hr; }
extern "C" HRESULT ExeEngineExecutePackage( __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_VARIABLES* pVariables, __in BOOL fRollback, __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; WCHAR wzCurrentDirectory[MAX_PATH] = { }; BOOL fChangedCurrentDirectory = FALSE; int nResult = IDNOACTION; LPCWSTR wzArguments = NULL; LPWSTR sczArgumentsFormatted = NULL; LPWSTR sczArgumentsObfuscated = NULL; LPWSTR sczCachedDirectory = NULL; LPWSTR sczExecutablePath = NULL; LPWSTR sczCommand = NULL; LPWSTR sczCommandObfuscated = NULL; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; DWORD dwExitCode = 0; GENERIC_EXECUTE_MESSAGE message = { }; // get cached executable path hr = CacheGetCompletedPath(pExecuteAction->exePackage.pPackage->fPerMachine, pExecuteAction->exePackage.pPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure1(hr, "Failed to get cached path for package: %ls", pExecuteAction->exePackage.pPackage->sczId); // Best effort to set the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); hr = PathConcat(sczCachedDirectory, pExecuteAction->exePackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczExecutablePath); ExitOnFailure(hr, "Failed to build executable path."); // pick arguments switch (pExecuteAction->exePackage.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczInstallArguments; break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczUninstallArguments; break; case BOOTSTRAPPER_ACTION_STATE_REPAIR: wzArguments = pExecuteAction->exePackage.pPackage->Exe.sczRepairArguments; break; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed to get action arguments for executable package."); } // build command if (wzArguments && *wzArguments) { hr = VariableFormatString(pVariables, wzArguments, &sczArgumentsFormatted, NULL); ExitOnFailure(hr, "Failed to format argument string."); hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", sczExecutablePath, sczArgumentsFormatted); ExitOnFailure(hr, "Failed to create executable command."); hr = VariableFormatStringObfuscated(pVariables, wzArguments, &sczArgumentsObfuscated, NULL); ExitOnFailure(hr, "Failed to format obfuscated argument string."); hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", sczExecutablePath, sczArgumentsObfuscated); } else { hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", sczExecutablePath); ExitOnFailure(hr, "Failed to create executable command."); hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", sczExecutablePath); } ExitOnFailure(hr, "Failed to create obfuscated executable command."); if (BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) { // Add the list of dependencies to ignore, if any, to the burn command line. if (pExecuteAction->exePackage.sczIgnoreDependencies && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) { hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the command line."); hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_IGNOREDEPENDENCIES, pExecuteAction->exePackage.sczIgnoreDependencies); ExitOnFailure(hr, "Failed to append the list of dependencies to ignore to the obfuscated command line."); } // Add the list of ancestors, if any, to the burn command line. if (pExecuteAction->exePackage.sczAncestors) { hr = StrAllocFormattedSecure(&sczCommand, L"%ls -%ls=%ls", sczCommand, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); ExitOnFailure(hr, "Failed to append the list of ancestors to the command line."); hr = StrAllocFormatted(&sczCommandObfuscated, L"%ls -%ls=%ls", sczCommandObfuscated, BURN_COMMANDLINE_SWITCH_ANCESTORS, pExecuteAction->exePackage.sczAncestors); ExitOnFailure(hr, "Failed to append the list of ancestors to the obfuscated command line."); } } // Log before we add the secret pipe name and client token for embedded processes. LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->exePackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->exePackage.action), sczExecutablePath, sczCommandObfuscated); if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_BURN == pExecuteAction->exePackage.pPackage->Exe.protocol) { hr = EmbeddedRunBundle(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); ExitOnFailure1(hr, "Failed to run bundle as embedded from path: %ls", sczExecutablePath); } else if (!pExecuteAction->exePackage.fFireAndForget && BURN_EXE_PROTOCOL_TYPE_NETFX4 == pExecuteAction->exePackage.pPackage->Exe.protocol) { hr = NetFxRunChainer(sczExecutablePath, sczCommand, pfnGenericMessageHandler, pvContext, &dwExitCode); ExitOnFailure1(hr, "Failed to run netfx chainer: %ls", sczExecutablePath); } else // create and wait for the executable process while sending fake progress to allow cancel. { // Make the cache location of the executable the current directory to help those executables // that expect stuff to be relative to them. if (::GetCurrentDirectoryW(countof(wzCurrentDirectory), wzCurrentDirectory)) { fChangedCurrentDirectory = ::SetCurrentDirectoryW(sczCachedDirectory); } si.cb = sizeof(si); // TODO: hookup the stdin/stdout/stderr pipes for logging purposes? if (!::CreateProcessW(sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", sczExecutablePath); } if (pExecuteAction->exePackage.fFireAndForget) { ::WaitForInputIdle(pi.hProcess, 5000); ExitFunction(); } do { message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; message.dwAllowedResults = MB_OKCANCEL; message.progress.dwPercentage = 50; nResult = pfnGenericMessageHandler(&message, pvContext); hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); ExitOnRootFailure(hr, "Bootstrapper application aborted during EXE progress."); hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) { ExitOnFailure1(hr, "Failed to wait for executable to complete: %ls", sczExecutablePath); } } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); } hr = HandleExitCode(pExecuteAction->exePackage.pPackage, dwExitCode, pRestart); ExitOnRootFailure1(hr, "Process returned error: 0x%x", dwExitCode); LExit: if (fChangedCurrentDirectory) { ::SetCurrentDirectoryW(wzCurrentDirectory); } StrSecureZeroFreeString(sczArgumentsFormatted); ReleaseStr(sczArgumentsObfuscated); ReleaseStr(sczCachedDirectory); ReleaseStr(sczExecutablePath); StrSecureZeroFreeString(sczCommand); ReleaseStr(sczCommandObfuscated); ReleaseHandle(pi.hThread); ReleaseHandle(pi.hProcess); // Best effort to clear the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); return hr; }
extern "C" HRESULT EngineRun( __in HINSTANCE hInstance, __in_z_opt LPCWSTR wzCommandLine, __in int nCmdShow, __out DWORD* pdwExitCode ) { HRESULT hr = S_OK; BOOL fComInitialized = FALSE; BOOL fLogInitialized = FALSE; BOOL fRegInitialized = FALSE; BOOL fWiuInitialized = FALSE; BOOL fXmlInitialized = FALSE; OSVERSIONINFOEXW ovix = { }; LPWSTR sczExePath = NULL; BOOL fRunNormal = FALSE; BOOL fRestart = FALSE; BURN_ENGINE_STATE engineState = { }; hr = InitializeEngineState(&engineState); ExitOnFailure(hr, "Failed to initialize engine state."); engineState.command.nCmdShow = nCmdShow; // Ensure that log contains approriate level of information #ifdef _DEBUG LogSetLevel(REPORT_DEBUG, FALSE); #else LogSetLevel(REPORT_VERBOSE, FALSE); // FALSE means don't write an additional text line to the log saying the level changed #endif // initialize platform layer PlatformInitialize(); // initialize COM hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED); ExitOnFailure(hr, "Failed to initialize COM."); fComInitialized = TRUE; // Initialize dutil. LogInitialize(::GetModuleHandleW(NULL)); fLogInitialized = TRUE; hr = RegInitialize(); ExitOnFailure(hr, "Failed to initialize Regutil."); fRegInitialized = TRUE; hr = WiuInitialize(); ExitOnFailure(hr, "Failed to initialize Wiutil."); fWiuInitialized = TRUE; hr = XmlInitialize(); ExitOnFailure(hr, "Failed to initialize XML util."); fXmlInitialized = TRUE; ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); if (!::GetVersionExW((LPOSVERSIONINFOW)&ovix)) { ExitWithLastError(hr, "Failed to get OS info."); } PathForCurrentProcess(&sczExePath, NULL); // Ignore failure. LogId(REPORT_STANDARD, MSG_BURN_INFO, szVerMajorMinorBuild, ovix.dwMajorVersion, ovix.dwMinorVersion, ovix.dwBuildNumber, ovix.wServicePackMajor, sczExePath, wzCommandLine ? wzCommandLine : L""); ReleaseNullStr(sczExePath); // initialize core hr = CoreInitialize(wzCommandLine, &engineState); ExitOnFailure(hr, "Failed to initialize core."); // select run mode switch (engineState.mode) { case BURN_MODE_NORMAL: fRunNormal = TRUE; hr = RunNormal(hInstance, &engineState); ExitOnFailure(hr, "Failed to run per-user mode."); break; case BURN_MODE_ELEVATED: hr = RunElevated(hInstance, wzCommandLine, &engineState); ExitOnFailure(hr, "Failed to run per-machine mode."); break; case BURN_MODE_EMBEDDED: fRunNormal = TRUE; hr = RunEmbedded(hInstance, &engineState); ExitOnFailure(hr, "Failed to run embedded mode."); break; case BURN_MODE_RUNONCE: hr = RunRunOnce(wzCommandLine, nCmdShow); ExitOnFailure(hr, "Failed to run RunOnce mode."); break; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "Invalid run mode."); } // set exit code and remember if we are supposed to restart. *pdwExitCode = engineState.userExperience.dwExitCode; fRestart = engineState.fRestart; LExit: ReleaseStr(sczExePath); // If anything went wrong but the log was never open, try to open a "failure" log // and that will dump anything captured in the log memory buffer to the log. if (FAILED(hr) && BURN_LOGGING_STATE_CLOSED == engineState.log.state) { LogOpen(NULL, L"Setup", L"_Failed", L"txt", FALSE, FALSE, NULL); } UserExperienceRemove(&engineState.userExperience); CacheRemoveWorkingFolder(engineState.registration.sczId); // If this is a related bundle (but not an update) suppress restart and return the standard restart error code. if (fRestart && BOOTSTRAPPER_RELATION_NONE != engineState.command.relationType && BOOTSTRAPPER_RELATION_UPDATE != engineState.command.relationType) { LogId(REPORT_STANDARD, MSG_RESTART_ABORTED, LoggingRelationTypeToString(engineState.command.relationType)); fRestart = FALSE; hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED); } UninitializeEngineState(&engineState); if (fXmlInitialized) { XmlUninitialize(); } if (fWiuInitialized) { WiuUninitialize(); } if (fRegInitialized) { RegUninitialize(); } if (fComInitialized) { ::CoUninitialize(); } if (fRunNormal) { LogId(REPORT_STANDARD, MSG_EXITING, FAILED(hr) ? (int)hr : *pdwExitCode, LoggingBoolToString(fRestart)); if (fRestart) { LogId(REPORT_STANDARD, MSG_RESTARTING); } } if (fLogInitialized) { LogClose(FALSE); } if (fRestart) { Restart(); } if (fLogInitialized) { LogUninitialize(FALSE); } return hr; }
extern "C" HRESULT MspEngineDetectInitialize( __in BURN_PACKAGES* pPackages ) { AssertSz(pPackages->cPatchInfo, "MspEngineDetectInitialize() should only be called if there are MSP packages."); HRESULT hr = S_OK; POSSIBLE_TARGETPRODUCT* rgPossibleTargetProducts = NULL; DWORD cPossibleTargetProducts = 0; #ifdef DEBUG // All patch info should be initialized to zero. for (DWORD i = 0; i < pPackages->cPatchInfo; ++i) { BURN_PACKAGE* pPackage = pPackages->rgPatchInfoToPackage[i]; Assert(!pPackage->Msp.cTargetProductCodes); Assert(!pPackage->Msp.rgTargetProducts); } #endif // Figure out which product codes to target on the machine. In the worst case all products on the machine // will be returned. hr = GetPossibleTargetProductCodes(pPackages, &rgPossibleTargetProducts, &cPossibleTargetProducts); ExitOnFailure(hr, "Failed to get possible target product codes."); // Loop through possible target products, testing the collective patch applicability against each product in // the appropriate context. Store the result with the appropriate patch package. for (DWORD iSearch = 0; iSearch < cPossibleTargetProducts; ++iSearch) { const POSSIBLE_TARGETPRODUCT* pPossibleTargetProduct = rgPossibleTargetProducts + iSearch; LogId(REPORT_STANDARD, MSG_DETECT_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context)); if (pPossibleTargetProduct->pszLocalPackage) { // Ignores current machine state to determine just patch applicability. // Superseded and obsolesced patches will be planned separately. hr = WiuDetermineApplicablePatches(pPossibleTargetProduct->pszLocalPackage, pPackages->rgPatchInfo, pPackages->cPatchInfo); } else { hr = WiuDeterminePatchSequence(pPossibleTargetProduct->wzProductCode, NULL, pPossibleTargetProduct->context, pPackages->rgPatchInfo, pPackages->cPatchInfo); } if (SUCCEEDED(hr)) { for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) { if (ERROR_SUCCESS == pPackages->rgPatchInfo[iPatchInfo].uStatus) { BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo]; Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type); // Note that we do add superseded and obsolete MSP packages. Package Detect and Plan will sort them out later. hr = AddDetectedTargetProduct(pPackages, pMspPackage, pPackages->rgPatchInfo[iPatchInfo].dwOrder, pPossibleTargetProduct->wzProductCode, pPossibleTargetProduct->context); ExitOnFailure(hr, "Failed to add target product code to package: %ls", pMspPackage->sczId); } // TODO: should we log something for this error case? } } else { LogId(REPORT_STANDARD, MSG_DETECT_FAILED_CALCULATE_PATCH_APPLICABILITY, pPossibleTargetProduct->wzProductCode, LoggingMsiInstallContext(pPossibleTargetProduct->context), hr); } hr = S_OK; // always reset so we test all possible target products. } LExit: if (rgPossibleTargetProducts) { for (DWORD i = 0; i < cPossibleTargetProducts; ++i) { ReleaseStr(rgPossibleTargetProducts[i].pszLocalPackage); } MemFree(rgPossibleTargetProducts); } return hr; }
static HRESULT RunNormal( __in HINSTANCE hInstance, __in BURN_ENGINE_STATE* pEngineState ) { HRESULT hr = S_OK; HANDLE hPipesCreatedEvent = NULL; BOOL fContinueExecution = TRUE; BOOL fReloadApp = FALSE; // Initialize logging. hr = LoggingOpen(&pEngineState->log, &pEngineState->variables, pEngineState->command.display, pEngineState->registration.sczDisplayName); ExitOnFailure(hr, "Failed to open log."); // Ensure the cache functions are initialized since we might use them soon. hr = CacheInitialize(&pEngineState->registration, &pEngineState->variables); ExitOnFailure(hr, "Failed to initialize internal cache functionality."); // When launched explicitly unelevated, create the pipes so the elevated process can connect. if (BURN_ELEVATION_STATE_UNELEVATED_EXPLICITLY == pEngineState->elevationState) { Assert(pEngineState->companionConnection.dwProcessId); Assert(pEngineState->companionConnection.sczName); Assert(pEngineState->companionConnection.sczSecret); Assert(!pEngineState->companionConnection.hProcess); Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hPipe); Assert(INVALID_HANDLE_VALUE == pEngineState->companionConnection.hCachePipe); hr = PipeCreatePipes(&pEngineState->companionConnection, TRUE, &hPipesCreatedEvent); ExitOnFailure(hr, "Failed to create pipes to connect to elevated parent process."); hr = PipeWaitForChildConnect(&pEngineState->companionConnection); ExitOnFailure(hr, "Failed to connect to elevated parent process."); ReleaseHandle(hPipesCreatedEvent); } // Ensure we're on a supported operating system. hr = ConditionGlobalCheck(&pEngineState->variables, &pEngineState->condition, pEngineState->command.display, pEngineState->registration.sczDisplayName, &pEngineState->userExperience.dwExitCode, &fContinueExecution); ExitOnFailure(hr, "Failed to check global conditions"); if (!fContinueExecution) { LogId(REPORT_STANDARD, MSG_FAILED_CONDITION_CHECK); // If the block told us to abort, abort! ExitFunction1(hr = S_OK); } if (pEngineState->userExperience.fSplashScreen && BOOTSTRAPPER_DISPLAY_NONE < pEngineState->command.display) { SplashScreenCreate(hInstance, NULL, &pEngineState->command.hwndSplashScreen); } // Create a top-level window to handle system messages. hr = UiCreateMessageWindow(hInstance, pEngineState); ExitOnFailure(hr, "Failed to create the message window."); // Query registration state. hr = CoreQueryRegistration(pEngineState); ExitOnFailure(hr, "Failed to query registration."); // Set some built-in variables before loading the BA. hr = PlanSetVariables(pEngineState->command.action, &pEngineState->variables); ExitOnFailure(hr, "Failed to set action variables."); hr = RegistrationSetVariables(&pEngineState->registration, &pEngineState->variables); ExitOnFailure(hr, "Failed to set registration variables."); // If a layout directory was specified on the command-line, set it as a well-known variable. if (pEngineState->command.wzLayoutDirectory && *pEngineState->command.wzLayoutDirectory) { hr = VariableSetString(&pEngineState->variables, BURN_BUNDLE_LAYOUT_DIRECTORY, pEngineState->command.wzLayoutDirectory, FALSE); ExitOnFailure(hr, "Failed to set layout directory variable to value provided from command-line."); } do { fReloadApp = FALSE; hr = RunApplication(pEngineState, &fReloadApp); ExitOnFailure(hr, "Failed while running "); } while (fReloadApp); LExit: // If the message window is still around, close it. UiCloseMessageWindow(pEngineState); VariablesDump(&pEngineState->variables); // end per-machine process if running if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe) { PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, (BURN_ELEVATION_STATE_UNELEVATED_EXPLICITLY == pEngineState->elevationState) ? pEngineState->fRestart : FALSE); } // If the splash screen is still around, close it. if (::IsWindow(pEngineState->command.hwndSplashScreen)) { ::PostMessageW(pEngineState->command.hwndSplashScreen, WM_CLOSE, 0, 0); } ReleaseHandle(hPipesCreatedEvent); return hr; }
static HRESULT RunApplication( __in BURN_ENGINE_STATE* pEngineState, __out BOOL* pfReloadApp ) { HRESULT hr = S_OK; DWORD dwThreadId = 0; IBootstrapperEngine* pEngineForApplication = NULL; BOOL fStartupCalled = FALSE; BOOL fRet = FALSE; MSG msg = { }; ::PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); dwThreadId = ::GetCurrentThreadId(); // Load the bootstrapper application. hr = EngineForApplicationCreate(pEngineState, dwThreadId, &pEngineForApplication); ExitOnFailure(hr, "Failed to create engine for UX."); hr = UserExperienceLoad(&pEngineState->userExperience, pEngineForApplication, &pEngineState->command); ExitOnFailure(hr, "Failed to load UX."); fStartupCalled = TRUE; hr = pEngineState->userExperience.pUserExperience->OnStartup(); ExitOnFailure(hr, "Failed to start bootstrapper application."); // Enter the message pump. while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) { if (-1 == fRet) { hr = E_UNEXPECTED; ExitOnRootFailure(hr, "Unexpected return value from message pump."); } else { ProcessMessage(pEngineState, &msg); } } // get exit code pEngineState->userExperience.dwExitCode = (DWORD)msg.wParam; LExit: if (fStartupCalled) { int nResult = pEngineState->userExperience.pUserExperience->OnShutdown(); if (IDRESTART == nResult) { LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RESTART, LoggingBoolToString(pEngineState->fRestart)); pEngineState->fRestart = TRUE; } else if (IDRELOAD_BOOTSTRAPPER == nResult) { LogId(REPORT_STANDARD, MSG_BA_REQUESTED_RELOAD); *pfReloadApp = TRUE; } } // unload UX UserExperienceUnload(&pEngineState->userExperience); ReleaseObject(pEngineForApplication); return hr; }
extern "C" HRESULT MsuEngineExecutePackage( __in BURN_EXECUTE_ACTION* pExecuteAction, __in BOOL fRollback, __in PFN_GENERICMESSAGEHANDLER pfnGenericMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; int nResult = IDNOACTION; LPWSTR sczCachedDirectory = NULL; LPWSTR sczMsuPath = NULL; LPWSTR sczSystemPath = NULL; LPWSTR sczWusaPath = NULL; LPWSTR sczCommand = NULL; SC_HANDLE schWu = NULL; BOOL fWuWasDisabled = FALSE; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; GENERIC_EXECUTE_MESSAGE message = { }; DWORD dwExitCode = 0; BOOL fDoDependency = FALSE; // get wusa.exe path hr = PathGetKnownFolder(CSIDL_SYSTEM, &sczSystemPath); ExitOnFailure(hr, "Failed to find System32 directory."); hr = PathConcat(sczSystemPath, L"wusa.exe", &sczWusaPath); ExitOnFailure(hr, "Failed to allocate WUSA.exe path."); // build command switch (pExecuteAction->msuPackage.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: // get cached executable path hr = CacheGetCompletedPath(TRUE, pExecuteAction->msuPackage.pPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure1(hr, "Failed to get cached path for package: %ls", pExecuteAction->msuPackage.pPackage->sczId); hr = PathConcat(sczCachedDirectory, pExecuteAction->msuPackage.pPackage->rgPayloads[0].pPayload->sczFilePath, &sczMsuPath); ExitOnFailure(hr, "Failed to build executable path."); // format command hr = StrAllocFormatted(&sczCommand, L"\"%ls\" \"%ls\" /quiet /norestart", sczWusaPath, sczMsuPath); ExitOnFailure(hr, "Failed to format MSU install command."); break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: // format command hr = StrAllocFormatted(&sczCommand, L"\"%ls\" /uninstall /kb:%ls /quiet /norestart", sczWusaPath, pExecuteAction->msuPackage.pPackage->Msu.sczKB); ExitOnFailure(hr, "Failed to format MSU uninstall command."); break; default: hr = E_UNEXPECTED; ExitOnFailure(hr, "Failed to get action arguments for MSU package."); } if (pExecuteAction->msuPackage.sczLogPath && *pExecuteAction->msuPackage.sczLogPath) { hr = StrAllocConcat(&sczCommand, L" /log:", 0); ExitOnFailure(hr, "Failed to append log switch to MSU command-line."); hr = StrAllocConcat(&sczCommand, pExecuteAction->msuPackage.sczLogPath, 0); ExitOnFailure(hr, "Failed to append log path to MSU command-line."); } LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pExecuteAction->msuPackage.pPackage->sczId, LoggingActionStateToString(pExecuteAction->msuPackage.action), sczMsuPath ? sczMsuPath : pExecuteAction->msuPackage.pPackage->Msu.sczKB, sczCommand); hr = EnsureWUServiceEnabled(&schWu, &fWuWasDisabled); ExitOnFailure(hr, "Failed to ensure WU service was enabled to install MSU package."); // create process si.cb = sizeof(si); if (!::CreateProcessW(sczWusaPath, sczCommand, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", sczWusaPath); } do { message.type = GENERIC_EXECUTE_MESSAGE_PROGRESS; message.dwAllowedResults = MB_OKCANCEL; message.progress.dwPercentage = 50; nResult = pfnGenericMessageHandler(&message, pvContext); hr = (IDOK == nResult || IDNOACTION == nResult) ? S_OK : IDCANCEL == nResult ? HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) : HRESULT_FROM_WIN32(ERROR_INSTALL_FAILURE); ExitOnRootFailure(hr, "Bootstrapper application aborted during MSU progress."); // wait for process to terminate hr = ProcWaitForCompletion(pi.hProcess, 500, &dwExitCode); if (HRESULT_FROM_WIN32(WAIT_TIMEOUT) != hr) { ExitOnFailure1(hr, "Failed to wait for executable to complete: %ls", sczWusaPath); } } while (HRESULT_FROM_WIN32(WAIT_TIMEOUT) == hr); // get process exit code if (!::GetExitCodeProcess(pi.hProcess, &dwExitCode)) { ExitWithLastError(hr, "Failed to get process exit code."); } // We'll normalize the restart required error code from wusa.exe just in case. Most likely // that on reboot we'll actually get WU_S_REBOOT_REQUIRED. if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == static_cast<HRESULT>(dwExitCode)) { dwExitCode = ERROR_SUCCESS_REBOOT_REQUIRED; } // handle exit code switch (dwExitCode) { case S_OK: __fallthrough; case WU_S_ALREADY_INSTALLED: fDoDependency = TRUE; __fallthrough; case S_FALSE: __fallthrough; case WU_E_NOT_APPLICABLE: *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; hr = S_OK; break; case ERROR_SUCCESS_REBOOT_REQUIRED: __fallthrough; case WU_S_REBOOT_REQUIRED: fDoDependency = TRUE; *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; hr = S_OK; break; default: hr = E_UNEXPECTED; break; } if (fDoDependency) { if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msuPackage.action) { hr = DependencyRegisterPackage(pExecuteAction->msuPackage.pPackage); ExitOnFailure(hr, "Failed to register the package dependency providers."); } else if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msuPackage.action) { hr = DependencyUnregisterPackage(pExecuteAction->msuPackage.pPackage); ExitOnFailure(hr, "Failed to unregister the package dependency providers."); } } LExit: ReleaseStr(sczCachedDirectory); ReleaseStr(sczMsuPath); ReleaseStr(sczSystemPath); ReleaseStr(sczWusaPath); ReleaseStr(sczCommand); ReleaseHandle(pi.hProcess); ReleaseHandle(pi.hThread); if (fWuWasDisabled) { SetServiceStartType(schWu, SERVICE_DISABLED); } return hr; }
extern "C" HRESULT ApprovedExesLaunch( __in BURN_VARIABLES* pVariables, __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe, __out DWORD* pdwProcessId ) { HRESULT hr = S_OK; LPWSTR sczArgumentsFormatted = NULL; LPWSTR sczArgumentsObfuscated = NULL; LPWSTR sczCommand = NULL; LPWSTR sczCommandObfuscated = NULL; LPWSTR sczExecutableDirectory = NULL; STARTUPINFOW si = { }; PROCESS_INFORMATION pi = { }; // build command if (pLaunchApprovedExe->sczArguments && *pLaunchApprovedExe->sczArguments) { hr = VariableFormatString(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsFormatted, NULL); ExitOnFailure(hr, "Failed to format argument string."); hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsFormatted); ExitOnFailure(hr, "Failed to create executable command."); hr = VariableFormatStringObfuscated(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsObfuscated, NULL); ExitOnFailure(hr, "Failed to format obfuscated argument string."); hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsObfuscated); } else { hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath); ExitOnFailure(hr, "Failed to create executable command."); hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath); } ExitOnFailure(hr, "Failed to create obfuscated executable command."); // Try to get the directory of the executable so we can set the current directory of the process to help those executables // that expect stuff to be relative to them. Best effort only. hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory); if (FAILED(hr)) { ReleaseNullStr(sczExecutableDirectory); } LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated); si.cb = sizeof(si); if (!::CreateProcessW(pLaunchApprovedExe->sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, sczExecutableDirectory, &si, &pi)) { ExitWithLastError1(hr, "Failed to CreateProcess on path: %ls", pLaunchApprovedExe->sczExecutablePath); } *pdwProcessId = pi.dwProcessId; if (pLaunchApprovedExe->dwWaitForInputIdleTimeout) { ::WaitForInputIdle(pi.hProcess, pLaunchApprovedExe->dwWaitForInputIdleTimeout); } LExit: StrSecureZeroFreeString(sczArgumentsFormatted); ReleaseStr(sczArgumentsObfuscated); StrSecureZeroFreeString(sczCommand); ReleaseStr(sczCommandObfuscated); ReleaseStr(sczExecutableDirectory); ReleaseHandle(pi.hThread); ReleaseHandle(pi.hProcess); return hr; }
extern "C" HRESULT DetectForwardCompatibleBundle( __in BURN_USER_EXPERIENCE* pUX, __in BOOTSTRAPPER_COMMAND* pCommand, __in BURN_REGISTRATION* pRegistration ) { HRESULT hr = S_OK; int nRecommendation = IDNOACTION; if (pRegistration->sczDetectedProviderKeyBundleId && CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1)) { // Only change the recommendation if an parent was provided. if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent) { // On install, recommend running the forward compatible bundle because there is an active parent. This // will essentially register the parent with the forward compatible bundle. if (BOOTSTRAPPER_ACTION_INSTALL == pCommand->action) { nRecommendation = IDOK; } else if (BOOTSTRAPPER_ACTION_UNINSTALL == pCommand->action || BOOTSTRAPPER_ACTION_MODIFY == pCommand->action || BOOTSTRAPPER_ACTION_REPAIR == pCommand->action) { // When modifying the bundle, only recommend running the forward compatible bundle if the parent // is already registered as a dependent of the provider key. if (DependencyDependentExists(pRegistration, pRegistration->sczActiveParent)) { nRecommendation = IDOK; } } } for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) { BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType && pRegistration->qwVersion <= pRelatedBundle->qwVersion && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1)) { int nResult = pUX->pUserExperience->OnDetectForwardCompatibleBundle(pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, nRecommendation); hr = UserExperienceInterpretResult(pUX, MB_OKCANCEL, nResult); ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle."); if (IDOK == nResult) { hr = PseudoBundleInitializePassthrough(&pRegistration->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package); ExitOnFailure(hr, "Failed to initialize update bundle."); pRegistration->fEnabledForwardCompatibleBundle = TRUE; } LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingBoolToString(pRegistration->fEnabledForwardCompatibleBundle)); break; } } } LExit: return hr; }
extern "C" HRESULT DetectReportRelatedBundles( __in BURN_USER_EXPERIENCE* pUX, __in BURN_REGISTRATION* pRegistration, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in BOOTSTRAPPER_ACTION action ) { HRESULT hr = S_OK; for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) { const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE; switch (pRelatedBundle->relationType) { case BOOTSTRAPPER_RELATION_UPGRADE: if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action) { if (pRegistration->qwVersion > pRelatedBundle->qwVersion) { operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; } else if (pRegistration->qwVersion < pRelatedBundle->qwVersion) { operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; } } break; case BOOTSTRAPPER_RELATION_PATCH: __fallthrough; case BOOTSTRAPPER_RELATION_ADDON: if (BOOTSTRAPPER_ACTION_UNINSTALL == action) { operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE; } else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action) { operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL; } else if (BOOTSTRAPPER_ACTION_REPAIR == action) { operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR; } break; case BOOTSTRAPPER_RELATION_DETECT: __fallthrough; case BOOTSTRAPPER_RELATION_DEPENDENT: break; default: hr = E_FAIL; ExitOnRootFailure1(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType); break; } LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingRelatedOperationToString(operation)); int nResult = pUX->pUserExperience->OnDetectRelatedBundle(pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, operation); hr = UserExperienceInterpretResult(pUX, MB_OKCANCEL, nResult); ExitOnRootFailure(hr, "BA aborted detect related bundle."); } LExit: return hr; }
extern "C" HRESULT MspEngineDetectInitialize( __in BURN_PACKAGES* pPackages ) { AssertSz(pPackages->cPatchInfo, "MspEngineDetectInitialize() should only be called if there are MSP packages."); static MSIINSTALLCONTEXT rgContexts[] = { MSIINSTALLCONTEXT_USERUNMANAGED, MSIINSTALLCONTEXT_USERMANAGED, MSIINSTALLCONTEXT_MACHINE }; HRESULT hr = S_OK; DWORD iProduct = 0; WCHAR wzProductCode[MAX_GUID_CHARS + 1]; #ifdef DEBUG // All patch info should be initialized to zero. for (DWORD i = 0; i < pPackages->cPatchInfo; ++i) { BURN_PACKAGE* pPackage = pPackages->rgPatchInfoToPackage[i]; Assert(!pPackage->Msp.cTargetProductCodes); Assert(!pPackage->Msp.rgTargetProducts); } #endif // Loop through all products on the machine, testing the collective patch applicability // against each product in all contexts. Store the result with the appropriate patch package. do { hr = WiuEnumProducts(iProduct, wzProductCode); if (SUCCEEDED(hr)) { for (DWORD i = 0; i < countof(rgContexts); ++i) { hr = WiuDeterminePatchSequence(wzProductCode, NULL, rgContexts[i], pPackages->rgPatchInfo, pPackages->cPatchInfo); if (SUCCEEDED(hr)) { for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) { if (ERROR_SUCCESS == pPackages->rgPatchInfo[iPatchInfo].uStatus) { BURN_PACKAGE* pMspPackage = pPackages->rgPatchInfoToPackage[iPatchInfo]; Assert(BURN_PACKAGE_TYPE_MSP == pMspPackage->type); // Note that we do add superseded and obsolete MSP packages. Package Detect and Plan will sort them out later. hr = AddDetectedTargetProduct(pPackages, pMspPackage, rgContexts[i], pPackages->rgPatchInfo[iPatchInfo].dwOrder, wzProductCode); ExitOnFailure1(hr, "Failed to add target product code to package: %ls", pMspPackage->sczId); } // TODO: should we log something for this error case? } } // TODO: should we log something for this error case? } hr = S_OK; // always reset so we test all possible target products. } else if (E_BADCONFIGURATION == hr) { // Skip this product and continue. LogId(REPORT_STANDARD, MSG_DETECT_BAD_PRODUCT_CONFIGURATION, wzProductCode); hr = S_OK; } ++iProduct; } while (SUCCEEDED(hr)); if (E_NOMOREITEMS == hr) { hr = S_OK; } ExitOnFailure(hr, "Failed to test patches applicability against all products on the machine."); LExit: return hr; }