// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. #include "precomp.h" // constants // structs // internal function declarations static HRESULT ParseRelatedMsiFromXml( __in IXMLDOMNode* pixnRelatedMsi, __in BURN_RELATED_MSI* pRelatedMsi ); static HRESULT EvaluateActionStateConditions( __in BURN_VARIABLES* pVariables, __in_z_opt LPCWSTR sczAddLocalCondition, __in_z_opt LPCWSTR sczAddSourceCondition, __in_z_opt LPCWSTR sczAdvertiseCondition, __out BOOTSTRAPPER_FEATURE_STATE* pState ); static HRESULT CalculateFeatureAction( __in BOOTSTRAPPER_FEATURE_STATE currentState, __in BOOTSTRAPPER_FEATURE_STATE requestedState, __in BOOL fRepair, __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction, __inout BOOL* pfDelta ); static HRESULT EscapePropertyArgumentString( __in LPCWSTR wzProperty, __inout_z LPWSTR* psczEscapedValue, __in BOOL fZeroOnRealloc ); static HRESULT ConcatFeatureActionProperties( __in BURN_PACKAGE* pPackage, __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions, __inout_z LPWSTR* psczArguments ); static HRESULT ConcatPatchProperty( __in BURN_CACHE* pCache, __in BURN_PACKAGE* pPackage, __in BOOL fRollback, __inout_z LPWSTR* psczArguments ); static void RegisterSourceDirectory( __in BURN_PACKAGE* pPackage, __in_z LPCWSTR wzCacheDirectory ); // function definitions extern "C" HRESULT MsiEngineParsePackageFromXml( __in IXMLDOMNode* pixnMsiPackage, __in BURN_PACKAGE* pPackage ) { HRESULT hr = S_OK; IXMLDOMNodeList* pixnNodes = NULL; IXMLDOMNode* pixnNode = NULL; DWORD cNodes = 0; LPWSTR scz = NULL; // @ProductCode hr = XmlGetAttributeEx(pixnMsiPackage, L"ProductCode", &pPackage->Msi.sczProductCode); ExitOnFailure(hr, "Failed to get @ProductCode."); // @Language hr = XmlGetAttributeNumber(pixnMsiPackage, L"Language", &pPackage->Msi.dwLanguage); ExitOnFailure(hr, "Failed to get @Language."); // @Version hr = XmlGetAttributeEx(pixnMsiPackage, L"Version", &scz); ExitOnFailure(hr, "Failed to get @Version."); hr = VerParseVersion(scz, 0, FALSE, &pPackage->Msi.pVersion); ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); if (pPackage->Msi.pVersion->fInvalid) { LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); } // @UpgradeCode hr = XmlGetAttributeEx(pixnMsiPackage, L"UpgradeCode", &pPackage->Msi.sczUpgradeCode); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @UpgradeCode."); } // select feature nodes hr = XmlSelectNodes(pixnMsiPackage, L"MsiFeature", &pixnNodes); ExitOnFailure(hr, "Failed to select feature nodes."); // get feature node count hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get feature node count."); if (cNodes) { // allocate memory for features pPackage->Msi.rgFeatures = (BURN_MSIFEATURE*)MemAlloc(sizeof(BURN_MSIFEATURE) * cNodes, TRUE); ExitOnNull(pPackage->Msi.rgFeatures, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI feature structs."); pPackage->Msi.cFeatures = cNodes; // parse feature elements for (DWORD i = 0; i < cNodes; ++i) { BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; hr = XmlNextElement(pixnNodes, &pixnNode, NULL); ExitOnFailure(hr, "Failed to get next node."); // @Id hr = XmlGetAttributeEx(pixnNode, L"Id", &pFeature->sczId); ExitOnFailure(hr, "Failed to get @Id."); // @AddLocalCondition hr = XmlGetAttributeEx(pixnNode, L"AddLocalCondition", &pFeature->sczAddLocalCondition); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @AddLocalCondition."); } // @AddSourceCondition hr = XmlGetAttributeEx(pixnNode, L"AddSourceCondition", &pFeature->sczAddSourceCondition); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @AddSourceCondition."); } // @AdvertiseCondition hr = XmlGetAttributeEx(pixnNode, L"AdvertiseCondition", &pFeature->sczAdvertiseCondition); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @AdvertiseCondition."); } // @RollbackAddLocalCondition hr = XmlGetAttributeEx(pixnNode, L"RollbackAddLocalCondition", &pFeature->sczRollbackAddLocalCondition); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @RollbackAddLocalCondition."); } // @RollbackAddSourceCondition hr = XmlGetAttributeEx(pixnNode, L"RollbackAddSourceCondition", &pFeature->sczRollbackAddSourceCondition); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @RollbackAddSourceCondition."); } // @RollbackAdvertiseCondition hr = XmlGetAttributeEx(pixnNode, L"RollbackAdvertiseCondition", &pFeature->sczRollbackAdvertiseCondition); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @RollbackAdvertiseCondition."); } // prepare next iteration ReleaseNullObject(pixnNode); } } ReleaseNullObject(pixnNodes); // done with the MsiFeature elements. hr = MsiEngineParsePropertiesFromXml(pixnMsiPackage, &pPackage->Msi.rgProperties, &pPackage->Msi.cProperties); ExitOnFailure(hr, "Failed to parse properties from XML."); // select related MSI nodes hr = XmlSelectNodes(pixnMsiPackage, L"RelatedPackage", &pixnNodes); ExitOnFailure(hr, "Failed to select related MSI nodes."); // get related MSI node count hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get related MSI node count."); if (cNodes) { // allocate memory for related MSIs pPackage->Msi.rgRelatedMsis = (BURN_RELATED_MSI*)MemAlloc(sizeof(BURN_RELATED_MSI) * cNodes, TRUE); ExitOnNull(pPackage->Msi.rgRelatedMsis, hr, E_OUTOFMEMORY, "Failed to allocate memory for related MSI structs."); pPackage->Msi.cRelatedMsis = cNodes; // parse related MSI elements for (DWORD i = 0; i < cNodes; ++i) { hr = XmlNextElement(pixnNodes, &pixnNode, NULL); ExitOnFailure(hr, "Failed to get next node."); // parse related MSI element hr = ParseRelatedMsiFromXml(pixnNode, &pPackage->Msi.rgRelatedMsis[i]); ExitOnFailure(hr, "Failed to parse related MSI element."); // prepare next iteration ReleaseNullObject(pixnNode); } } ReleaseNullObject(pixnNodes); // done with the RelatedPackage elements. // Select slipstream MSP nodes. hr = XmlSelectNodes(pixnMsiPackage, L"SlipstreamMsp", &pixnNodes); ExitOnFailure(hr, "Failed to select related MSI nodes."); hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get related MSI node count."); if (cNodes) { pPackage->Msi.rgSlipstreamMsps = reinterpret_cast(MemAlloc(sizeof(BURN_SLIPSTREAM_MSP) * cNodes, TRUE)); ExitOnNull(pPackage->Msi.rgSlipstreamMsps, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP packages."); pPackage->Msi.rgsczSlipstreamMspPackageIds = reinterpret_cast(MemAlloc(sizeof(LPWSTR*) * cNodes, TRUE)); ExitOnNull(pPackage->Msi.rgsczSlipstreamMspPackageIds, hr, E_OUTOFMEMORY, "Failed to allocate memory for slipstream MSP ids."); pPackage->Msi.cSlipstreamMspPackages = cNodes; // Parse slipstream MSP Ids. for (DWORD i = 0; i < cNodes; ++i) { hr = XmlNextElement(pixnNodes, &pixnNode, NULL); ExitOnFailure(hr, "Failed to get next slipstream MSP node."); hr = XmlGetAttributeEx(pixnNode, L"Id", pPackage->Msi.rgsczSlipstreamMspPackageIds + i); ExitOnFailure(hr, "Failed to parse slipstream MSP ids."); ReleaseNullObject(pixnNode); } } hr = S_OK; LExit: ReleaseObject(pixnNodes); ReleaseObject(pixnNode); ReleaseStr(scz); return hr; } extern "C" HRESULT MsiEngineParsePropertiesFromXml( __in IXMLDOMNode* pixnPackage, __out BURN_MSIPROPERTY** prgProperties, __out DWORD* pcProperties ) { HRESULT hr = S_OK; IXMLDOMNodeList* pixnNodes = NULL; IXMLDOMNode* pixnNode = NULL; DWORD cNodes = 0; BURN_MSIPROPERTY* pProperties = NULL; // select property nodes hr = XmlSelectNodes(pixnPackage, L"MsiProperty", &pixnNodes); ExitOnFailure(hr, "Failed to select property nodes."); // get property node count hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get property node count."); if (cNodes) { // allocate memory for properties pProperties = (BURN_MSIPROPERTY*)MemAlloc(sizeof(BURN_MSIPROPERTY) * cNodes, TRUE); ExitOnNull(pProperties, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSI property structs."); // parse property elements for (DWORD i = 0; i < cNodes; ++i) { BURN_MSIPROPERTY* pProperty = &pProperties[i]; hr = XmlNextElement(pixnNodes, &pixnNode, NULL); ExitOnFailure(hr, "Failed to get next node."); // @Id hr = XmlGetAttributeEx(pixnNode, L"Id", &pProperty->sczId); ExitOnFailure(hr, "Failed to get @Id."); // @Value hr = XmlGetAttributeEx(pixnNode, L"Value", &pProperty->sczValue); ExitOnFailure(hr, "Failed to get @Value."); // @RollbackValue hr = XmlGetAttributeEx(pixnNode, L"RollbackValue", &pProperty->sczRollbackValue); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @RollbackValue."); } // @Condition hr = XmlGetAttributeEx(pixnNode, L"Condition", &pProperty->sczCondition); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @Condition."); } // prepare next iteration ReleaseNullObject(pixnNode); } } *pcProperties = cNodes; *prgProperties = pProperties; pProperties = NULL; hr = S_OK; LExit: ReleaseNullObject(pixnNodes); ReleaseMem(pProperties); return hr; } extern "C" void MsiEnginePackageUninitialize( __in BURN_PACKAGE* pPackage ) { ReleaseStr(pPackage->Msi.sczProductCode); ReleaseStr(pPackage->Msi.sczUpgradeCode); // free features if (pPackage->Msi.rgFeatures) { for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) { BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; ReleaseStr(pFeature->sczId); ReleaseStr(pFeature->sczAddLocalCondition); ReleaseStr(pFeature->sczAddSourceCondition); ReleaseStr(pFeature->sczAdvertiseCondition); ReleaseStr(pFeature->sczRollbackAddLocalCondition); ReleaseStr(pFeature->sczRollbackAddSourceCondition); ReleaseStr(pFeature->sczRollbackAdvertiseCondition); } MemFree(pPackage->Msi.rgFeatures); } // free properties if (pPackage->Msi.rgProperties) { for (DWORD i = 0; i < pPackage->Msi.cProperties; ++i) { BURN_MSIPROPERTY* pProperty = &pPackage->Msi.rgProperties[i]; ReleaseStr(pProperty->sczId); ReleaseStr(pProperty->sczValue); ReleaseStr(pProperty->sczRollbackValue); ReleaseStr(pProperty->sczCondition); } MemFree(pPackage->Msi.rgProperties); } // free related MSIs if (pPackage->Msi.rgRelatedMsis) { for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i) { BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i]; ReleaseStr(pRelatedMsi->sczUpgradeCode); ReleaseMem(pRelatedMsi->rgdwLanguages); } MemFree(pPackage->Msi.rgRelatedMsis); } // free slipstream MSPs if (pPackage->Msi.rgsczSlipstreamMspPackageIds) { for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) { ReleaseStr(pPackage->Msi.rgsczSlipstreamMspPackageIds[i]); } MemFree(pPackage->Msi.rgsczSlipstreamMspPackageIds); } if (pPackage->Msi.rgSlipstreamMsps) { MemFree(pPackage->Msi.rgSlipstreamMsps); } if (pPackage->Msi.rgChainedPatches) { MemFree(pPackage->Msi.rgChainedPatches); } // clear struct memset(&pPackage->Msi, 0, sizeof(pPackage->Msi)); } extern "C" HRESULT MsiEngineDetectInitialize( __in BURN_PACKAGES* pPackages ) { AssertSz(pPackages->cPatchInfo, "MsiEngineDetectInitialize() should only be called if there are MSP packages."); HRESULT hr = S_OK; // Add target products for slipstream MSIs that weren't detected. for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage) { BURN_PACKAGE* pMsiPackage = pPackages->rgPackages + iPackage; if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type) { for (DWORD j = 0; j < pMsiPackage->Msi.cSlipstreamMspPackages; ++j) { BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + j; Assert(pSlipstreamMsp->pMspPackage && BURN_PACKAGE_TYPE_MSP == pSlipstreamMsp->pMspPackage->type); if (pSlipstreamMsp->pMspPackage && BURN_PACKAGE_INVALID_PATCH_INDEX == pSlipstreamMsp->dwMsiChainedPatchIndex) { hr = MspEngineAddMissingSlipstreamTarget(pMsiPackage, pSlipstreamMsp); ExitOnFailure(hr, "Failed to add slipstreamed target product code to package: %ls", pSlipstreamMsp->pMspPackage->sczId); } } } } LExit: return hr; } extern "C" HRESULT MsiEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_REGISTRATION* pRegistration, __in BURN_USER_EXPERIENCE* pUserExperience ) { Trace(REPORT_STANDARD, "Detecting MSI package 0x%p", pPackage); HRESULT hr = S_OK; int nCompareResult = 0; LPWSTR sczInstalledVersion = NULL; LPWSTR sczInstalledLanguage = NULL; INSTALLSTATE installState = INSTALLSTATE_UNKNOWN; BOOTSTRAPPER_RELATED_OPERATION relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; WCHAR wzProductCode[MAX_GUID_CHARS + 1] = { }; VERUTIL_VERSION* pVersion = NULL; UINT uLcid = 0; BOOL fPerMachine = FALSE; BOOL fDetectFeatures = FALSE; // detect self by product code // TODO: what to do about MSIINSTALLCONTEXT_USERMANAGED? hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (SUCCEEDED(hr)) { fDetectFeatures = TRUE; hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion); ExitOnFailure(hr, "Failed to parse installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, pPackage->Msi.sczProductCode); if (pVersion->fInvalid) { LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, pPackage->Msi.sczProductCode, sczInstalledVersion); } // compare versions hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pVersion, &nCompareResult); ExitOnFailure(hr, "Failed to compare version '%ls' to installed version: '%ls'", pPackage->Msi.pVersion->sczVersion, pVersion->sczVersion); if (nCompareResult < 0) { pPackage->Msi.operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; } else if (nCompareResult > 0) { pPackage->Msi.operation = BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE; pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; } else { pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; } // Report related MSI package to BA. if (BOOTSTRAPPER_RELATED_OPERATION_NONE != pPackage->Msi.operation) { LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, pPackage->Msi.sczProductCode, LoggingPerMachineToString(pPackage->fPerMachine), pVersion->sczVersion, pPackage->Msi.dwLanguage, LoggingRelatedOperationToString(pPackage->Msi.operation)); hr = BACallbackOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pPackage->Msi.sczUpgradeCode, pPackage->Msi.sczProductCode, pPackage->fPerMachine, pVersion, pPackage->Msi.operation); ExitOnRootFailure(hr, "BA aborted detect related MSI package."); } } else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr) // package not present. { pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; hr = S_OK; } else { ExitOnFailure(hr, "Failed to get product information for ProductCode: %ls", pPackage->Msi.sczProductCode); } // detect related packages by upgrade code for (DWORD i = 0; i < pPackage->Msi.cRelatedMsis; ++i) { BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i]; ReleaseVerutilVersion(pVersion); pVersion = NULL; for (DWORD iProduct = 0; ; ++iProduct) { // get product hr = WiuEnumRelatedProducts(pRelatedMsi->sczUpgradeCode, iProduct, wzProductCode); if (E_NOMOREITEMS == hr) { hr = S_OK; break; } ExitOnFailure(hr, "Failed to enum related products."); // If we found ourselves, skip because saying that a package is related to itself is nonsensical. if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczProductCode, -1, wzProductCode, -1)) { continue; } // get product version hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) { ExitOnFailure(hr, "Failed to get version for product in machine context: %ls", wzProductCode); fPerMachine = TRUE; } else { hr = WiuGetProductInfoEx(wzProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) != hr && HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) != hr) { ExitOnFailure(hr, "Failed to get version for product in user unmanaged context: %ls", wzProductCode); fPerMachine = FALSE; } else { hr = S_OK; continue; } } hr = VerParseVersion(sczInstalledVersion, 0, FALSE, &pVersion); ExitOnFailure(hr, "Failed to parse related installed version: '%ls' for ProductCode: %ls", sczInstalledVersion, wzProductCode); if (pVersion->fInvalid) { LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, wzProductCode, sczInstalledVersion); } // compare versions if (pRelatedMsi->fMinProvided) { hr = VerCompareParsedVersions(pVersion, pRelatedMsi->pMinVersion, &nCompareResult); ExitOnFailure(hr, "Failed to compare related installed version '%ls' to related min version: '%ls'", pVersion->sczVersion, pRelatedMsi->pMinVersion->sczVersion); if (pRelatedMsi->fMinInclusive ? (nCompareResult < 0) : (nCompareResult <= 0)) { continue; } } if (pRelatedMsi->fMaxProvided) { hr = VerCompareParsedVersions(pVersion, pRelatedMsi->pMaxVersion, &nCompareResult); ExitOnFailure(hr, "Failed to compare related installed version '%ls' to related max version: '%ls'", pVersion->sczVersion, pRelatedMsi->pMaxVersion->sczVersion); if (pRelatedMsi->fMaxInclusive ? (nCompareResult > 0) : (nCompareResult >= 0)) { continue; } } // Filter by language if necessary. uLcid = 0; // always reset the found language. if (pRelatedMsi->cLanguages) { // If there is a language to get, convert it into an LCID. hr = WiuGetProductInfoEx(wzProductCode, NULL, fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_LANGUAGE, &sczInstalledLanguage); if (SUCCEEDED(hr)) { hr = StrStringToUInt32(sczInstalledLanguage, 0, &uLcid); } // Ignore related product where we can't read the language. if (FAILED(hr)) { LogErrorId(hr, MSG_FAILED_READ_RELATED_PACKAGE_LANGUAGE, wzProductCode, sczInstalledLanguage, NULL); hr = S_OK; continue; } BOOL fMatchedLcid = FALSE; for (DWORD iLanguage = 0; iLanguage < pRelatedMsi->cLanguages; ++iLanguage) { if (uLcid == pRelatedMsi->rgdwLanguages[iLanguage]) { fMatchedLcid = TRUE; break; } } // Skip the product if the language did not meet the inclusive/exclusive criteria. if ((pRelatedMsi->fLangInclusive && !fMatchedLcid) || (!pRelatedMsi->fLangInclusive && fMatchedLcid)) { continue; } } // If this is a detect-only related package and we're not installed yet, then we'll assume a downgrade // would take place since that is the overwhelmingly common use of detect-only related packages. If // not detect-only then it's easy; we're clearly doing a major upgrade. if (pRelatedMsi->fOnlyDetect) { // If we've already detected a major upgrade that trumps any guesses that the detect is a downgrade // or even something else. if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == pPackage->Msi.operation) { relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; } // It can't be a downgrade if the upgrade codes aren't the same. else if (BOOTSTRAPPER_PACKAGE_STATE_ABSENT == pPackage->currentState && pPackage->Msi.sczUpgradeCode && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pPackage->Msi.sczUpgradeCode, -1, pRelatedMsi->sczUpgradeCode, -1)) { relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; pPackage->Msi.operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE; pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; } else // we're already on the machine so the detect-only *must* be for detection purposes only. { relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_NONE; } } else { relatedMsiOperation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; pPackage->Msi.operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE; } LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_PACKAGE, wzProductCode, LoggingPerMachineToString(fPerMachine), pVersion->sczVersion, uLcid, LoggingRelatedOperationToString(relatedMsiOperation)); // Pass to BA. hr = BACallbackOnDetectRelatedMsiPackage(pUserExperience, pPackage->sczId, pRelatedMsi->sczUpgradeCode, wzProductCode, fPerMachine, pVersion, relatedMsiOperation); ExitOnRootFailure(hr, "BA aborted detect related MSI package."); } } // detect features if (pPackage->Msi.cFeatures) { for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) { BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; // Try to detect features state if the product is present on the machine. if (fDetectFeatures) { hr = WiuQueryFeatureState(pPackage->Msi.sczProductCode, pFeature->sczId, &installState); ExitOnFailure(hr, "Failed to query feature state."); if (INSTALLSTATE_UNKNOWN == installState) // in case of an upgrade a feature could be removed. { installState = INSTALLSTATE_ABSENT; } } else // MSI not installed then the features can't be either. { installState = INSTALLSTATE_ABSENT; } // set current state switch (installState) { case INSTALLSTATE_ABSENT: pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ABSENT; break; case INSTALLSTATE_ADVERTISED: pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED; break; case INSTALLSTATE_LOCAL: pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_LOCAL; break; case INSTALLSTATE_SOURCE: pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_SOURCE; break; default: hr = E_UNEXPECTED; ExitOnRootFailure(hr, "Invalid state value."); } // Pass to BA. hr = BACallbackOnDetectMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, pFeature->currentState); ExitOnRootFailure(hr, "BA aborted detect MSI feature."); } } if (pPackage->fCanAffectRegistration) { pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; } hr = DependencyDetectChainPackage(pPackage, pRegistration); ExitOnFailure(hr, "Failed to detect dependencies for MSI package."); if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState) { hr = MsiEngineDetectCompatiblePackage(pPackage); ExitOnFailure(hr, "Failed to detect compatible package for MSI package."); if (BURN_PACKAGE_TYPE_MSI == pPackage->compatiblePackage.type) { LPCWSTR wzCompatibleProductCode = pPackage->compatiblePackage.compatibleEntry.sczId; LPCWSTR wzCompatibleInstalledVersion = pPackage->compatiblePackage.Msi.sczVersion; ReleaseVerutilVersion(pVersion); pVersion = NULL; hr = VerParseVersion(wzCompatibleInstalledVersion, 0, FALSE, &pVersion); ExitOnFailure(hr, "Failed to parse dependency version: '%ls' for ProductCode: %ls", wzCompatibleInstalledVersion, wzCompatibleProductCode); if (pVersion->fInvalid) { LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, wzCompatibleProductCode, wzCompatibleInstalledVersion); } pPackage->compatiblePackage.Msi.pVersion = pVersion; pVersion = NULL; // compare versions hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pPackage->compatiblePackage.Msi.pVersion, &nCompareResult); ExitOnFailure(hr, "Failed to compare version '%ls' to dependency version: '%ls'", pPackage->Msi.pVersion->sczVersion, wzCompatibleInstalledVersion); if (nCompareResult < 0) { pPackage->compatiblePackage.fPlannable = TRUE; LogId(REPORT_STANDARD, MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczProviderKey, wzCompatibleProductCode, wzCompatibleInstalledVersion, pPackage->Msi.sczProductCode); hr = BACallbackOnDetectCompatibleMsiPackage(pUserExperience, pPackage->sczId, wzCompatibleProductCode, pPackage->compatiblePackage.Msi.pVersion); ExitOnRootFailure(hr, "BA aborted detect compatible MSI package."); } } } LExit: ReleaseStr(sczInstalledLanguage); ReleaseStr(sczInstalledVersion); ReleaseVerutilVersion(pVersion); return hr; } extern "C" HRESULT MsiEngineDetectCompatiblePackage( __in BURN_PACKAGE* pPackage ) { HRESULT hr = S_OK; LPWSTR sczVersion = NULL; LPWSTR sczCacheId = NULL; LPCWSTR wzCompatibleProductCode = pPackage->compatiblePackage.compatibleEntry.sczId; if (!pPackage->compatiblePackage.fDetected) { ExitFunction(); } hr = WiuGetProductInfoEx(wzCompatibleProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczVersion); if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr || E_INVALIDARG == hr) { ExitFunction1(hr = S_OK); } ExitOnFailure(hr, "Failed to get product information for compatible ProductCode: %ls", wzCompatibleProductCode); // Assume the package used the default cache ID generation from the binder. hr = StrAllocFormatted(&sczCacheId, L"%lsv%ls", wzCompatibleProductCode, sczVersion); ExitOnFailure(hr, "Failed to format cache ID for compatible package."); pPackage->compatiblePackage.sczCacheId = sczCacheId; sczCacheId = NULL; pPackage->compatiblePackage.Msi.sczVersion = sczVersion; sczVersion = NULL; pPackage->compatiblePackage.type = BURN_PACKAGE_TYPE_MSI; LExit: ReleaseStr(sczVersion); ReleaseStr(sczCacheId); return hr; } extern "C" HRESULT MsiEnginePlanInitializePackage( __in BURN_PACKAGE* pPackage, __in BOOTSTRAPPER_ACTION overallAction, __in BURN_VARIABLES* pVariables, __in BURN_USER_EXPERIENCE* pUserExperience ) { HRESULT hr = S_OK; if (pPackage->Msi.cFeatures) { // get feature request states for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) { BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; // Evaluate feature conditions. hr = EvaluateActionStateConditions(pVariables, pFeature->sczAddLocalCondition, pFeature->sczAddSourceCondition, pFeature->sczAdvertiseCondition, &pFeature->defaultRequested); ExitOnFailure(hr, "Failed to evaluate requested state conditions."); hr = EvaluateActionStateConditions(pVariables, pFeature->sczRollbackAddLocalCondition, pFeature->sczRollbackAddSourceCondition, pFeature->sczRollbackAdvertiseCondition, &pFeature->expectedState); ExitOnFailure(hr, "Failed to evaluate expected state conditions."); // Remember the default feature requested state so the engine doesn't get blamed for planning the wrong thing if the BA changes it. pFeature->requested = pFeature->defaultRequested; // Send plan MSI feature message to BA. hr = BACallbackOnPlanMsiFeature(pUserExperience, pPackage->sczId, pFeature->sczId, &pFeature->requested); ExitOnRootFailure(hr, "BA aborted plan MSI feature."); } } if (pPackage->compatiblePackage.fPlannable) { Assert(BURN_PACKAGE_TYPE_MSI == pPackage->compatiblePackage.type); pPackage->compatiblePackage.fDefaultRequested = BOOTSTRAPPER_ACTION_UNINSTALL == overallAction || BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == overallAction; pPackage->compatiblePackage.fRequested = pPackage->compatiblePackage.fDefaultRequested; hr = BACallbackOnPlanCompatibleMsiPackageBegin(pUserExperience, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczId, pPackage->compatiblePackage.Msi.pVersion, &pPackage->compatiblePackage.fRequested); BACallbackOnPlanCompatibleMsiPackageComplete(pUserExperience, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczId, hr, pPackage->compatiblePackage.fRequested); ExitOnRootFailure(hr, "BA aborted plan compatible MSI package begin."); } LExit: return hr; } // // PlanCalculate - calculates the execute and rollback state for the requested package state. // extern "C" HRESULT MsiEnginePlanCalculatePackage( __in BURN_PACKAGE* pPackage, __in BOOL fInsideMsiTransaction ) { Trace(REPORT_STANDARD, "Planning MSI package 0x%p", pPackage); HRESULT hr = S_OK; BOOTSTRAPPER_ACTION_STATE execute = BOOTSTRAPPER_ACTION_STATE_NONE; BOOTSTRAPPER_ACTION_STATE rollback = BOOTSTRAPPER_ACTION_STATE_NONE; BOOL fFeatureActionDelta = FALSE; BOOL fRollbackFeatureActionDelta = FALSE; if (pPackage->Msi.cFeatures) { // If the package is present and we're repairing it, or we're doing a minor update. BOOL fRepairingPackage = (BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState && BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) || BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE == pPackage->Msi.operation; // plan features for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) { BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; // Calculate feature actions. hr = CalculateFeatureAction(pFeature->currentState, pFeature->requested, fRepairingPackage, &pFeature->execute, &fFeatureActionDelta); ExitOnFailure(hr, "Failed to calculate execute feature state."); hr = CalculateFeatureAction(pFeature->requested, BOOTSTRAPPER_FEATURE_ACTION_NONE == pFeature->execute ? pFeature->expectedState : pFeature->currentState, FALSE, &pFeature->rollback, &fRollbackFeatureActionDelta); ExitOnFailure(hr, "Failed to calculate rollback feature state."); } } // execute action switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: if (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) { if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested) { execute = BOOTSTRAPPER_ACTION_STATE_REPAIR; } else { execute = fFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE; } } else if ((BOOTSTRAPPER_REQUEST_STATE_ABSENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_CACHE == pPackage->requested) && !pPackage->fPermanent) // removing a package that should be removed. { execute = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED == pPackage->currentState ? BOOTSTRAPPER_ACTION_STATE_NONE : BOOTSTRAPPER_ACTION_STATE_UNINSTALL; } else if (BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT == pPackage->requested) { execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; } else if (BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT == pPackage->requested) { execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; } else { execute = BOOTSTRAPPER_ACTION_STATE_NONE; } break; case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: if (BOOTSTRAPPER_RELATED_OPERATION_MINOR_UPDATE == pPackage->Msi.operation) { execute = BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE; } else { execute = BOOTSTRAPPER_ACTION_STATE_INSTALL; } break; case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: execute = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; break; default: execute = BOOTSTRAPPER_ACTION_STATE_NONE; break; } break; default: hr = E_INVALIDARG; ExitOnRootFailure(hr, "Invalid package current state result encountered during plan: %d", pPackage->currentState); } // Calculate the rollback action if there is an execute action. if (BOOTSTRAPPER_ACTION_STATE_NONE != execute && !fInsideMsiTransaction) { switch (pPackage->currentState) { case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED: switch (pPackage->requested) { case BOOTSTRAPPER_REQUEST_STATE_PRESENT: rollback = fRollbackFeatureActionDelta ? BOOTSTRAPPER_ACTION_STATE_MODIFY : BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_REPAIR: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; case BOOTSTRAPPER_REQUEST_STATE_CACHE: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT: __fallthrough; case BOOTSTRAPPER_REQUEST_STATE_ABSENT: rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL == execute ? BOOTSTRAPPER_ACTION_STATE_INSTALL : BOOTSTRAPPER_ACTION_STATE_NONE; break; default: rollback = BOOTSTRAPPER_ACTION_STATE_NONE; break; } break; case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough; case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: // If the package is not permanent and we requested to put the package on the machine then // remove the package during rollback. if (!pPackage->fPermanent && (BOOTSTRAPPER_REQUEST_STATE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT == pPackage->requested || BOOTSTRAPPER_REQUEST_STATE_REPAIR == pPackage->requested)) { rollback = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; } else { rollback = BOOTSTRAPPER_ACTION_STATE_NONE; } break; default: hr = E_INVALIDARG; ExitOnRootFailure(hr, "Invalid package detection result encountered."); } } // return values pPackage->execute = execute; pPackage->rollback = rollback; LExit: return hr; } // // PlanAdd - adds the calculated execute and rollback actions for the package. // extern "C" HRESULT MsiEnginePlanAddPackage( __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_PACKAGE* pPackage, __in BURN_PLAN* pPlan, __in BURN_LOGGING* pLog, __in BURN_VARIABLES* pVariables ) { HRESULT hr = S_OK; BURN_EXECUTE_ACTION* pAction = NULL; BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions = NULL; BOOTSTRAPPER_FEATURE_ACTION* rgRollbackFeatureActions = NULL; if (pPackage->Msi.cFeatures) { // Allocate and populate array for feature actions. rgFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE); ExitOnNull(rgFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for feature actions."); rgRollbackFeatureActions = (BOOTSTRAPPER_FEATURE_ACTION*)MemAlloc(sizeof(BOOTSTRAPPER_FEATURE_ACTION) * pPackage->Msi.cFeatures, TRUE); ExitOnNull(rgRollbackFeatureActions, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback feature actions."); for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) { BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; // calculate feature actions rgFeatureActions[i] = pFeature->execute; rgRollbackFeatureActions[i] = pFeature->rollback; } } hr = DependencyPlanPackage(NULL, pPackage, pPlan); ExitOnFailure(hr, "Failed to plan package dependency actions."); if (pPackage->compatiblePackage.fRemove) { hr = PlanAppendExecuteAction(pPlan, &pAction); ExitOnFailure(hr, "Failed to append execute action."); pAction->type = BURN_EXECUTE_ACTION_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE; pAction->uninstallMsiCompatiblePackage.pParentPackage = pPackage; pAction->uninstallMsiCompatiblePackage.dwLoggingAttributes = pLog->dwAttributes; LoggingSetCompatiblePackageVariable(pPackage, pLog, pVariables, &pAction->uninstallMsiCompatiblePackage.sczLogPath); // ignore errors. } // add rollback action if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) { hr = PlanAppendRollbackAction(pPlan, &pAction); ExitOnFailure(hr, "Failed to append rollback action."); pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; pAction->msiPackage.pPackage = pPackage; pAction->msiPackage.action = pPackage->rollback; pAction->msiPackage.rgFeatures = rgRollbackFeatureActions; rgRollbackFeatureActions = NULL; hr = MsiEnginePlanPackageOptions(display, pUserExperience, pPackage->sczId, FALSE, pAction->msiPackage.action, &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler, &pAction->msiPackage.fileVersioning); ExitOnFailure(hr, "Failed to get msi ui options."); LoggingSetPackageVariable(pPackage, NULL, TRUE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; // Plan a checkpoint between rollback and execute so that we always attempt // rollback in the case that the MSI was not able to rollback itself (e.g. // user pushes cancel after InstallFinalize). hr = PlanExecuteCheckpoint(pPlan); ExitOnFailure(hr, "Failed to append execute checkpoint."); } // add execute action if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute) { hr = PlanAppendExecuteAction(pPlan, &pAction); ExitOnFailure(hr, "Failed to append execute action."); pAction->type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE; pAction->msiPackage.pPackage = pPackage; pAction->msiPackage.action = pPackage->execute; pAction->msiPackage.rgFeatures = rgFeatureActions; rgFeatureActions = NULL; hr = MsiEnginePlanPackageOptions(display, pUserExperience, pPackage->sczId, TRUE, pAction->msiPackage.action, &pAction->msiPackage.actionMsiProperty, &pAction->msiPackage.uiLevel, &pAction->msiPackage.fDisableExternalUiHandler, &pAction->msiPackage.fileVersioning); ExitOnFailure(hr, "Failed to get msi ui options."); LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; } LExit: ReleaseMem(rgFeatureActions); ReleaseMem(rgRollbackFeatureActions); return hr; } extern "C" HRESULT MsiEngineBeginTransaction( __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary ) { HRESULT hr = S_OK; MSIHANDLE hTransactionHandle = NULL; HANDLE hChangeOfOwnerEvent = NULL; LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_BEGIN, pRollbackBoundary->sczId); hr = WiuBeginTransaction(pRollbackBoundary->sczId, 0, &hTransactionHandle, &hChangeOfOwnerEvent, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath); if (HRESULT_FROM_WIN32(ERROR_ROLLBACK_DISABLED) == hr) { LogId(REPORT_ERROR, MSG_MSI_TRANSACTIONS_DISABLED); } ExitOnFailure(hr, "Failed to begin an MSI transaction"); LExit: // Assume that MsiEndTransaction cleans up the handles. return hr; } extern "C" HRESULT MsiEngineCommitTransaction( __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; WIU_RESTART restart = WIU_RESTART_NONE; LogId(REPORT_STANDARD, MSG_MSI_TRANSACTION_COMMIT, pRollbackBoundary->sczId); hr = WiuEndTransaction(MSITRANSACTIONSTATE_COMMIT, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath, &restart); ExitOnFailure(hr, "Failed to commit the MSI transaction"); LExit: 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; } return hr; } extern "C" HRESULT MsiEngineRollbackTransaction( __in BURN_ROLLBACK_BOUNDARY* pRollbackBoundary, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; WIU_RESTART restart = WIU_RESTART_NONE; LogId(REPORT_WARNING, MSG_MSI_TRANSACTION_ROLLBACK, pRollbackBoundary->sczId); hr = WiuEndTransaction(MSITRANSACTIONSTATE_ROLLBACK, WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE, pRollbackBoundary->sczLogPath, &restart); ExitOnFailure(hr, "Failed to rollback the MSI transaction"); LExit: 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; } return hr; } extern "C" HRESULT MsiEngineExecutePackage( __in_opt HWND hwndParent, __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_CACHE* pCache, __in BURN_VARIABLES* pVariables, __in BOOL fRollback, __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; WIU_MSI_EXECUTE_CONTEXT context = { }; WIU_RESTART restart = WIU_RESTART_NONE; LPWSTR sczInstalledVersion = NULL; LPWSTR sczCachedDirectory = NULL; LPWSTR sczMsiPath = NULL; LPWSTR sczProperties = NULL; LPWSTR sczObfuscatedProperties = NULL; BURN_PACKAGE* pPackage = pExecuteAction->msiPackage.pPackage; BURN_PAYLOAD* pPackagePayload = pPackage->payloads.rgItems[0].pPayload; // During rollback, if the package is already in the rollback state we expect don't // touch it again. if (fRollback) { if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pExecuteAction->msiPackage.action) { hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (FAILED(hr)) // package not present. { LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_ABSENT)); hr = S_OK; ExitFunction(); } } else if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->msiPackage.action) { hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); if (SUCCEEDED(hr)) // package present. { LogId(REPORT_STANDARD, MSG_ROLLBACK_PACKAGE_SKIPPED, pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), LoggingPackageStateToString(BOOTSTRAPPER_PACKAGE_STATE_PRESENT)); hr = S_OK; ExitFunction(); } hr = S_OK; } } // Default to "verbose" logging and set extra debug mode only if explicitly required. DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; if (pExecuteAction->msiPackage.dwLoggingAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) { dwLogMode |= INSTALLLOGMODE_EXTRADEBUG; } if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL != pExecuteAction->msiPackage.action) { // get cached MSI path hr = CacheGetCompletedPath(pCache, pPackage->fPerMachine, pPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure(hr, "Failed to get cached path for package: %ls", pPackage->sczId); // Best effort to set the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE, FALSE); hr = PathConcatRelativeToFullyQualifiedBase(sczCachedDirectory, pPackagePayload->sczFilePath, &sczMsiPath); ExitOnFailure(hr, "Failed to build MSI path."); } // Best effort to set the execute package action variable. VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->msiPackage.action, TRUE); // Wire up the external UI handler and logging. if (pExecuteAction->msiPackage.fDisableExternalUiHandler) { hr = WiuInitializeInternalUI(pExecuteAction->msiPackage.uiLevel, hwndParent, &context); ExitOnFailure(hr, "Failed to initialize internal UI for MSI package."); } else { hr = WiuInitializeExternalUI(pfnMessageHandler, pExecuteAction->msiPackage.uiLevel, hwndParent, pvContext, fRollback, &context); ExitOnFailure(hr, "Failed to initialize external UI handler."); } if (pExecuteAction->msiPackage.sczLogPath && *pExecuteAction->msiPackage.sczLogPath) { hr = WiuEnableLog(dwLogMode, pExecuteAction->msiPackage.sczLogPath, INSTALLLOGATTRIBUTES_APPEND); ExitOnFailure(hr, "Failed to enable logging for package: %ls to: %ls", pPackage->sczId, pExecuteAction->msiPackage.sczLogPath); } // set up properties hr = MsiEngineConcatPackageProperties(pPackage->Msi.rgProperties, pPackage->Msi.cProperties, pVariables, fRollback, &sczProperties, FALSE); ExitOnFailure(hr, "Failed to add properties to argument string."); hr = MsiEngineConcatPackageProperties(pPackage->Msi.rgProperties, pPackage->Msi.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); ExitOnFailure(hr, "Failed to add obfuscated properties to argument string."); // add feature action properties hr = ConcatFeatureActionProperties(pPackage, pExecuteAction->msiPackage.rgFeatures, &sczProperties); ExitOnFailure(hr, "Failed to add feature action properties to argument string."); hr = ConcatFeatureActionProperties(pPackage, pExecuteAction->msiPackage.rgFeatures, &sczObfuscatedProperties); ExitOnFailure(hr, "Failed to add feature action properties to obfuscated argument string."); // add slipstream patch properties hr = ConcatPatchProperty(pCache, pPackage, fRollback, &sczProperties); ExitOnFailure(hr, "Failed to add patch properties to argument string."); hr = ConcatPatchProperty(pCache, pPackage, fRollback, &sczObfuscatedProperties); ExitOnFailure(hr, "Failed to add patch properties to obfuscated argument string."); hr = MsiEngineConcatBurnProperties(pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.actionMsiProperty, pExecuteAction->msiPackage.fileVersioning, TRUE, 0 != pPackage->Msi.cFeatures, &sczProperties); ExitOnFailure(hr, "Failed to add action property to argument string."); hr = MsiEngineConcatBurnProperties(pExecuteAction->msiPackage.action, pExecuteAction->msiPackage.actionMsiProperty, pExecuteAction->msiPackage.fileVersioning, TRUE, 0 != pPackage->Msi.cFeatures, &sczObfuscatedProperties); ExitOnFailure(hr, "Failed to add action property to obfuscated argument string."); LogId(REPORT_STANDARD, MSG_APPLYING_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, LoggingActionStateToString(pExecuteAction->msiPackage.action), sczMsiPath, sczObfuscatedProperties ? sczObfuscatedProperties : L""); // // Do the actual action. // switch (pExecuteAction->msiPackage.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); ExitOnFailure(hr, "Failed to install MSI package."); RegisterSourceDirectory(pPackage, sczMsiPath); break; case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); ExitOnFailure(hr, "Failed to perform minor upgrade of MSI package."); RegisterSourceDirectory(pPackage, sczMsiPath); break; case BOOTSTRAPPER_ACTION_STATE_MODIFY: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_REPAIR: hr = WiuInstallProduct(sczMsiPath, sczProperties, &restart); ExitOnFailure(hr, "Failed to run maintenance mode for MSI package."); break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: hr = WiuConfigureProductEx(pPackage->Msi.sczProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart); if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) { LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pPackage->sczId); hr = S_OK; } ExitOnFailure(hr, "Failed to uninstall MSI package."); break; } LExit: WiuUninitializeExternalUI(&context); StrSecureZeroFreeString(sczProperties); ReleaseStr(sczObfuscatedProperties); ReleaseStr(sczMsiPath); ReleaseStr(sczCachedDirectory); ReleaseStr(sczInstalledVersion); 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 and action variables. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE, FALSE); VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE, FALSE); return hr; } extern "C" HRESULT MsiEngineUninstallCompatiblePackage( __in_opt HWND hwndParent, __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_CACHE* /*pCache*/, __in BURN_VARIABLES* /*pVariables*/, __in BOOL fRollback, __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; WIU_MSI_EXECUTE_CONTEXT context = { }; WIU_RESTART restart = WIU_RESTART_NONE; LPWSTR sczProperties = NULL; BOOTSTRAPPER_ACTION_STATE action = BOOTSTRAPPER_ACTION_STATE_UNINSTALL; BURN_MSI_PROPERTY burnMsiProperty = BURN_MSI_PROPERTY_NONE; BOOTSTRAPPER_MSI_FILE_VERSIONING fileVersioning = BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER; BURN_PACKAGE* pParentPackage = pExecuteAction->uninstallMsiCompatiblePackage.pParentPackage; BURN_COMPATIBLE_PROVIDER_ENTRY* pCompatibleEntry = &pExecuteAction->uninstallMsiCompatiblePackage.pParentPackage->compatiblePackage.compatibleEntry; // Default to "verbose" logging and set extra debug mode only if explicitly required. DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; if (pExecuteAction->uninstallMsiCompatiblePackage.dwLoggingAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG) { dwLogMode |= INSTALLLOGMODE_EXTRADEBUG; } hr = WiuInitializeExternalUI(pfnMessageHandler, INSTALLUILEVEL_NONE, hwndParent, pvContext, fRollback, &context); ExitOnFailure(hr, "Failed to initialize external UI handler."); if (pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath && *pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath) { hr = WiuEnableLog(dwLogMode, pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath, INSTALLLOGATTRIBUTES_APPEND); ExitOnFailure(hr, "Failed to enable logging for compatible package: %ls to: %ls", pCompatibleEntry->sczId, pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath); } hr = MsiEngineConcatBurnProperties(action, burnMsiProperty, fileVersioning, TRUE, FALSE, &sczProperties); ExitOnFailure(hr, "Failed to add action property to argument string."); LogId(REPORT_STANDARD, MSG_APPLYING_ORPHAN_COMPATIBLE_PACKAGE, LoggingRollbackOrExecute(fRollback), pCompatibleEntry->sczId, pParentPackage->sczId, LoggingActionStateToString(action), sczProperties ? sczProperties : L""); hr = WiuConfigureProductEx(pCompatibleEntry->sczId, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart); if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr) { LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pCompatibleEntry->sczId); hr = S_OK; } ExitOnFailure(hr, "Failed to uninstall compatible MSI package."); LExit: WiuUninitializeExternalUI(&context); StrSecureZeroFreeString(sczProperties); 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; } return hr; } extern "C" HRESULT MsiEngineConcatBurnProperties( __in BOOTSTRAPPER_ACTION_STATE action, __in BURN_MSI_PROPERTY actionMsiProperty, __in BOOTSTRAPPER_MSI_FILE_VERSIONING fileVersioning, __in BOOL fMsiPackage, __in BOOL fFeatureSelectionEnabled, __deref_out_z LPWSTR* psczProperties ) { HRESULT hr = S_OK; LPCWSTR wzPropertyName = NULL; LPCWSTR wzReinstallModeOptions = NULL; LPCWSTR wzFileVersioning = L""; BOOL fReinstallAll = FALSE; BOOL fIgnoreDependencies = FALSE; switch (action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: wzReinstallModeOptions = L"mus"; break; case BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE: if (fMsiPackage) { // If feature selection is not enabled, // then reinstall the existing features to ensure they get updated. fReinstallAll = !fFeatureSelectionEnabled; wzReinstallModeOptions = L"vmus"; } break; case BOOTSTRAPPER_ACTION_STATE_REPAIR: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_MODIFY: if (fMsiPackage) { fReinstallAll = BOOTSTRAPPER_ACTION_STATE_MODIFY != action && !fFeatureSelectionEnabled; wzReinstallModeOptions = L"cmus"; fIgnoreDependencies = TRUE; } else { wzReinstallModeOptions = L"mus"; } break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: fIgnoreDependencies = TRUE; break; } switch (actionMsiProperty) { case BURN_MSI_PROPERTY_INSTALL: wzPropertyName = BURNMSIINSTALL_PROPERTY_NAME; break; case BURN_MSI_PROPERTY_MODIFY: wzPropertyName = BURNMSIMODIFY_PROPERTY_NAME; break; case BURN_MSI_PROPERTY_REPAIR: wzPropertyName = BURNMSIREPAIR_PROPERTY_NAME; break; case BURN_MSI_PROPERTY_UNINSTALL: wzPropertyName = BURNMSIUNINSTALL_PROPERTY_NAME; break; } if (wzPropertyName) { hr = StrAllocConcatFormattedSecure(psczProperties, L" %ls=1", wzPropertyName); ExitOnFailure(hr, "Failed to add burn action property."); } if (fReinstallAll) { hr = StrAllocConcatSecure(psczProperties, L" REINSTALL=ALL", 0); ExitOnFailure(hr, "Failed to add reinstall all property."); } if (wzReinstallModeOptions) { switch (fileVersioning) { case BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER: wzFileVersioning = L"o"; break; case BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER_OR_EQUAL: wzFileVersioning = L"e"; break; case BOOTSTRAPPER_MSI_FILE_VERSIONING_ALL: wzFileVersioning = L"a"; break; } hr = StrAllocConcatFormattedSecure(psczProperties, L" REINSTALLMODE=\"%ls%ls\"", wzReinstallModeOptions, wzFileVersioning); ExitOnFailure(hr, "Failed to add reinstall mode."); } hr = StrAllocConcatSecure(psczProperties, L" REBOOT=ReallySuppress", 0); ExitOnFailure(hr, "Failed to add reboot suppression property."); if (fIgnoreDependencies) { // Ignore all dependencies, since the Burn engine already performed the check. hr = StrAllocConcatFormattedSecure(psczProperties, L" %ls=ALL", DEPENDENCY_IGNOREDEPENDENCIES); ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); } LExit: return hr; } extern "C" HRESULT MsiEngineConcatPackageProperties( __in_ecount(cProperties) BURN_MSIPROPERTY* rgProperties, __in DWORD cProperties, __in BURN_VARIABLES* pVariables, __in BOOL fRollback, __deref_out_z LPWSTR* psczProperties, __in BOOL fObfuscateHiddenVariables ) { HRESULT hr = S_OK; LPWSTR sczValue = NULL; LPWSTR sczEscapedValue = NULL; LPWSTR sczProperty = NULL; for (DWORD i = 0; i < cProperties; ++i) { BURN_MSIPROPERTY* pProperty = &rgProperties[i]; if (pProperty->sczCondition && *pProperty->sczCondition) { BOOL fCondition = FALSE; hr = ConditionEvaluate(pVariables, pProperty->sczCondition, &fCondition); if (FAILED(hr) || !fCondition) { LogId(REPORT_VERBOSE, MSG_MSI_PROPERTY_CONDITION_FAILED, pProperty->sczId, pProperty->sczCondition, LoggingTrueFalseToString(fCondition)); continue; } } // format property value if (fObfuscateHiddenVariables) { hr = VariableFormatStringObfuscated(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL); } else { hr = VariableFormatString(pVariables, (fRollback && pProperty->sczRollbackValue) ? pProperty->sczRollbackValue : pProperty->sczValue, &sczValue, NULL); ExitOnFailure(hr, "Failed to format property value."); } ExitOnFailure(hr, "Failed to format property value."); // escape property value hr = EscapePropertyArgumentString(sczValue, &sczEscapedValue, !fObfuscateHiddenVariables); ExitOnFailure(hr, "Failed to escape string."); // build part hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &sczProperty, L" %s%=\"%s\"", pProperty->sczId, sczEscapedValue); ExitOnFailure(hr, "Failed to format property string part."); // append to property string hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, psczProperties, sczProperty, 0); ExitOnFailure(hr, "Failed to append property string part."); } LExit: StrSecureZeroFreeString(sczValue); StrSecureZeroFreeString(sczEscapedValue); StrSecureZeroFreeString(sczProperty); return hr; } extern "C" HRESULT MsiEnginePlanPackageOptions( __in BOOTSTRAPPER_DISPLAY display, __in BURN_USER_EXPERIENCE* pUserExperience, __in LPCWSTR wzPackageId, __in BOOL fExecute, __in BOOTSTRAPPER_ACTION_STATE actionState, __out BURN_MSI_PROPERTY* pActionMsiProperty, __out INSTALLUILEVEL* pUiLevel, __out BOOL* pfDisableExternalUiHandler, __out BOOTSTRAPPER_MSI_FILE_VERSIONING* pFileVersioning ) { *pUiLevel = INSTALLUILEVEL_NONE; *pfDisableExternalUiHandler = FALSE; *pFileVersioning = BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER; if (BOOTSTRAPPER_DISPLAY_FULL == display || BOOTSTRAPPER_DISPLAY_PASSIVE == display) { *pUiLevel = static_cast(*pUiLevel | INSTALLUILEVEL_SOURCERESONLY); } switch (actionState) { case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: *pActionMsiProperty = BURN_MSI_PROPERTY_UNINSTALL; break; case BOOTSTRAPPER_ACTION_STATE_REPAIR: *pActionMsiProperty = BURN_MSI_PROPERTY_REPAIR; *pFileVersioning = BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER_OR_EQUAL; break; case BOOTSTRAPPER_ACTION_STATE_MODIFY: *pActionMsiProperty = BURN_MSI_PROPERTY_MODIFY; break; default: *pActionMsiProperty = BURN_MSI_PROPERTY_INSTALL; break; } return BACallbackOnPlanMsiPackage(pUserExperience, wzPackageId, fExecute, actionState, pActionMsiProperty, pUiLevel, pfDisableExternalUiHandler, pFileVersioning); } extern "C" void MsiEngineUpdateInstallRegistrationState( __in BURN_EXECUTE_ACTION* pAction, __in BOOL fRollback, __in HRESULT hrExecute, __in BOOL fInsideMsiTransaction ) { BURN_PACKAGE_REGISTRATION_STATE newState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; BURN_PACKAGE* pPackage = pAction->msiPackage.pPackage; if (FAILED(hrExecute) || !pPackage->fCanAffectRegistration) { ExitFunction(); } if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pAction->msiPackage.action) { newState = BURN_PACKAGE_REGISTRATION_STATE_ABSENT; } else { newState = BURN_PACKAGE_REGISTRATION_STATE_PRESENT; } if (fInsideMsiTransaction) { pPackage->transactionRegistrationState = newState; } else { pPackage->installRegistrationState = newState; } if (BURN_PACKAGE_REGISTRATION_STATE_ABSENT == newState) { for (DWORD i = 0; i < pPackage->Msi.cChainedPatches; ++i) { BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + i; BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; if (fInsideMsiTransaction) { pTargetProduct->transactionRegistrationState = newState; } else { pTargetProduct->registrationState = newState; } } } else { for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) { BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i; BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute; if (BOOTSTRAPPER_ACTION_STATE_INSTALL > patchExecuteAction) { continue; } BURN_CHAINED_PATCH* pChainedPatch = pPackage->Msi.rgChainedPatches + pSlipstreamMsp->dwMsiChainedPatchIndex; BURN_MSPTARGETPRODUCT* pTargetProduct = pChainedPatch->pMspPackage->Msp.rgTargetProducts + pChainedPatch->dwMspTargetProductIndex; if (fInsideMsiTransaction) { pTargetProduct->transactionRegistrationState = newState; } else { pTargetProduct->registrationState = newState; } } } LExit: return; } // internal helper functions static HRESULT ParseRelatedMsiFromXml( __in IXMLDOMNode* pixnRelatedMsi, __in BURN_RELATED_MSI* pRelatedMsi ) { HRESULT hr = S_OK; IXMLDOMNodeList* pixnNodes = NULL; IXMLDOMNode* pixnNode = NULL; DWORD cNodes = 0; LPWSTR scz = NULL; // @Id hr = XmlGetAttributeEx(pixnRelatedMsi, L"Id", &pRelatedMsi->sczUpgradeCode); ExitOnFailure(hr, "Failed to get @Id."); // @MinVersion hr = XmlGetAttributeEx(pixnRelatedMsi, L"MinVersion", &scz); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @MinVersion."); hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMinVersion); ExitOnFailure(hr, "Failed to parse @MinVersion: %ls", scz); if (pRelatedMsi->pMinVersion->fInvalid) { LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); } // flag that we have a min version pRelatedMsi->fMinProvided = TRUE; // @MinInclusive hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MinInclusive", &pRelatedMsi->fMinInclusive); ExitOnFailure(hr, "Failed to get @MinInclusive."); } // @MaxVersion hr = XmlGetAttributeEx(pixnRelatedMsi, L"MaxVersion", &scz); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get @MaxVersion."); hr = VerParseVersion(scz, 0, FALSE, &pRelatedMsi->pMaxVersion); ExitOnFailure(hr, "Failed to parse @MaxVersion: %ls", scz); if (pRelatedMsi->pMaxVersion->fInvalid) { LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); } // flag that we have a max version pRelatedMsi->fMaxProvided = TRUE; // @MaxInclusive hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"MaxInclusive", &pRelatedMsi->fMaxInclusive); ExitOnFailure(hr, "Failed to get @MaxInclusive."); } // @OnlyDetect hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"OnlyDetect", &pRelatedMsi->fOnlyDetect); ExitOnFailure(hr, "Failed to get @OnlyDetect."); // select language nodes hr = XmlSelectNodes(pixnRelatedMsi, L"Language", &pixnNodes); ExitOnFailure(hr, "Failed to select language nodes."); // get language node count hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get language node count."); if (cNodes) { // @LangInclusive hr = XmlGetYesNoAttribute(pixnRelatedMsi, L"LangInclusive", &pRelatedMsi->fLangInclusive); ExitOnFailure(hr, "Failed to get @LangInclusive."); // allocate memory for language IDs pRelatedMsi->rgdwLanguages = (DWORD*)MemAlloc(sizeof(DWORD) * cNodes, TRUE); ExitOnNull(pRelatedMsi->rgdwLanguages, hr, E_OUTOFMEMORY, "Failed to allocate memory for language IDs."); pRelatedMsi->cLanguages = cNodes; // parse language elements for (DWORD i = 0; i < cNodes; ++i) { hr = XmlNextElement(pixnNodes, &pixnNode, NULL); ExitOnFailure(hr, "Failed to get next node."); // @Id hr = XmlGetAttributeNumber(pixnNode, L"Id", &pRelatedMsi->rgdwLanguages[i]); ExitOnFailure(hr, "Failed to get Language/@Id."); // prepare next iteration ReleaseNullObject(pixnNode); } } hr = S_OK; LExit: ReleaseObject(pixnNodes); ReleaseObject(pixnNode); ReleaseStr(scz); return hr; } static HRESULT EvaluateActionStateConditions( __in BURN_VARIABLES* pVariables, __in_z_opt LPCWSTR sczAddLocalCondition, __in_z_opt LPCWSTR sczAddSourceCondition, __in_z_opt LPCWSTR sczAdvertiseCondition, __out BOOTSTRAPPER_FEATURE_STATE* pState ) { HRESULT hr = S_OK; BOOL fCondition = FALSE; // if no condition was set, return no feature state if (!sczAddLocalCondition && !sczAddSourceCondition && !sczAdvertiseCondition) { *pState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN; ExitFunction(); } if (sczAddLocalCondition) { hr = ConditionEvaluate(pVariables, sczAddLocalCondition, &fCondition); ExitOnFailure(hr, "Failed to evaluate add local condition."); if (fCondition) { *pState = BOOTSTRAPPER_FEATURE_STATE_LOCAL; ExitFunction(); } } if (sczAddSourceCondition) { hr = ConditionEvaluate(pVariables, sczAddSourceCondition, &fCondition); ExitOnFailure(hr, "Failed to evaluate add source condition."); if (fCondition) { *pState = BOOTSTRAPPER_FEATURE_STATE_SOURCE; ExitFunction(); } } if (sczAdvertiseCondition) { hr = ConditionEvaluate(pVariables, sczAdvertiseCondition, &fCondition); ExitOnFailure(hr, "Failed to evaluate advertise condition."); if (fCondition) { *pState = BOOTSTRAPPER_FEATURE_STATE_ADVERTISED; ExitFunction(); } } // if no condition was true, set to absent *pState = BOOTSTRAPPER_FEATURE_STATE_ABSENT; LExit: return hr; } static HRESULT CalculateFeatureAction( __in BOOTSTRAPPER_FEATURE_STATE currentState, __in BOOTSTRAPPER_FEATURE_STATE requestedState, __in BOOL fRepair, __out BOOTSTRAPPER_FEATURE_ACTION* pFeatureAction, __inout BOOL* pfDelta ) { HRESULT hr = S_OK; *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE; switch (requestedState) { case BOOTSTRAPPER_FEATURE_STATE_UNKNOWN: *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_NONE; break; case BOOTSTRAPPER_FEATURE_STATE_ABSENT: if (BOOTSTRAPPER_FEATURE_STATE_ABSENT != currentState) { *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REMOVE; } break; case BOOTSTRAPPER_FEATURE_STATE_ADVERTISED: if (BOOTSTRAPPER_FEATURE_STATE_ADVERTISED != currentState) { *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE; } else if (fRepair) { *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; } break; case BOOTSTRAPPER_FEATURE_STATE_LOCAL: if (BOOTSTRAPPER_FEATURE_STATE_LOCAL != currentState) { *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL; } else if (fRepair) { *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; } break; case BOOTSTRAPPER_FEATURE_STATE_SOURCE: if (BOOTSTRAPPER_FEATURE_STATE_SOURCE != currentState) { *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE; } else if (fRepair) { *pFeatureAction = BOOTSTRAPPER_FEATURE_ACTION_REINSTALL; } break; default: hr = E_UNEXPECTED; ExitOnRootFailure(hr, "Invalid state value."); } if (BOOTSTRAPPER_FEATURE_ACTION_NONE != *pFeatureAction) { *pfDelta = TRUE; } LExit: return hr; } static HRESULT EscapePropertyArgumentString( __in LPCWSTR wzProperty, __inout_z LPWSTR* psczEscapedValue, __in BOOL fZeroOnRealloc ) { HRESULT hr = S_OK; DWORD cch = 0; DWORD cchEscape = 0; LPCWSTR wzSource = NULL; LPWSTR wzTarget = NULL; // count characters to escape wzSource = wzProperty; while (*wzSource) { ++cch; if (L'\"' == *wzSource) { ++cchEscape; } ++wzSource; } // allocate target buffer hr = VariableStrAlloc(fZeroOnRealloc, psczEscapedValue, cch + cchEscape + 1); // character count, plus escape character count, plus null terminator ExitOnFailure(hr, "Failed to allocate string buffer."); // write to target buffer wzSource = wzProperty; wzTarget = *psczEscapedValue; while (*wzSource) { *wzTarget = *wzSource; if (L'\"' == *wzTarget) { ++wzTarget; *wzTarget = L'\"'; } ++wzSource; ++wzTarget; } *wzTarget = L'\0'; // add null terminator LExit: return hr; } static HRESULT ConcatFeatureActionProperties( __in BURN_PACKAGE* pPackage, __in BOOTSTRAPPER_FEATURE_ACTION* rgFeatureActions, __inout_z LPWSTR* psczArguments ) { HRESULT hr = S_OK; LPWSTR scz = NULL; LPWSTR sczAddLocal = NULL; LPWSTR sczAddSource = NULL; LPWSTR sczAddDefault = NULL; LPWSTR sczReinstall = NULL; LPWSTR sczAdvertise = NULL; LPWSTR sczRemove = NULL; // features for (DWORD i = 0; i < pPackage->Msi.cFeatures; ++i) { BURN_MSIFEATURE* pFeature = &pPackage->Msi.rgFeatures[i]; switch (rgFeatureActions[i]) { case BOOTSTRAPPER_FEATURE_ACTION_ADDLOCAL: if (sczAddLocal) { hr = StrAllocConcat(&sczAddLocal, L",", 0); ExitOnFailure(hr, "Failed to concat separator."); } hr = StrAllocConcat(&sczAddLocal, pFeature->sczId, 0); ExitOnFailure(hr, "Failed to concat feature."); break; case BOOTSTRAPPER_FEATURE_ACTION_ADDSOURCE: if (sczAddSource) { hr = StrAllocConcat(&sczAddSource, L",", 0); ExitOnFailure(hr, "Failed to concat separator."); } hr = StrAllocConcat(&sczAddSource, pFeature->sczId, 0); ExitOnFailure(hr, "Failed to concat feature."); break; case BOOTSTRAPPER_FEATURE_ACTION_ADDDEFAULT: if (sczAddDefault) { hr = StrAllocConcat(&sczAddDefault, L",", 0); ExitOnFailure(hr, "Failed to concat separator."); } hr = StrAllocConcat(&sczAddDefault, pFeature->sczId, 0); ExitOnFailure(hr, "Failed to concat feature."); break; case BOOTSTRAPPER_FEATURE_ACTION_REINSTALL: if (sczReinstall) { hr = StrAllocConcat(&sczReinstall, L",", 0); ExitOnFailure(hr, "Failed to concat separator."); } hr = StrAllocConcat(&sczReinstall, pFeature->sczId, 0); ExitOnFailure(hr, "Failed to concat feature."); break; case BOOTSTRAPPER_FEATURE_ACTION_ADVERTISE: if (sczAdvertise) { hr = StrAllocConcat(&sczAdvertise, L",", 0); ExitOnFailure(hr, "Failed to concat separator."); } hr = StrAllocConcat(&sczAdvertise, pFeature->sczId, 0); ExitOnFailure(hr, "Failed to concat feature."); break; case BOOTSTRAPPER_FEATURE_ACTION_REMOVE: if (sczRemove) { hr = StrAllocConcat(&sczRemove, L",", 0); ExitOnFailure(hr, "Failed to concat separator."); } hr = StrAllocConcat(&sczRemove, pFeature->sczId, 0); ExitOnFailure(hr, "Failed to concat feature."); break; } } if (sczAddLocal) { hr = StrAllocFormatted(&scz, L" ADDLOCAL=\"%s\"", sczAddLocal, 0); ExitOnFailure(hr, "Failed to format ADDLOCAL string."); hr = StrAllocConcatSecure(psczArguments, scz, 0); ExitOnFailure(hr, "Failed to concat argument string."); } if (sczAddSource) { hr = StrAllocFormatted(&scz, L" ADDSOURCE=\"%s\"", sczAddSource, 0); ExitOnFailure(hr, "Failed to format ADDSOURCE string."); hr = StrAllocConcatSecure(psczArguments, scz, 0); ExitOnFailure(hr, "Failed to concat argument string."); } if (sczAddDefault) { hr = StrAllocFormatted(&scz, L" ADDDEFAULT=\"%s\"", sczAddDefault, 0); ExitOnFailure(hr, "Failed to format ADDDEFAULT string."); hr = StrAllocConcatSecure(psczArguments, scz, 0); ExitOnFailure(hr, "Failed to concat argument string."); } if (sczReinstall) { hr = StrAllocFormatted(&scz, L" REINSTALL=\"%s\"", sczReinstall, 0); ExitOnFailure(hr, "Failed to format REINSTALL string."); hr = StrAllocConcatSecure(psczArguments, scz, 0); ExitOnFailure(hr, "Failed to concat argument string."); } if (sczAdvertise) { hr = StrAllocFormatted(&scz, L" ADVERTISE=\"%s\"", sczAdvertise, 0); ExitOnFailure(hr, "Failed to format ADVERTISE string."); hr = StrAllocConcatSecure(psczArguments, scz, 0); ExitOnFailure(hr, "Failed to concat argument string."); } if (sczRemove) { hr = StrAllocFormatted(&scz, L" REMOVE=\"%s\"", sczRemove, 0); ExitOnFailure(hr, "Failed to format REMOVE string."); hr = StrAllocConcatSecure(psczArguments, scz, 0); ExitOnFailure(hr, "Failed to concat argument string."); } LExit: ReleaseStr(scz); ReleaseStr(sczAddLocal); ReleaseStr(sczAddSource); ReleaseStr(sczAddDefault); ReleaseStr(sczReinstall); ReleaseStr(sczAdvertise); ReleaseStr(sczRemove); return hr; } static HRESULT ConcatPatchProperty( __in BURN_CACHE* pCache, __in BURN_PACKAGE* pPackage, __in BOOL fRollback, __inout_z LPWSTR* psczArguments ) { HRESULT hr = S_OK; LPWSTR sczCachedDirectory = NULL; LPWSTR sczMspPath = NULL; LPWSTR sczPatches = NULL; // If there are slipstream patch actions, build up their patch action. if (pPackage->Msi.cSlipstreamMspPackages) { for (DWORD i = 0; i < pPackage->Msi.cSlipstreamMspPackages; ++i) { BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + i; BURN_PACKAGE* pMspPackage = pSlipstreamMsp->pMspPackage; BURN_PAYLOAD* pMspPackagePayload = pMspPackage->payloads.rgItems[0].pPayload; BOOTSTRAPPER_ACTION_STATE patchExecuteAction = fRollback ? pSlipstreamMsp->rollback : pSlipstreamMsp->execute; if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL < patchExecuteAction) { hr = CacheGetCompletedPath(pCache, pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); hr = PathConcatRelativeToFullyQualifiedBase(sczCachedDirectory, pMspPackagePayload->sczFilePath, &sczMspPath); ExitOnFailure(hr, "Failed to build MSP path."); if (!sczPatches) { hr = StrAllocConcat(&sczPatches, L" PATCH=\"", 0); ExitOnFailure(hr, "Failed to prefix with PATCH property."); } else { hr = StrAllocConcat(&sczPatches, L";", 0); ExitOnFailure(hr, "Failed to semi-colon delimit patches."); } hr = StrAllocConcat(&sczPatches, sczMspPath, 0); ExitOnFailure(hr, "Failed to append patch path."); } } if (sczPatches) { hr = StrAllocConcat(&sczPatches, L"\"", 0); ExitOnFailure(hr, "Failed to close the quoted PATCH property."); hr = StrAllocConcatSecure(psczArguments, sczPatches, 0); ExitOnFailure(hr, "Failed to append PATCH property."); } } LExit: ReleaseStr(sczMspPath); ReleaseStr(sczCachedDirectory); ReleaseStr(sczPatches); return hr; } static void RegisterSourceDirectory( __in BURN_PACKAGE* pPackage, __in_z LPCWSTR wzMsiPath ) { HRESULT hr = S_OK; LPWSTR sczMsiDirectory = NULL; MSIINSTALLCONTEXT dwContext = pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED; hr = PathGetDirectory(wzMsiPath, &sczMsiDirectory); ExitOnFailure(hr, "Failed to get directory for path: %ls", wzMsiPath); hr = WiuSourceListAddSourceEx(pPackage->Msi.sczProductCode, NULL, dwContext, MSICODE_PRODUCT, sczMsiDirectory, 1); if (FAILED(hr)) { LogId(REPORT_VERBOSE, MSG_SOURCELIST_REGISTER, sczMsiDirectory, pPackage->Msi.sczProductCode, hr); ExitFunction(); } LExit: ReleaseStr(sczMsiDirectory); return; }