// 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" // internal function declarations static HRESULT ParsePayloadRefsFromXml( __in BURN_PACKAGE* pPackage, __in BURN_PAYLOADS* pPayloads, __in IXMLDOMNode* pixnPackage ); static HRESULT ParsePatchTargetCode( __in BURN_PACKAGES* pPackages, __in IXMLDOMNode* pixnBundle ); static HRESULT FindRollbackBoundaryById( __in BURN_PACKAGES* pPackages, __in_z LPCWSTR wzId, __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary ); // function definitions extern "C" HRESULT PackagesParseFromXml( __in BURN_PACKAGES* pPackages, __in BURN_PAYLOADS* pPayloads, __in IXMLDOMNode* pixnBundle ) { HRESULT hr = S_OK; IXMLDOMNodeList* pixnNodes = NULL; IXMLDOMNode* pixnNode = NULL; DWORD cNodes = 0; BSTR bstrNodeName = NULL; DWORD cMspPackages = 0; LPWSTR scz = NULL; BOOL fFoundXml = FALSE; // select rollback boundary nodes hr = XmlSelectNodes(pixnBundle, L"RollbackBoundary", &pixnNodes); ExitOnFailure(hr, "Failed to select rollback boundary nodes."); // get rollback boundary node count hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get rollback bundary node count."); if (cNodes) { // allocate memory for rollback boundaries pPackages->rgRollbackBoundaries = (BURN_ROLLBACK_BOUNDARY*)MemAlloc(sizeof(BURN_ROLLBACK_BOUNDARY) * cNodes, TRUE); ExitOnNull(pPackages->rgRollbackBoundaries, hr, E_OUTOFMEMORY, "Failed to allocate memory for rollback boundary structs."); pPackages->cRollbackBoundaries = cNodes; // parse rollback boundary elements for (DWORD i = 0; i < cNodes; ++i) { BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); ExitOnFailure(hr, "Failed to get next node."); // @Id hr = XmlGetAttributeEx(pixnNode, L"Id", &pRollbackBoundary->sczId); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Id."); // @Vital hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pRollbackBoundary->fVital); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Vital."); // @Transaction hr = XmlGetYesNoAttribute(pixnNode, L"Transaction", &pRollbackBoundary->fTransactionAuthored); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Transaction."); // @LogPathVariable hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pRollbackBoundary->sczLogPathVariable); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @LogPathVariable."); // prepare next iteration ReleaseNullObject(pixnNode); ReleaseNullBSTR(bstrNodeName); } } ReleaseNullObject(pixnNodes); // done with the RollbackBoundary elements. // select package nodes hr = XmlSelectNodes(pixnBundle, L"Chain/BundlePackage|Chain/ExePackage|Chain/MsiPackage|Chain/MspPackage|Chain/MsuPackage", &pixnNodes); ExitOnFailure(hr, "Failed to select package nodes."); // get package node count hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get package node count."); if (!cNodes) { ExitFunction1(hr = S_OK); } // allocate memory for packages pPackages->rgPackages = (BURN_PACKAGE*)MemAlloc(sizeof(BURN_PACKAGE) * cNodes, TRUE); ExitOnNull(pPackages->rgPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for package structs."); pPackages->cPackages = cNodes; // parse package elements for (DWORD i = 0; i < cNodes; ++i) { BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; hr = XmlNextElement(pixnNodes, &pixnNode, &bstrNodeName); ExitOnFailure(hr, "Failed to get next node."); // @Id hr = XmlGetAttributeEx(pixnNode, L"Id", &pPackage->sczId); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Id."); // @Cache hr = XmlGetAttributeEx(pixnNode, L"Cache", &scz); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Cache."); if (fFoundXml) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"remove", -1)) { pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_REMOVE; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"keep", -1)) { pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_KEEP; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"force", -1)) { pPackage->authoredCacheType = BOOTSTRAPPER_CACHE_TYPE_FORCE; } else { hr = E_UNEXPECTED; ExitOnRootFailure(hr, "Invalid cache type: %ls", scz); } } // @CacheId hr = XmlGetAttributeEx(pixnNode, L"CacheId", &pPackage->sczCacheId); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @CacheId."); // @Size hr = XmlGetAttributeUInt64(pixnNode, L"Size", &pPackage->qwSize); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Size."); // @InstallSize hr = XmlGetAttributeUInt64(pixnNode, L"InstallSize", &pPackage->qwInstallSize); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallSize."); // @PerMachine hr = XmlGetYesNoAttribute(pixnNode, L"PerMachine", &pPackage->fPerMachine); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @PerMachine."); // @Permanent hr = XmlGetYesNoAttribute(pixnNode, L"Permanent", &pPackage->fPermanent); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Permanent."); // @Vital hr = XmlGetYesNoAttribute(pixnNode, L"Vital", &pPackage->fVital); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Vital."); // @LogPathVariable hr = XmlGetAttributeEx(pixnNode, L"LogPathVariable", &pPackage->sczLogPathVariable); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @LogPathVariable."); // @RollbackLogPathVariable hr = XmlGetAttributeEx(pixnNode, L"RollbackLogPathVariable", &pPackage->sczRollbackLogPathVariable); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RollbackLogPathVariable."); if (pPackage->sczLogPathVariable && *pPackage->sczLogPathVariable) { // Format a suitable log path variable from the original package. hr = StrAllocFormatted(&pPackage->sczCompatibleLogPathVariable, L"%ls_Compatible", pPackage->sczLogPathVariable); ExitOnFailure(hr, "Failed to format log path variable for compatible package."); } // @InstallCondition hr = XmlGetAttributeEx(pixnNode, L"InstallCondition", &pPackage->sczInstallCondition); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallCondition."); // @RepairCondition hr = XmlGetAttributeEx(pixnNode, L"RepairCondition", &pPackage->sczRepairCondition); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RepairCondition."); // @RollbackBoundaryForward hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryForward", &scz); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RollbackBoundaryForward."); if (fFoundXml) { hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryForward); ExitOnFailure(hr, "Failed to find forward transaction boundary: %ls", scz); } // @RollbackBoundaryBackward hr = XmlGetAttributeEx(pixnNode, L"RollbackBoundaryBackward", &scz); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @RollbackBoundaryBackward."); if (fFoundXml) { hr = FindRollbackBoundaryById(pPackages, scz, &pPackage->pRollbackBoundaryBackward); ExitOnFailure(hr, "Failed to find backward transaction boundary: %ls", scz); } // read type specific attributes if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"BundlePackage", -1)) { pPackage->type = BURN_PACKAGE_TYPE_BUNDLE; hr = BundlePackageEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization ExitOnFailure(hr, "Failed to parse BUNDLE package."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"ExePackage", -1)) { pPackage->type = BURN_PACKAGE_TYPE_EXE; hr = ExeEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization ExitOnFailure(hr, "Failed to parse EXE package."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsiPackage", -1)) { pPackage->type = BURN_PACKAGE_TYPE_MSI; hr = MsiEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization ExitOnFailure(hr, "Failed to parse MSI package."); } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MspPackage", -1)) { pPackage->type = BURN_PACKAGE_TYPE_MSP; hr = MspEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization ExitOnFailure(hr, "Failed to parse MSP package."); ++cMspPackages; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, bstrNodeName, -1, L"MsuPackage", -1)) { pPackage->type = BURN_PACKAGE_TYPE_MSU; hr = MsuEngineParsePackageFromXml(pixnNode, pPackage); // TODO: Modularization ExitOnFailure(hr, "Failed to parse MSU package."); } else { ExitWithRootFailure(hr, E_UNEXPECTED, "Invalid package type: %ls", bstrNodeName); } if (!pPackage->fPermanent) { BOOL fUninstallable = TRUE; switch (pPackage->type) { case BURN_PACKAGE_TYPE_EXE: fUninstallable = pPackage->Exe.fUninstallable; break; case BURN_PACKAGE_TYPE_MSU: fUninstallable = FALSE; break; } if (!fUninstallable) { ExitWithRootFailure(hr, E_INVALIDDATA, "Non-permanent packages must be uninstallable."); } } pPackage->fCanAffectRegistration = !pPackage->fPermanent; // parse payload references hr = ParsePayloadRefsFromXml(pPackage, pPayloads, pixnNode); ExitOnFailure(hr, "Failed to parse payload references."); // parse dependency providers hr = DependencyParseProvidersFromXml(pPackage, pixnNode); ExitOnFailure(hr, "Failed to parse dependency providers."); // prepare next iteration ReleaseNullObject(pixnNode); ReleaseNullBSTR(bstrNodeName); } if (cMspPackages) { pPackages->rgPatchInfo = static_cast(MemAlloc(sizeof(MSIPATCHSEQUENCEINFOW) * cMspPackages, TRUE)); ExitOnNull(pPackages->rgPatchInfo, hr, E_OUTOFMEMORY, "Failed to allocate memory for MSP patch sequence information."); pPackages->rgPatchInfoToPackage = static_cast(MemAlloc(sizeof(BURN_PACKAGE*) * cMspPackages, TRUE)); ExitOnNull(pPackages->rgPatchInfoToPackage, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch sequence information to package lookup."); for (DWORD i = 0; i < pPackages->cPackages; ++i) { BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; if (BURN_PACKAGE_TYPE_MSP == pPackage->type) { pPackages->rgPatchInfo[pPackages->cPatchInfo].szPatchData = pPackage->Msp.sczApplicabilityXml; pPackages->rgPatchInfo[pPackages->cPatchInfo].ePatchDataType = MSIPATCH_DATATYPE_XMLBLOB; pPackages->rgPatchInfoToPackage[pPackages->cPatchInfo] = pPackage; ++pPackages->cPatchInfo; // Loop through all MSI packages seeing if any of them slipstream this MSP. for (DWORD j = 0; j < pPackages->cPackages; ++j) { BURN_PACKAGE* pMsiPackage = &pPackages->rgPackages[j]; if (BURN_PACKAGE_TYPE_MSI == pMsiPackage->type) { for (DWORD k = 0; k < pMsiPackage->Msi.cSlipstreamMspPackages; ++k) { if (pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k] && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k], -1)) { BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pMsiPackage->Msi.rgSlipstreamMsps + k; pSlipstreamMsp->pMspPackage = pPackage; pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX; ReleaseNullStr(pMsiPackage->Msi.rgsczSlipstreamMspPackageIds[k]); // we don't need the slipstream package id any longer so free it. } } } } } } } AssertSz(pPackages->cPatchInfo == cMspPackages, "Count of packages patch info should be equal to the number of MSP packages."); #if DEBUG // Loop through all MSI packages seeing if any of them are missing their slipstream MSP. for (DWORD i = 0; i < pPackages->cPackages; ++i) { BURN_PACKAGE* pPackage = &pPackages->rgPackages[i]; if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { for (DWORD k = 0; k < pPackage->Msi.cSlipstreamMspPackages; ++k) { if (pPackage->Msi.rgsczSlipstreamMspPackageIds[k]) { AssertSz(FALSE, "MSI slipstream MSP package doesn't exist."); } } } } #endif hr = ParsePatchTargetCode(pPackages, pixnBundle); ExitOnFailure(hr, "Failed to parse target product codes."); hr = S_OK; LExit: ReleaseObject(pixnNodes); ReleaseObject(pixnNode); ReleaseBSTR(bstrNodeName); ReleaseStr(scz); return hr; } extern "C" void PackageUninitialize( __in BURN_PACKAGE* pPackage ) { ReleaseStr(pPackage->sczId); ReleaseStr(pPackage->sczLogPathVariable); ReleaseStr(pPackage->sczRollbackLogPathVariable); ReleaseStr(pPackage->sczCompatibleLogPathVariable); ReleaseStr(pPackage->sczInstallCondition); ReleaseStr(pPackage->sczRepairCondition); ReleaseStr(pPackage->sczCacheId); if (pPackage->rgDependencyProviders) { for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) { DependencyUninitializeProvider(pPackage->rgDependencyProviders + i); } MemFree(pPackage->rgDependencyProviders); } ReleaseMem(pPackage->payloads.rgItems); switch (pPackage->type) { case BURN_PACKAGE_TYPE_BUNDLE: BundlePackageEnginePackageUninitialize(pPackage); // TODO: Modularization break; case BURN_PACKAGE_TYPE_EXE: ExeEnginePackageUninitialize(pPackage); // TODO: Modularization break; case BURN_PACKAGE_TYPE_MSI: MsiEnginePackageUninitialize(pPackage); // TODO: Modularization break; case BURN_PACKAGE_TYPE_MSP: MspEnginePackageUninitialize(pPackage); // TODO: Modularization break; case BURN_PACKAGE_TYPE_MSU: MsuEnginePackageUninitialize(pPackage); // TODO: Modularization break; } PackageUninitializeCompatible(&pPackage->compatiblePackage); } extern "C" void PackageUninitializeCompatible( __in BURN_COMPATIBLE_PACKAGE* pCompatiblePackage ) { ReleaseStr(pCompatiblePackage->compatibleEntry.sczId); ReleaseStr(pCompatiblePackage->compatibleEntry.sczName); ReleaseStr(pCompatiblePackage->compatibleEntry.sczProviderKey); ReleaseStr(pCompatiblePackage->compatibleEntry.sczVersion); ReleaseStr(pCompatiblePackage->sczCacheId); switch (pCompatiblePackage->type) { case BURN_PACKAGE_TYPE_MSI: ReleaseStr(pCompatiblePackage->Msi.sczVersion); ReleaseVerutilVersion(pCompatiblePackage->Msi.pVersion); break; } // clear struct memset(pCompatiblePackage, 0, sizeof(BURN_COMPATIBLE_PACKAGE)); } extern "C" void PackagesUninitialize( __in BURN_PACKAGES* pPackages ) { if (pPackages->rgRollbackBoundaries) { for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) { ReleaseStr(pPackages->rgRollbackBoundaries[i].sczId); ReleaseStr(pPackages->rgRollbackBoundaries[i].sczLogPath); ReleaseStr(pPackages->rgRollbackBoundaries[i].sczLogPathVariable); } MemFree(pPackages->rgRollbackBoundaries); } if (pPackages->rgPackages) { for (DWORD i = 0; i < pPackages->cPackages; ++i) { PackageUninitialize(pPackages->rgPackages + i); } MemFree(pPackages->rgPackages); } if (pPackages->rgPatchTargetCodes) { for (DWORD i = 0; i < pPackages->cPatchTargetCodes; ++i) { ReleaseStr(pPackages->rgPatchTargetCodes[i].sczTargetCode); } MemFree(pPackages->rgPatchTargetCodes); } ReleaseMem(pPackages->rgPatchInfo); ReleaseMem(pPackages->rgPatchInfoToPackage); // clear struct memset(pPackages, 0, sizeof(BURN_PACKAGES)); } extern "C" HRESULT PackageFindById( __in BURN_PACKAGES* pPackages, __in_z LPCWSTR wzId, __out BURN_PACKAGE** ppPackage ) { HRESULT hr = S_OK; BURN_PACKAGE* pPackage = NULL; for (DWORD i = 0; i < pPackages->cPackages; ++i) { pPackage = &pPackages->rgPackages[i]; if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pPackage->sczId, -1, wzId, -1)) { *ppPackage = pPackage; ExitFunction1(hr = S_OK); } } hr = E_NOTFOUND; LExit: return hr; } extern "C" HRESULT PackageFindRelatedById( __in BURN_RELATED_BUNDLES* pRelatedBundles, __in_z LPCWSTR wzId, __out BURN_PACKAGE** ppPackage ) { HRESULT hr = S_OK; BURN_RELATED_BUNDLE* pRelatedBundle = NULL; hr = RelatedBundleFindById(pRelatedBundles, wzId, &pRelatedBundle); *ppPackage = FAILED(hr) ? NULL : &pRelatedBundle->package; return hr; } /******************************************************************** PackageGetProperty - Determines if the property is defined and optionally copies the property value. Note: The caller must free psczValue if requested. Note: Returns E_NOTFOUND if the property was not defined or if the package does not support properties. *********************************************************************/ extern "C" HRESULT PackageGetProperty( __in const BURN_PACKAGE* pPackage, __in_z LPCWSTR wzProperty, __out_z_opt LPWSTR* psczValue ) { HRESULT hr = E_NOTFOUND; BURN_MSIPROPERTY* rgProperties = NULL; DWORD cProperties = 0; // For MSIs and MSPs, enumerate the properties looking for wzProperty. if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { rgProperties = pPackage->Msi.rgProperties; cProperties = pPackage->Msi.cProperties; } else if (BURN_PACKAGE_TYPE_MSP == pPackage->type) { rgProperties = pPackage->Msp.rgProperties; cProperties = pPackage->Msp.cProperties; } for (DWORD i = 0; i < cProperties; ++i) { const BURN_MSIPROPERTY* pProperty = &rgProperties[i]; if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, pProperty->sczId, -1, wzProperty, -1)) { if (psczValue) { hr = StrAllocString(psczValue, pProperty->sczValue, 0); ExitOnFailure(hr, "Failed to copy the property value."); } ExitFunction1(hr = S_OK); } } LExit: return hr; } extern "C" HRESULT PackageFindRollbackBoundaryById( __in BURN_PACKAGES* pPackages, __in_z LPCWSTR wzId, __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary ) { HRESULT hr = S_OK; BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) { pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1)) { *ppRollbackBoundary = pRollbackBoundary; ExitFunction1(hr = S_OK); } } hr = E_NOTFOUND; LExit: return hr; } // internal function declarations static HRESULT ParsePayloadRefsFromXml( __in BURN_PACKAGE* pPackage, __in BURN_PAYLOADS* pPayloads, __in IXMLDOMNode* pixnPackage ) { HRESULT hr = S_OK; IXMLDOMNodeList* pixnNodes = NULL; IXMLDOMNode* pixnNode = NULL; DWORD cNodes = 0; LPWSTR sczId = NULL; // select package nodes hr = XmlSelectNodes(pixnPackage, L"PayloadRef", &pixnNodes); ExitOnFailure(hr, "Failed to select package nodes."); // get package node count hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get package node count."); if (!cNodes) { ExitFunction1(hr = S_OK); } // allocate memory for payload pointers pPackage->payloads.rgItems = (BURN_PAYLOAD_GROUP_ITEM*)MemAlloc(sizeof(BURN_PAYLOAD_GROUP_ITEM) * cNodes, TRUE); ExitOnNull(pPackage->payloads.rgItems, hr, E_OUTOFMEMORY, "Failed to allocate memory for package payloads."); pPackage->payloads.cItems = cNodes; // parse package elements for (DWORD i = 0; i < cNodes; ++i) { BURN_PAYLOAD_GROUP_ITEM* pPackagePayload = pPackage->payloads.rgItems + i; hr = XmlNextElement(pixnNodes, &pixnNode, NULL); ExitOnFailure(hr, "Failed to get next node."); // @Id hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId); ExitOnFailure(hr, "Failed to get Id attribute."); // find payload hr = PayloadFindById(pPayloads, sczId, &pPackagePayload->pPayload); ExitOnFailure(hr, "Failed to find payload."); pPackage->payloads.qwTotalSize += pPackagePayload->pPayload->qwFileSize; // prepare next iteration ReleaseNullObject(pixnNode); } hr = S_OK; LExit: ReleaseObject(pixnNodes); ReleaseObject(pixnNode); ReleaseStr(sczId); return hr; } static HRESULT ParsePatchTargetCode( __in BURN_PACKAGES* pPackages, __in IXMLDOMNode* pixnBundle ) { HRESULT hr = S_OK; IXMLDOMNodeList* pixnNodes = NULL; IXMLDOMNode* pixnNode = NULL; DWORD cNodes = 0; BOOL fProduct; hr = XmlSelectNodes(pixnBundle, L"PatchTargetCode", &pixnNodes); ExitOnFailure(hr, "Failed to select PatchTargetCode nodes."); hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get PatchTargetCode node count."); if (!cNodes) { ExitFunction1(hr = S_OK); } pPackages->rgPatchTargetCodes = (BURN_PATCH_TARGETCODE*)MemAlloc(sizeof(BURN_PATCH_TARGETCODE) * cNodes, TRUE); ExitOnNull(pPackages->rgPatchTargetCodes, hr, E_OUTOFMEMORY, "Failed to allocate memory for patch targetcodes."); pPackages->cPatchTargetCodes = cNodes; for (DWORD i = 0; i < cNodes; ++i) { BURN_PATCH_TARGETCODE* pTargetCode = pPackages->rgPatchTargetCodes + i; hr = XmlNextElement(pixnNodes, &pixnNode, NULL); ExitOnFailure(hr, "Failed to get next node."); hr = XmlGetAttributeEx(pixnNode, L"TargetCode", &pTargetCode->sczTargetCode); ExitOnFailure(hr, "Failed to get @TargetCode attribute."); hr = XmlGetYesNoAttribute(pixnNode, L"Product", &fProduct); if (E_NOTFOUND == hr) { fProduct = FALSE; hr = S_OK; } ExitOnFailure(hr, "Failed to get @Product."); pTargetCode->type = fProduct ? BURN_PATCH_TARGETCODE_TYPE_PRODUCT : BURN_PATCH_TARGETCODE_TYPE_UPGRADE; // prepare next iteration ReleaseNullObject(pixnNode); } LExit: ReleaseObject(pixnNode); ReleaseObject(pixnNodes); return hr; } static HRESULT FindRollbackBoundaryById( __in BURN_PACKAGES* pPackages, __in_z LPCWSTR wzId, __out BURN_ROLLBACK_BOUNDARY** ppRollbackBoundary ) { HRESULT hr = S_OK; BURN_ROLLBACK_BOUNDARY* pRollbackBoundary = NULL; for (DWORD i = 0; i < pPackages->cRollbackBoundaries; ++i) { pRollbackBoundary = &pPackages->rgRollbackBoundaries[i]; if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pRollbackBoundary->sczId, -1, wzId, -1)) { *ppRollbackBoundary = pRollbackBoundary; ExitFunction1(hr = S_OK); } } hr = E_NOTFOUND; LExit: return hr; }