// 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" // prototypes static HRESULT ParsePackagesFromXml( __in BAL_INFO_PACKAGES* pPackages, __in IXMLDOMDocument* pixdManifest ); static HRESULT ParseBalPackageInfoFromXml( __in BAL_INFO_PACKAGES* pPackages, __in IXMLDOMDocument* pixdManifest ); DAPI_(HRESULT) BalInfoParseFromXml( __in BAL_INFO_BUNDLE* pBundle, __in IXMLDOMDocument* pixdManifest ) { HRESULT hr = S_OK; IXMLDOMNode* pNode = NULL; hr = XmlSelectSingleNode(pixdManifest, L"/BootstrapperApplicationData/WixBundleProperties", &pNode); ExitOnFailure(hr, "Failed to select bundle information."); if (S_OK == hr) { hr = XmlGetYesNoAttribute(pNode, L"PerMachine", &pBundle->fPerMachine); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to read bundle information per-machine."); } hr = XmlGetAttributeEx(pNode, L"DisplayName", &pBundle->sczName); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to read bundle information display name."); } hr = XmlGetAttributeEx(pNode, L"LogPathVariable", &pBundle->sczLogVariable); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to read bundle information log path variable."); } } hr = ParsePackagesFromXml(&pBundle->packages, pixdManifest); BalExitOnFailure(hr, "Failed to parse package information from bootstrapper application data."); hr = ParseBalPackageInfoFromXml(&pBundle->packages, pixdManifest); BalExitOnFailure(hr, "Failed to parse bal package information from bootstrapper application data."); LExit: ReleaseObject(pNode); return hr; } DAPI_(HRESULT) BalInfoAddRelatedBundleAsPackage( __in BAL_INFO_PACKAGES* pPackages, __in LPCWSTR wzId, __in BOOTSTRAPPER_RELATION_TYPE relationType, __in BOOL /*fPerMachine*/, __out_opt BAL_INFO_PACKAGE** ppPackage ) { HRESULT hr = S_OK; BAL_INFO_PACKAGE_TYPE type = BAL_INFO_PACKAGE_TYPE_UNKNOWN; BAL_INFO_PACKAGE* pPackage = NULL; // Ensure we have a supported relation type. switch (relationType) { case BOOTSTRAPPER_RELATION_ADDON: type = BAL_INFO_PACKAGE_TYPE_BUNDLE_ADDON; break; case BOOTSTRAPPER_RELATION_PATCH: type = BAL_INFO_PACKAGE_TYPE_BUNDLE_PATCH; break; case BOOTSTRAPPER_RELATION_UPGRADE: type = BAL_INFO_PACKAGE_TYPE_BUNDLE_UPGRADE; break; default: ExitOnFailure(hr = E_INVALIDARG, "Unknown related bundle type: %u", relationType); } // Check to see if the bundle is already in the list of packages. for (DWORD i = 0; i < pPackages->cPackages; ++i) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzId, -1, pPackages->rgPackages[i].sczId, -1)) { ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)); } } // Add the related bundle as a package. hr = MemEnsureArraySize(reinterpret_cast(&pPackages->rgPackages), pPackages->cPackages + 1, sizeof(BAL_INFO_PACKAGE), 2); ExitOnFailure(hr, "Failed to allocate memory for related bundle package information."); pPackage = pPackages->rgPackages + pPackages->cPackages; ++pPackages->cPackages; hr = StrAllocString(&pPackage->sczId, wzId, 0); ExitOnFailure(hr, "Failed to copy related bundle package id."); pPackage->type = type; // TODO: try to look up the DisplayName and Description in Add/Remove Programs with the wzId. if (ppPackage) { *ppPackage = pPackage; } LExit: return hr; } DAPI_(HRESULT) BalInfoFindPackageById( __in BAL_INFO_PACKAGES* pPackages, __in LPCWSTR wzId, __out BAL_INFO_PACKAGE** ppPackage ) { *ppPackage = NULL; for (DWORD i = 0; i < pPackages->cPackages; ++i) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzId, -1, pPackages->rgPackages[i].sczId, -1)) { *ppPackage = pPackages->rgPackages + i; break; } } return *ppPackage ? S_OK : E_NOTFOUND; } DAPI_(void) BalInfoUninitialize( __in BAL_INFO_BUNDLE* pBundle ) { for (DWORD i = 0; i < pBundle->packages.cPackages; ++i) { ReleaseStr(pBundle->packages.rgPackages[i].sczDisplayName); ReleaseStr(pBundle->packages.rgPackages[i].sczDescription); ReleaseStr(pBundle->packages.rgPackages[i].sczId); ReleaseStr(pBundle->packages.rgPackages[i].sczDisplayInternalUICondition); ReleaseStr(pBundle->packages.rgPackages[i].sczProductCode); ReleaseStr(pBundle->packages.rgPackages[i].sczUpgradeCode); ReleaseStr(pBundle->packages.rgPackages[i].sczVersion); ReleaseStr(pBundle->packages.rgPackages[i].sczInstallCondition); ReleaseStr(pBundle->packages.rgPackages[i].sczPrereqLicenseFile); ReleaseStr(pBundle->packages.rgPackages[i].sczPrereqLicenseUrl); } ReleaseMem(pBundle->packages.rgPackages); ReleaseStr(pBundle->sczName); ReleaseStr(pBundle->sczLogVariable); memset(pBundle, 0, sizeof(BAL_INFO_BUNDLE)); } static HRESULT ParsePackagesFromXml( __in BAL_INFO_PACKAGES* pPackages, __in IXMLDOMDocument* pixdManifest ) { HRESULT hr = S_OK; IXMLDOMNodeList* pNodeList = NULL; IXMLDOMNode* pNode = NULL; BAL_INFO_PACKAGE* prgPackages = NULL; DWORD cPackages = 0; LPWSTR scz = NULL; hr = XmlSelectNodes(pixdManifest, L"/BootstrapperApplicationData/WixPackageProperties", &pNodeList); ExitOnFailure(hr, "Failed to select all packages."); hr = pNodeList->get_length(reinterpret_cast(&cPackages)); ExitOnFailure(hr, "Failed to get the package count."); prgPackages = static_cast(MemAlloc(sizeof(BAL_INFO_PACKAGE) * cPackages, TRUE)); ExitOnNull(prgPackages, hr, E_OUTOFMEMORY, "Failed to allocate memory for packages."); DWORD iPackage = 0; while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, NULL))) { hr = XmlGetAttributeEx(pNode, L"Package", &prgPackages[iPackage].sczId); ExitOnFailure(hr, "Failed to get package identifier for package."); hr = XmlGetAttributeEx(pNode, L"DisplayName", &prgPackages[iPackage].sczDisplayName); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get display name for package."); } hr = XmlGetAttributeEx(pNode, L"Description", &prgPackages[iPackage].sczDescription); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get description for package."); } hr = XmlGetAttributeEx(pNode, L"PackageType", &scz); ExitOnFailure(hr, "Failed to get package type for package."); if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, L"Exe", -1, scz, -1)) { prgPackages[iPackage].type = BAL_INFO_PACKAGE_TYPE_EXE; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, L"Msi", -1, scz, -1)) { prgPackages[iPackage].type = BAL_INFO_PACKAGE_TYPE_MSI; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, L"Msp", -1, scz, -1)) { prgPackages[iPackage].type = BAL_INFO_PACKAGE_TYPE_MSP; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, L"Msu", -1, scz, -1)) { prgPackages[iPackage].type = BAL_INFO_PACKAGE_TYPE_MSU; } hr = XmlGetYesNoAttribute(pNode, L"Permanent", &prgPackages[iPackage].fPermanent); ExitOnFailure(hr, "Failed to get permanent setting for package."); hr = XmlGetYesNoAttribute(pNode, L"Vital", &prgPackages[iPackage].fVital); ExitOnFailure(hr, "Failed to get vital setting for package."); hr = XmlGetAttributeEx(pNode, L"ProductCode", &prgPackages[iPackage].sczProductCode); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get product code for package."); } hr = XmlGetAttributeEx(pNode, L"UpgradeCode", &prgPackages[iPackage].sczUpgradeCode); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get upgrade code for package."); } hr = XmlGetAttributeEx(pNode, L"Version", &prgPackages[iPackage].sczVersion); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get version for package."); } hr = XmlGetAttributeEx(pNode, L"InstallCondition", &prgPackages[iPackage].sczInstallCondition); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get install condition for package."); } hr = XmlGetAttributeEx(pNode, L"Cache", &scz); ExitOnFailure(hr, "Failed to get cache type for package."); if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, scz, -1, L"remove", -1)) { prgPackages[iPackage].cacheType = BOOTSTRAPPER_CACHE_TYPE_REMOVE; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, scz, -1, L"keep", -1)) { prgPackages[iPackage].cacheType = BOOTSTRAPPER_CACHE_TYPE_KEEP; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, scz, -1, L"force", -1)) { prgPackages[iPackage].cacheType = BOOTSTRAPPER_CACHE_TYPE_FORCE; } ++iPackage; ReleaseNullObject(pNode); } ExitOnFailure(hr, "Failed to parse all package property elements."); if (S_FALSE == hr) { hr = S_OK; } pPackages->cPackages = cPackages; pPackages->rgPackages = prgPackages; prgPackages = NULL; LExit: ReleaseStr(scz); ReleaseMem(prgPackages); ReleaseObject(pNode); ReleaseObject(pNodeList); return hr; } static HRESULT ParseBalPackageInfoFromXml( __in BAL_INFO_PACKAGES* pPackages, __in IXMLDOMDocument* pixdManifest ) { HRESULT hr = S_OK; IXMLDOMNodeList* pNodeList = NULL; IXMLDOMNode* pNode = NULL; LPWSTR scz = NULL; BAL_INFO_PACKAGE* pPackage = NULL; hr = XmlSelectNodes(pixdManifest, L"/BootstrapperApplicationData/WixBalPackageInfo", &pNodeList); ExitOnFailure(hr, "Failed to select all packages."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, NULL))) { hr = XmlGetAttributeEx(pNode, L"PackageId", &scz); ExitOnFailure(hr, "Failed to get package identifier for WixBalPackageInfo."); hr = BalInfoFindPackageById(pPackages, scz, &pPackage); ExitOnFailure(hr, "Failed to find package specified in WixBalPackageInfo: %ls", scz); hr = XmlGetAttributeEx(pNode, L"DisplayInternalUICondition", &pPackage->sczDisplayInternalUICondition); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get DisplayInternalUICondition setting for package."); } ReleaseNullObject(pNode); } ExitOnFailure(hr, "Failed to parse all WixBalPackageInfo elements."); hr = XmlSelectNodes(pixdManifest, L"/BootstrapperApplicationData/WixMbaPrereqInformation", &pNodeList); ExitOnFailure(hr, "Failed to select all packages."); while (S_OK == (hr = XmlNextElement(pNodeList, &pNode, NULL))) { hr = XmlGetAttributeEx(pNode, L"PackageId", &scz); ExitOnFailure(hr, "Failed to get package identifier for WixMbaPrereqInformation."); hr = BalInfoFindPackageById(pPackages, scz, &pPackage); ExitOnFailure(hr, "Failed to find package specified in WixMbaPrereqInformation: %ls", scz); pPackage->fPrereqPackage = TRUE; hr = XmlGetAttributeEx(pNode, L"LicenseFile", &pPackage->sczPrereqLicenseFile); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get LicenseFile setting for prereq package."); } hr = XmlGetAttributeEx(pNode, L"LicenseUrl", &pPackage->sczPrereqLicenseUrl); if (E_NOTFOUND != hr) { ExitOnFailure(hr, "Failed to get LicenseUrl setting for prereq package."); } ReleaseNullObject(pNode); } ExitOnFailure(hr, "Failed to parse all WixMbaPrereqInformation elements."); if (S_FALSE == hr) { hr = S_OK; } LExit: ReleaseStr(scz); ReleaseObject(pNode); ReleaseObject(pNodeList); return hr; }