// 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 const LPCWSTR REGISTRY_RUN_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; const LPCWSTR REGISTRY_RUN_ONCE_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"; const LPCWSTR REGISTRY_BUNDLE_DISPLAY_ICON = L"DisplayIcon"; const LPCWSTR REGISTRY_BUNDLE_DISPLAY_VERSION = L"DisplayVersion"; const LPCWSTR REGISTRY_BUNDLE_ESTIMATED_SIZE = L"EstimatedSize"; const LPCWSTR REGISTRY_BUNDLE_INSTALL_DATE = L"InstallDate"; const LPCWSTR REGISTRY_BUNDLE_PUBLISHER = L"Publisher"; const LPCWSTR REGISTRY_BUNDLE_HELP_LINK = L"HelpLink"; const LPCWSTR REGISTRY_BUNDLE_HELP_TELEPHONE = L"HelpTelephone"; const LPCWSTR REGISTRY_BUNDLE_URL_INFO_ABOUT = L"URLInfoAbout"; const LPCWSTR REGISTRY_BUNDLE_URL_UPDATE_INFO = L"URLUpdateInfo"; const LPCWSTR REGISTRY_BUNDLE_PARENT_DISPLAY_NAME = L"ParentDisplayName"; const LPCWSTR REGISTRY_BUNDLE_PARENT_KEY_NAME = L"ParentKeyName"; const LPCWSTR REGISTRY_BUNDLE_COMMENTS = L"Comments"; const LPCWSTR REGISTRY_BUNDLE_CONTACT = L"Contact"; const LPCWSTR REGISTRY_BUNDLE_NO_MODIFY = L"NoModify"; const LPCWSTR REGISTRY_BUNDLE_MODIFY_PATH = L"ModifyPath"; const LPCWSTR REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY = L"NoElevateOnModify"; const LPCWSTR REGISTRY_BUNDLE_NO_REMOVE = L"NoRemove"; const LPCWSTR REGISTRY_BUNDLE_SYSTEM_COMPONENT = L"SystemComponent"; const LPCWSTR REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING = L"QuietUninstallString"; const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString"; const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine"; const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor"; const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor"; const LPCWSTR SWIDTAG_FOLDER = L"swidtag"; const LPCWSTR REGISTRY_BUNDLE_VARIABLE_KEY = L"variables"; // internal function declarations static HRESULT ParseSoftwareTagsFromXml( __in IXMLDOMNode* pixnRegistrationNode, __out BURN_SOFTWARE_TAG** prgSoftwareTags, __out DWORD* pcSoftwareTags ); static HRESULT SetPaths( __in BURN_REGISTRATION* pRegistration, __in BURN_CACHE* pCache ); static HRESULT GetBundleManufacturer( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __out_z LPWSTR* psczBundleManufacturer ); static HRESULT GetBundleInProgressName( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __out_z LPWSTR* psczBundleName ); static HRESULT GetBundleName( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __out_z LPWSTR* psczBundleName ); static HRESULT EnsureRegistrationVariable( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in_z LPCWSTR wzDefaultValue, __out_z LPWSTR* psczValue ); static HRESULT UpdateResumeMode( __in BURN_REGISTRATION* pRegistration, __in HKEY hkRegistration, __in BURN_RESUME_MODE resumeMode, __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType, __in BOOL fRestartInitiated ); static HRESULT FormatUpdateRegistrationKey( __in BURN_REGISTRATION* pRegistration, __out_z LPWSTR* psczKey ); static HRESULT WriteSoftwareTags( __in BURN_VARIABLES* pVariables, __in BURN_SOFTWARE_TAGS* pSoftwareTags ); static HRESULT RemoveSoftwareTags( __in BURN_VARIABLES* pVariables, __in BURN_SOFTWARE_TAGS* pSoftwareTags ); static HRESULT WriteUpdateRegistration( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables ); static HRESULT RemoveUpdateRegistration( __in BURN_REGISTRATION* pRegistration ); static HRESULT RegWriteStringVariable( __in HKEY hkKey, __in BURN_VARIABLES* pVariables, __in LPCWSTR wzVariable, __in LPCWSTR wzName ); static HRESULT UpdateBundleNameRegistration( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __in HKEY hkRegistration, __in BOOL fInProgressRegistration ); static HRESULT UpdateEstimatedSize( __in HKEY hkRegistration, __in DWORD64 qwEstimatedSize ); static BOOL IsWuRebootPending(); static BOOL IsRegistryRebootPending(); // function definitions /******************************************************************* RegistrationParseFromXml - Parses registration information from manifest. *******************************************************************/ extern "C" HRESULT RegistrationParseFromXml( __in BURN_REGISTRATION* pRegistration, __in BURN_CACHE* pCache, __in IXMLDOMNode* pixnBundle ) { HRESULT hr = S_OK; IXMLDOMNode* pixnRegistrationNode = NULL; IXMLDOMNode* pixnArpNode = NULL; IXMLDOMNode* pixnUpdateNode = NULL; LPWSTR scz = NULL; BOOL fFoundXml = FALSE; // select registration node hr = XmlSelectSingleNode(pixnBundle, L"Registration", &pixnRegistrationNode); ExitOnRequiredXmlQueryFailure(hr, "Failed to select registration node."); // @Id hr = XmlGetAttributeEx(pixnRegistrationNode, L"Id", &pRegistration->sczId); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Id."); // @Tag hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Tag."); hr = BundlePackageEngineParseRelatedCodes(pixnBundle, &pRegistration->rgsczDetectCodes, &pRegistration->cDetectCodes, &pRegistration->rgsczUpgradeCodes, &pRegistration->cUpgradeCodes, &pRegistration->rgsczAddonCodes, &pRegistration->cAddonCodes, &pRegistration->rgsczPatchCodes, &pRegistration->cPatchCodes); ExitOnFailure(hr, "Failed to parse related bundles"); // @Version hr = XmlGetAttributeEx(pixnRegistrationNode, L"Version", &scz); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Version."); hr = VerParseVersion(scz, 0, FALSE, &pRegistration->pVersion); ExitOnFailure(hr, "Failed to parse @Version: %ls", scz); if (pRegistration->pVersion->fInvalid) { LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz); } // @ProviderKey hr = XmlGetAttributeEx(pixnRegistrationNode, L"ProviderKey", &pRegistration->sczProviderKey); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @ProviderKey."); // @ExecutableName hr = XmlGetAttributeEx(pixnRegistrationNode, L"ExecutableName", &pRegistration->sczExecutableName); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @ExecutableName."); // @PerMachine hr = XmlGetYesNoAttribute(pixnRegistrationNode, L"PerMachine", &pRegistration->fPerMachine); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @PerMachine."); // select ARP node hr = XmlSelectSingleNode(pixnRegistrationNode, L"Arp", &pixnArpNode); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to select ARP node."); if (fFoundXml) { // @DisplayName hr = XmlGetAttributeEx(pixnArpNode, L"DisplayName", &pRegistration->sczDisplayName); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @DisplayName."); // @InProgressDisplayName hr = XmlGetAttributeEx(pixnArpNode, L"InProgressDisplayName", &pRegistration->sczInProgressDisplayName); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InProgressDisplayName."); // @DisplayVersion hr = XmlGetAttributeEx(pixnArpNode, L"DisplayVersion", &pRegistration->sczDisplayVersion); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @DisplayVersion."); // @Publisher hr = XmlGetAttributeEx(pixnArpNode, L"Publisher", &pRegistration->sczPublisher); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Publisher."); // @HelpLink hr = XmlGetAttributeEx(pixnArpNode, L"HelpLink", &pRegistration->sczHelpLink); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @HelpLink."); // @HelpTelephone hr = XmlGetAttributeEx(pixnArpNode, L"HelpTelephone", &pRegistration->sczHelpTelephone); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @HelpTelephone."); // @AboutUrl hr = XmlGetAttributeEx(pixnArpNode, L"AboutUrl", &pRegistration->sczAboutUrl); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @AboutUrl."); // @UpdateUrl hr = XmlGetAttributeEx(pixnArpNode, L"UpdateUrl", &pRegistration->sczUpdateUrl); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @UpdateUrl."); // @ParentDisplayName hr = XmlGetAttributeEx(pixnArpNode, L"ParentDisplayName", &pRegistration->sczParentDisplayName); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @ParentDisplayName."); // @Comments hr = XmlGetAttributeEx(pixnArpNode, L"Comments", &pRegistration->sczComments); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Comments."); // @Contact hr = XmlGetAttributeEx(pixnArpNode, L"Contact", &pRegistration->sczContact); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Contact."); // @DisableModify hr = XmlGetAttributeEx(pixnArpNode, L"DisableModify", &scz); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @DisableModify."); if (fFoundXml) { if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"button", -1)) { pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE_BUTTON; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"yes", -1)) { pRegistration->modify = BURN_REGISTRATION_MODIFY_DISABLE; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"no", -1)) { pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED; } else { ExitWithRootFailure(hr, E_UNEXPECTED, "Invalid modify disabled type: %ls", scz); } } else { pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED; } // @DisableRemove hr = XmlGetYesNoAttribute(pixnArpNode, L"DisableRemove", &pRegistration->fNoRemove); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @DisableRemove."); } if (pRegistration->fNoRemove && BURN_REGISTRATION_MODIFY_ENABLED != pRegistration->modify) { pRegistration->fForceSystemComponent = TRUE; } hr = ParseSoftwareTagsFromXml(pixnRegistrationNode, &pRegistration->softwareTags.rgSoftwareTags, &pRegistration->softwareTags.cSoftwareTags); ExitOnFailure(hr, "Failed to parse software tag."); // select Update node hr = XmlSelectSingleNode(pixnRegistrationNode, L"Update", &pixnUpdateNode); ExitOnOptionalXmlQueryFailure(hr, pRegistration->update.fRegisterUpdate, "Failed to select Update node."); if (pRegistration->update.fRegisterUpdate) { // @Manufacturer hr = XmlGetAttributeEx(pixnUpdateNode, L"Manufacturer", &pRegistration->update.sczManufacturer); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Manufacturer."); // @Department hr = XmlGetAttributeEx(pixnUpdateNode, L"Department", &pRegistration->update.sczDepartment); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @Department."); // @ProductFamily hr = XmlGetAttributeEx(pixnUpdateNode, L"ProductFamily", &pRegistration->update.sczProductFamily); ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @ProductFamily."); // @Name hr = XmlGetAttributeEx(pixnUpdateNode, L"Name", &pRegistration->update.sczName); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Name."); // @Classification hr = XmlGetAttributeEx(pixnUpdateNode, L"Classification", &pRegistration->update.sczClassification); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Classification."); } hr = SetPaths(pRegistration, pCache); ExitOnFailure(hr, "Failed to set registration paths."); LExit: ReleaseObject(pixnRegistrationNode); ReleaseObject(pixnArpNode); ReleaseObject(pixnUpdateNode); ReleaseStr(scz); return hr; } /******************************************************************* RegistrationUninitialize - *******************************************************************/ extern "C" void RegistrationUninitialize( __in BURN_REGISTRATION* pRegistration ) { ReleaseStr(pRegistration->sczId); ReleaseStr(pRegistration->sczTag); for (DWORD i = 0; i < pRegistration->cDetectCodes; ++i) { ReleaseStr(pRegistration->rgsczDetectCodes[i]); } ReleaseMem(pRegistration->rgsczDetectCodes); for (DWORD i = 0; i < pRegistration->cUpgradeCodes; ++i) { ReleaseStr(pRegistration->rgsczUpgradeCodes[i]); } ReleaseMem(pRegistration->rgsczUpgradeCodes); for (DWORD i = 0; i < pRegistration->cAddonCodes; ++i) { ReleaseStr(pRegistration->rgsczAddonCodes[i]); } ReleaseMem(pRegistration->rgsczAddonCodes); for (DWORD i = 0; i < pRegistration->cPatchCodes; ++i) { ReleaseStr(pRegistration->rgsczPatchCodes[i]); } ReleaseMem(pRegistration->rgsczPatchCodes); ReleaseStr(pRegistration->sczProviderKey); ReleaseStr(pRegistration->sczExecutableName); ReleaseStr(pRegistration->sczRegistrationKey); ReleaseStr(pRegistration->sczCacheExecutablePath); ReleaseStr(pRegistration->sczResumeCommandLine); ReleaseStr(pRegistration->sczStateFile); ReleaseStr(pRegistration->sczDisplayName); ReleaseStr(pRegistration->sczInProgressDisplayName); ReleaseStr(pRegistration->sczDisplayVersion); ReleaseStr(pRegistration->sczPublisher); ReleaseStr(pRegistration->sczHelpLink); ReleaseStr(pRegistration->sczHelpTelephone); ReleaseStr(pRegistration->sczAboutUrl); ReleaseStr(pRegistration->sczUpdateUrl); ReleaseStr(pRegistration->sczParentDisplayName); ReleaseStr(pRegistration->sczComments); ReleaseStr(pRegistration->sczContact); ReleaseStr(pRegistration->update.sczManufacturer); ReleaseStr(pRegistration->update.sczDepartment); ReleaseStr(pRegistration->update.sczProductFamily); ReleaseStr(pRegistration->update.sczName); ReleaseStr(pRegistration->update.sczClassification); if (pRegistration->softwareTags.rgSoftwareTags) { for (DWORD i = 0; i < pRegistration->softwareTags.cSoftwareTags; ++i) { ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczFilename); ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczRegid); ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczPath); ReleaseStr(pRegistration->softwareTags.rgSoftwareTags[i].sczTag); } MemFree(pRegistration->softwareTags.rgSoftwareTags); } ReleaseStr(pRegistration->sczDetectedProviderKeyBundleId); ReleaseStr(pRegistration->sczBundlePackageAncestors); RelatedBundlesUninitialize(&pRegistration->relatedBundles); if (pRegistration->rgDependents) { ReleaseDependencyArray(pRegistration->rgDependents, pRegistration->cDependents); } // clear struct memset(pRegistration, 0, sizeof(BURN_REGISTRATION)); } /******************************************************************* RegistrationSetVariables - Initializes bundle variables that map to registration entities. *******************************************************************/ extern "C" HRESULT RegistrationSetVariables( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables ) { HRESULT hr = S_OK; LPWSTR scz = NULL; // Ensure the registration bundle name is updated. hr = GetBundleInProgressName(pRegistration, pVariables, &scz); ExitOnFailure(hr, "Failed to initialize bundle name."); hr = GetBundleName(pRegistration, pVariables, &scz); ExitOnFailure(hr, "Failed to initialize bundle name."); hr = GetBundleManufacturer(pRegistration, pVariables, &scz); ExitOnFailure(hr, "Failed to initialize bundle manufacturer."); hr = VariableSetString(pVariables, BURN_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey, TRUE, FALSE); ExitOnFailure(hr, "Failed to overwrite the bundle provider key built-in variable."); hr = VariableSetString(pVariables, BURN_BUNDLE_TAG, pRegistration->sczTag, TRUE, FALSE); ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->pVersion, TRUE); ExitOnFailure(hr, "Failed to overwrite the bundle version built-in variable."); LExit: ReleaseStr(scz); return hr; } /******************************************************************* RegistrationSetDynamicVariables - Initializes bundle variables that map to registration entities that can change during execution. *******************************************************************/ extern "C" HRESULT RegistrationSetDynamicVariables( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables ) { HRESULT hr = S_OK; LONGLONG llInstalled = 0; // Detect if bundle is already installed. hr = RegistrationDetectInstalled(pRegistration); ExitOnFailure(hr, "Failed to detect bundle install state."); llInstalled = BOOTSTRAPPER_REGISTRATION_TYPE_FULL == pRegistration->detectedRegistrationType ? 1 : 0; hr = VariableSetNumeric(pVariables, BURN_BUNDLE_INSTALLED, llInstalled, TRUE); ExitOnFailure(hr, "Failed to set the bundle installed built-in variable."); hr = VariableSetNumeric(pVariables, VARIABLE_REBOOTPENDING, IsWuRebootPending() || IsRegistryRebootPending(), TRUE); ExitOnFailure(hr, "Failed to overwrite the bundle reboot-pending built-in variable."); LExit: return hr; } extern "C" HRESULT RegistrationDetectInstalled( __in BURN_REGISTRATION* pRegistration ) { HRESULT hr = S_OK; HKEY hkRegistration = NULL; DWORD dwInstalled = 0; pRegistration->fCached = FileExistsEx(pRegistration->sczCacheExecutablePath, NULL); pRegistration->detectedRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_NONE; // open registration key hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); if (SUCCEEDED(hr)) { hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled); pRegistration->detectedRegistrationType = (1 == dwInstalled) ? BOOTSTRAPPER_REGISTRATION_TYPE_FULL : BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS; } // Not finding the key or value is okay. if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) { hr = S_OK; } ReleaseRegKey(hkRegistration); return hr; } /******************************************************************* RegistrationDetectResumeMode - Detects registration information on the system to determine if a resume is taking place. *******************************************************************/ extern "C" HRESULT RegistrationDetectResumeType( __in BURN_REGISTRATION* pRegistration, __out BOOTSTRAPPER_RESUME_TYPE* pResumeType ) { HRESULT hr = S_OK; HKEY hkRegistration = NULL; BOOL fExists = FALSE; DWORD dwResume = 0; // open registration key hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); ExitOnPathFailure(hr, fExists, "Failed to open registration key."); if (!fExists) { *pResumeType = BOOTSTRAPPER_RESUME_TYPE_NONE; ExitFunction(); } // read Resume value hr = RegReadNumber(hkRegistration, L"Resume", &dwResume); ExitOnPathFailure(hr, fExists, "Failed to read Resume value."); if (!fExists) { *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID; ExitFunction(); } switch (dwResume) { case BURN_RESUME_MODE_ACTIVE: // a previous run was interrupted *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INTERRUPTED; break; case BURN_RESUME_MODE_SUSPEND: *pResumeType = BOOTSTRAPPER_RESUME_TYPE_SUSPEND; break; case BURN_RESUME_MODE_ARP: *pResumeType = BOOTSTRAPPER_RESUME_TYPE_ARP; break; case BURN_RESUME_MODE_REBOOT_PENDING: // The volatile pending registry doesn't exist (checked above) which means // the system was successfully restarted. *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT; break; default: // the value stored in the registry is not valid *pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID; break; } LExit: ReleaseRegKey(hkRegistration); return hr; } /******************************************************************* RegistrationDetectRelatedBundles - finds the bundles with same upgrade/detect/addon/patch codes. *******************************************************************/ extern "C" HRESULT RegistrationDetectRelatedBundles( __in BURN_REGISTRATION* pRegistration ) { HRESULT hr = S_OK; hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles); ExitOnFailure(hr, "Failed to initialize per-machine related bundles."); hr = RelatedBundlesInitializeForScope(FALSE, pRegistration, &pRegistration->relatedBundles); ExitOnFailure(hr, "Failed to initialize per-user related bundles."); RelatedBundlesSortDetect(&pRegistration->relatedBundles); LExit: return hr; } extern "C" HRESULT RegistrationPlanInitialize( __in BURN_REGISTRATION* pRegistration ) { HRESULT hr = S_OK; if (pRegistration->relatedBundles.cRelatedBundles && !pRegistration->relatedBundles.rgpPlanSortedRelatedBundles) { hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->relatedBundles.rgpPlanSortedRelatedBundles), pRegistration->relatedBundles.cRelatedBundles, sizeof(BURN_RELATED_BUNDLE*), 5); ExitOnFailure(hr, "Failed to initialize plan related bundles array."); for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) { pRegistration->relatedBundles.rgpPlanSortedRelatedBundles[i] = pRegistration->relatedBundles.rgRelatedBundles + i; } } LExit: return hr; } /******************************************************************* RegistrationSessionBegin - Registers a run session on the system. *******************************************************************/ extern "C" HRESULT RegistrationSessionBegin( __in_z LPCWSTR wzEngineWorkingPath, __in BURN_REGISTRATION* pRegistration, __in BURN_CACHE* pCache, __in BURN_VARIABLES* pVariables, __in DWORD dwRegistrationOptions, __in DWORD64 qwEstimatedSize, __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType ) { HRESULT hr = S_OK; HKEY hkRegistration = NULL; BOOL fCreated = FALSE; LPWSTR sczPublisher = NULL; SYSTEMTIME systime = { }; DWORD er = ERROR_SUCCESS; AssertSz(BOOTSTRAPPER_REGISTRATION_TYPE_NONE != registrationType, "Registration type can't be NONE"); LogId(REPORT_VERBOSE, MSG_SESSION_BEGIN, pRegistration->sczRegistrationKey, dwRegistrationOptions, LoggingBoolToString(pRegistration->fDisableResume)); // Cache bundle executable. if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_CACHE_BUNDLE) { hr = CacheCompleteBundle(pCache, pRegistration->fPerMachine, pRegistration->sczExecutableName, pRegistration->sczId, wzEngineWorkingPath #ifdef DEBUG , pRegistration->sczCacheExecutablePath #endif ); ExitOnFailure(hr, "Failed to cache bundle from path: %ls", wzEngineWorkingPath); } // create registration key hr = RegCreateEx(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, REG_KEY_DEFAULT, FALSE, NULL, &hkRegistration, &fCreated); ExitOnFailure(hr, "Failed to create registration key."); // Write any ARP values and software tags. hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, pRegistration->sczCacheExecutablePath); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH); hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, pRegistration->rgsczUpgradeCodes, pRegistration->cUpgradeCodes); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE); hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE, pRegistration->rgsczAddonCodes, pRegistration->cAddonCodes); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_ADDON_CODE); hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE, pRegistration->rgsczDetectCodes, pRegistration->cDetectCodes); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DETECT_CODE); hr = RegWriteStringArray(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE, pRegistration->rgsczPatchCodes, pRegistration->cPatchCodes); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PATCH_CODE); hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, pRegistration->pVersion->sczVersion); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION); hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MAJOR, pRegistration->pVersion->dwMajor); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MAJOR); hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_VERSION_MINOR, pRegistration->pVersion->dwMinor); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_VERSION_MINOR); if (pRegistration->sczProviderKey) { hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY, pRegistration->sczProviderKey); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY); } if (pRegistration->sczTag) { hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_TAG, pRegistration->sczTag); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_TAG); } hr = RegWriteStringFormatted(hkRegistration, BURN_REGISTRATION_REGISTRY_ENGINE_VERSION, L"%hs", szVerMajorMinorBuild); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_ENGINE_VERSION); hr = RegWriteNumber(hkRegistration, BURN_REGISTRATION_REGISTRY_ENGINE_PROTOCOL_VERSION, BURN_PROTOCOL_VERSION); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_ENGINE_PROTOCOL_VERSION); // DisplayIcon: [path to exe] and ",0" to refer to the first icon in the executable. hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_DISPLAY_ICON, L"%s,0", pRegistration->sczCacheExecutablePath); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_ICON); // update display name hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS == registrationType); ExitOnFailure(hr, "Failed to update name and publisher."); // DisplayVersion: provided by UI if (pRegistration->sczDisplayVersion) { hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_DISPLAY_VERSION, pRegistration->sczDisplayVersion); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_VERSION); } // Publisher: provided by UI hr = GetBundleManufacturer(pRegistration, pVariables, &sczPublisher); hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PUBLISHER, SUCCEEDED(hr) ? sczPublisher : pRegistration->sczPublisher); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PUBLISHER); // HelpLink: provided by UI if (pRegistration->sczHelpLink) { hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_LINK, pRegistration->sczHelpLink); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_LINK); } // HelpTelephone: provided by UI if (pRegistration->sczHelpTelephone) { hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_HELP_TELEPHONE, pRegistration->sczHelpTelephone); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_HELP_TELEPHONE); } // URLInfoAbout, provided by UI if (pRegistration->sczAboutUrl) { hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_INFO_ABOUT, pRegistration->sczAboutUrl); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_INFO_ABOUT); } // URLUpdateInfo, provided by UI if (pRegistration->sczUpdateUrl) { hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_URL_UPDATE_INFO, pRegistration->sczUpdateUrl); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_URL_UPDATE_INFO); } // ParentDisplayName if (pRegistration->sczParentDisplayName) { hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_DISPLAY_NAME, pRegistration->sczParentDisplayName); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_DISPLAY_NAME); // Need to write the ParentKeyName but can be set to anything. hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_PARENT_KEY_NAME, pRegistration->sczParentDisplayName); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_PARENT_KEY_NAME); } // Comments, provided by UI if (pRegistration->sczComments) { hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_COMMENTS, pRegistration->sczComments); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_COMMENTS); } // Contact, provided by UI if (pRegistration->sczContact) { hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_CONTACT, pRegistration->sczContact); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_CONTACT); } // InstallLocation: provided by UI // TODO: need to figure out what "InstallLocation" means in a chainer. <smile/> // NoModify if (BURN_REGISTRATION_MODIFY_DISABLE == pRegistration->modify) { hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_MODIFY, 1); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_MODIFY); } else if (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON != pRegistration->modify) // if support modify (aka: did not disable anything) { // ModifyPath: [path to exe] /modify hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_MODIFY_PATH, L"\"%ls\" /%ls /modify", pRegistration->sczCacheExecutablePath, BURN_COMMANDLINE_SWITCH_CLEAN_ROOM); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_MODIFY_PATH); // NoElevateOnModify: 1 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY, 1); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_ELEVATE_ON_MODIFY); } // NoRemove: should this be allowed? if (pRegistration->fNoRemove) { hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_REMOVE, 1); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_REMOVE); } // Conditionally hide the ARP entry. if (pRegistration->fForceSystemComponent || (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_ARP_SYSTEM_COMPONENT)) { hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_SYSTEM_COMPONENT, 1); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_SYSTEM_COMPONENT); } else { er = ::RegDeleteValueW(hkRegistration, REGISTRY_BUNDLE_SYSTEM_COMPONENT); if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er) { er = ERROR_SUCCESS; } ExitOnWin32Error(er, hr, "Failed to delete %ls value.", REGISTRY_BUNDLE_SYSTEM_COMPONENT); } // QuietUninstallString: [path to exe] /uninstall /quiet hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING, L"\"%ls\" /%ls /uninstall /quiet", pRegistration->sczCacheExecutablePath, BURN_COMMANDLINE_SWITCH_CLEAN_ROOM); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_QUIET_UNINSTALL_STRING); // UninstallString, [path to exe] // If the modify button is to be disabled, we'll add "/modify" to the uninstall string because the button is "Uninstall/Change". Otherwise, // it's just the "Uninstall" button so we add "/uninstall" to make the program just go away. LPCWSTR wzUninstallParameters = (BURN_REGISTRATION_MODIFY_DISABLE_BUTTON == pRegistration->modify) ? L"/modify" : L" /uninstall"; hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_UNINSTALL_STRING, L"\"%ls\" /%ls %ls", pRegistration->sczCacheExecutablePath, BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, wzUninstallParameters); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_UNINSTALL_STRING); if (pRegistration->softwareTags.cSoftwareTags) { hr = WriteSoftwareTags(pVariables, &pRegistration->softwareTags); ExitOnFailure(hr, "Failed to write software tags."); } // Update registration. if (pRegistration->update.fRegisterUpdate) { hr = WriteUpdateRegistration(pRegistration, pVariables); ExitOnFailure(hr, "Failed to write update registration."); } // Only set install date and initial estimated size here for the first time. // Estimated size will always get updated at the end of the session. if (fCreated) { // Write the install date. ::GetLocalTime(&systime); hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_INSTALL_DATE, L"%04u%02u%02u", systime.wYear, systime.wMonth, systime.wDay); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_INSTALL_DATE); // Write the initial estimated size. hr = UpdateEstimatedSize(hkRegistration, qwEstimatedSize); ExitOnFailure(hr, "Failed to update estimated size."); } // Register the bundle dependency key. if (dwRegistrationOptions & BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_PROVIDER_KEY) { hr = DependencyRegisterBundle(pRegistration); ExitOnFailure(hr, "Failed to register the bundle dependency key."); } // update resume mode hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, registrationType, FALSE); ExitOnFailure(hr, "Failed to update resume mode."); LExit: ReleaseStr(sczPublisher); ReleaseRegKey(hkRegistration); return hr; } /******************************************************************* RegistrationSessionEnd - Unregisters a run session from the system. *******************************************************************/ extern "C" HRESULT RegistrationSessionEnd( __in BURN_REGISTRATION* pRegistration, __in BURN_CACHE* pCache, __in BURN_VARIABLES* pVariables, __in BURN_PACKAGES* pPackages, __in BURN_RESUME_MODE resumeMode, __in BOOTSTRAPPER_APPLY_RESTART restart, __in DWORD64 qwEstimatedSize, __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType ) { HRESULT hr = S_OK; HKEY hkRegistration = NULL; BOOL fDeleted = FALSE; // If no resume mode, then remove the bundle registration. if (BURN_RESUME_MODE_NONE == resumeMode) { AssertSz(BOOTSTRAPPER_REGISTRATION_TYPE_NONE == registrationType, "Registration type must be NONE if resume mode is NONE"); // Remove the bundle dependencies. DependencyUnregisterBundle(pRegistration, pPackages); // Delete update registration key. if (pRegistration->update.fRegisterUpdate) { RemoveUpdateRegistration(pRegistration); } RemoveSoftwareTags(pVariables, &pRegistration->softwareTags); // Delete registration key. hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, TRUE); ExitOnPathFailure(hr, fDeleted, "Failed to delete registration key: %ls", pRegistration->sczRegistrationKey); CacheRemoveBundle(pCache, pRegistration->fPerMachine, pRegistration->sczId); } else // the mode needs to be updated so open the registration key. { AssertSz(BOOTSTRAPPER_REGISTRATION_TYPE_NONE != registrationType, "Registration type must not be NONE if resume mode is not NONE"); // Open registration key. hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration); ExitOnFailure(hr, "Failed to open registration key."); // update display name hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS == registrationType); ExitOnFailure(hr, "Failed to update name and publisher."); hr = UpdateEstimatedSize(hkRegistration, qwEstimatedSize); ExitOnFailure(hr, "Failed to update estimated size."); } // Update resume mode. hr = UpdateResumeMode(pRegistration, hkRegistration, resumeMode, registrationType, BOOTSTRAPPER_APPLY_RESTART_INITIATED == restart); ExitOnFailure(hr, "Failed to update resume mode."); LExit: ReleaseRegKey(hkRegistration); return hr; } /******************************************************************* RegistrationSaveState - Saves an engine state BLOB for retreval after a resume. *******************************************************************/ extern "C" HRESULT RegistrationSaveState( __in BURN_REGISTRATION* pRegistration, __in_bcount(cbBuffer) BYTE* pbBuffer, __in SIZE_T cbBuffer ) { HRESULT hr = S_OK; BURN_VARIABLES variables = { }; SIZE_T iBuffer_Unused = 0; HKEY hkRegistration = NULL; LPWSTR sczVariableKey = NULL; LPWSTR sczVariableValue = NULL; LPWSTR sczValueName = NULL; DWORD dwType = 0; DWORD dwNumberOfExistingValues = 0; DWORD er = ERROR_SUCCESS; // write data to file hr = FileWrite(pRegistration->sczStateFile, FILE_ATTRIBUTE_NORMAL, pbBuffer, cbBuffer, NULL); if (E_PATHNOTFOUND == hr) { // TODO: should we log that the bundle's cache folder was not present so the state file wasn't created either? hr = S_OK; } ExitOnFailure(hr, "Failed to write state to file: %ls", pRegistration->sczStateFile); ::InitializeCriticalSection(&variables.csAccess); hr = VariableDeserialize(&variables, TRUE, pbBuffer, cbBuffer, &iBuffer_Unused); ExitOnFailure(hr, "Failed to read variables."); // build variable registry key path hr = StrAllocFormatted(&sczVariableKey, L"%s\\%s", pRegistration->sczRegistrationKey, REGISTRY_BUNDLE_VARIABLE_KEY); ExitOnFailure(hr, "Failed to build variable registry key path."); // open registration variable key hr = RegCreate(pRegistration->hkRoot, sczVariableKey, KEY_WRITE | KEY_QUERY_VALUE, &hkRegistration); ExitOnFailure(hr, "Failed to create registration variable key."); hr = RegQueryInfoKey(hkRegistration, 0, 0, 0, 0, 0, 0, &dwNumberOfExistingValues, 0, 0, 0, 0); ExitOnFailure(hr, "Failed to query registration variable count."); for (DWORD i = dwNumberOfExistingValues; i >= 0; --i) { hr = RegValueEnum(hkRegistration, i, &sczValueName, &dwType); if (E_NOMOREITEMS == hr) { hr = S_OK; break; } ExitOnFailure(hr, "Failed to enumerate value %u", i); er = ::RegDeleteValueW(hkRegistration, sczValueName); if (ERROR_FILE_NOT_FOUND != er) { ExitOnWin32Error(er, hr, "Failed to delete registration variable value."); } } // Write variables. for (DWORD i = 0; i < variables.cVariables; ++i) { BURN_VARIABLE* pVariable = &variables.rgVariables[i]; // Write variable value. switch (pVariable->Value.Type) { case BURN_VARIANT_TYPE_NONE: hr = RegWriteNone(hkRegistration, pVariable->sczName); ExitOnFailure(hr, "Failed to set variable value."); break; case BURN_VARIANT_TYPE_NUMERIC: __fallthrough; case BURN_VARIANT_TYPE_VERSION: __fallthrough; case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BVariantGetString(&pVariable->Value, &sczVariableValue); ExitOnFailure(hr, "Failed to get variable value."); hr = RegWriteString(hkRegistration, pVariable->sczName, sczVariableValue); ExitOnFailure(hr, "Failed to set variable value."); ReleaseNullStrSecure(sczVariableValue); break; default: hr = E_INVALIDARG; ExitOnFailure(hr, "Unsupported variable type."); } } LExit: VariablesUninitialize(&variables); ReleaseStr(sczValueName); ReleaseStr(sczVariableValue); ReleaseStr(sczVariableKey); ReleaseRegKey(hkRegistration); return hr; } /******************************************************************* RegistrationLoadState - Loads a previously stored engine state BLOB. *******************************************************************/ extern "C" HRESULT RegistrationLoadState( __in BURN_REGISTRATION* pRegistration, __out_bcount(*pcbBuffer) BYTE** ppbBuffer, __out SIZE_T* pcbBuffer ) { // read data from file HRESULT hr = FileRead(ppbBuffer, pcbBuffer, pRegistration->sczStateFile); return hr; } /******************************************************************* RegistrationGetResumeCommandLine - Gets the resume command line from the registry *******************************************************************/ extern "C" HRESULT RegistrationGetResumeCommandLine( __in const BURN_REGISTRATION* pRegistration, __deref_out_z LPWSTR* psczResumeCommandLine ) { HRESULT hr = S_OK; HKEY hkRegistration = NULL; // open registration key hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); if (SUCCEEDED(hr)) { hr = RegReadString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, psczResumeCommandLine); } // Not finding the key or value is okay. if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) { hr = S_OK; } ReleaseRegKey(hkRegistration); return hr; } // internal helper functions static HRESULT ParseSoftwareTagsFromXml( __in IXMLDOMNode* pixnRegistrationNode, __out BURN_SOFTWARE_TAG** prgSoftwareTags, __out DWORD* pcSoftwareTags ) { HRESULT hr = S_OK; IXMLDOMNodeList* pixnNodes = NULL; IXMLDOMNode* pixnNode = NULL; DWORD cNodes = 0; BURN_SOFTWARE_TAG* pSoftwareTags = NULL; BSTR bstrTagXml = NULL; // select tag nodes hr = XmlSelectNodes(pixnRegistrationNode, L"SoftwareTag", &pixnNodes); ExitOnFailure(hr, "Failed to select software tag nodes."); // get tag node count hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get software tag count."); if (cNodes) { pSoftwareTags = (BURN_SOFTWARE_TAG*)MemAlloc(sizeof(BURN_SOFTWARE_TAG) * cNodes, TRUE); ExitOnNull(pSoftwareTags, hr, E_OUTOFMEMORY, "Failed to allocate memory for software tag structs."); for (DWORD i = 0; i < cNodes; ++i) { BURN_SOFTWARE_TAG* pSoftwareTag = &pSoftwareTags[i]; hr = XmlNextElement(pixnNodes, &pixnNode, NULL); ExitOnRequiredXmlQueryFailure(hr, "Failed to get next node."); hr = XmlGetAttributeEx(pixnNode, L"Filename", &pSoftwareTag->sczFilename); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Filename."); hr = XmlGetAttributeEx(pixnNode, L"Regid", &pSoftwareTag->sczRegid); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Regid."); hr = XmlGetAttributeEx(pixnNode, L"Path", &pSoftwareTag->sczPath); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Path."); hr = XmlGetText(pixnNode, &bstrTagXml); ExitOnRequiredXmlQueryFailure(hr, "Failed to get SoftwareTag text."); hr = StrAnsiAllocString(&pSoftwareTag->sczTag, bstrTagXml, 0, CP_UTF8); ExitOnFailure(hr, "Failed to convert SoftwareTag text to UTF-8"); // prepare next iteration ReleaseNullBSTR(bstrTagXml); ReleaseNullObject(pixnNode); } } *pcSoftwareTags = cNodes; *prgSoftwareTags = pSoftwareTags; pSoftwareTags = NULL; hr = S_OK; LExit: ReleaseBSTR(bstrTagXml); ReleaseObject(pixnNode); ReleaseObject(pixnNodes); ReleaseMem(pSoftwareTags); return hr; } static HRESULT SetPaths( __in BURN_REGISTRATION* pRegistration, __in BURN_CACHE* pCache ) { HRESULT hr = S_OK; LPWSTR sczCacheDirectory = NULL; // save registration key root pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; // build uninstall registry key path hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%ls\\%ls", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczId); ExitOnFailure(hr, "Failed to build uninstall registry key path."); // build cache directory hr = CacheGetCompletedPath(pCache, pRegistration->fPerMachine, pRegistration->sczId, &sczCacheDirectory); ExitOnFailure(hr, "Failed to build cache directory."); // build cached executable path hr = PathConcatRelativeToFullyQualifiedBase(sczCacheDirectory, pRegistration->sczExecutableName, &pRegistration->sczCacheExecutablePath); ExitOnFailure(hr, "Failed to build cached executable path."); // build state file path hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%ls\\state.rsm", sczCacheDirectory); ExitOnFailure(hr, "Failed to build state file path."); LExit: ReleaseStr(sczCacheDirectory); return hr; } static HRESULT GetBundleManufacturer( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __out_z LPWSTR* psczBundleManufacturer ) { HRESULT hr = S_OK; LPCWSTR wzPublisher = pRegistration->sczPublisher ? pRegistration->sczPublisher : L""; hr = EnsureRegistrationVariable(pVariables, BURN_BUNDLE_MANUFACTURER, wzPublisher, psczBundleManufacturer); ExitOnFailure(hr, "Failed to get bundle manufacturer."); LExit: return hr; } static HRESULT GetBundleInProgressName( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __out_z LPWSTR* psczInProgressBundleName ) { HRESULT hr = S_OK; LPCWSTR wzInProgressDisplayName = pRegistration->sczInProgressDisplayName ? pRegistration->sczInProgressDisplayName : L""; hr = EnsureRegistrationVariable(pVariables, BURN_BUNDLE_INPROGRESS_NAME, wzInProgressDisplayName, psczInProgressBundleName); ExitOnFailure(hr, "Failed to ensure in-progress bundle name."); LExit: return hr; } static HRESULT GetBundleName( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __out_z LPWSTR* psczBundleName ) { HRESULT hr = S_OK; LPCWSTR wzDisplayName = pRegistration->sczDisplayName ? pRegistration->sczDisplayName : L""; hr = EnsureRegistrationVariable(pVariables, BURN_BUNDLE_NAME, wzDisplayName, psczBundleName); ExitOnFailure(hr, "Failed to ensure bundle name."); LExit: return hr; } static HRESULT EnsureRegistrationVariable( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in_z LPCWSTR wzDefaultValue, __out_z LPWSTR* psczValue ) { HRESULT hr = S_OK; hr = VariableGetString(pVariables, wzVariable, psczValue); if (E_NOTFOUND == hr) { hr = VariableSetString(pVariables, wzVariable, wzDefaultValue, FALSE, FALSE); ExitOnFailure(hr, "Failed to set registration variable."); hr = StrAllocString(psczValue, wzDefaultValue, 0); } ExitOnFailure(hr, "Failed to get registration variable."); LExit: return hr; } static HRESULT UpdateResumeMode( __in BURN_REGISTRATION* pRegistration, __in HKEY hkRegistration, __in BURN_RESUME_MODE resumeMode, __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType, __in BOOL fRestartInitiated ) { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; HKEY hkRun = NULL; LPWSTR sczRunOnceCommandLine = NULL; LPCWSTR sczResumeKey = REGISTRY_RUN_ONCE_KEY; LogId(REPORT_STANDARD, MSG_SESSION_UPDATE, pRegistration->sczRegistrationKey, LoggingResumeModeToString(resumeMode), LoggingBoolToString(fRestartInitiated), LoggingBoolToString(pRegistration->fDisableResume)); // write resume information if (hkRegistration) { // write Resume value hr = RegWriteNumber(hkRegistration, L"Resume", (DWORD)resumeMode); ExitOnFailure(hr, "Failed to write Resume value."); // Write Installed value. hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, BOOTSTRAPPER_REGISTRATION_TYPE_FULL == registrationType ? 1 : 0); ExitOnFailure(hr, "Failed to write Installed value."); } // If the engine is active write the run key so we resume if there is an unexpected // power loss. Also, if a restart was initiated in the middle of the chain then // ensure the run key exists (it should since going active would have written it). // Do not write the run key when embedded since the containing bundle // is expected to detect for and restart the embedded bundle. if ((BURN_RESUME_MODE_ACTIVE == resumeMode || fRestartInitiated) && !pRegistration->fDisableResume) { // append RunOnce switch hr = StrAllocFormatted(&sczRunOnceCommandLine, L"\"%ls\" /%ls /%ls", pRegistration->sczCacheExecutablePath, BURN_COMMANDLINE_SWITCH_CLEAN_ROOM, BURN_COMMANDLINE_SWITCH_RUNONCE); ExitOnFailure(hr, "Failed to format resume command line for RunOnce."); // write run key hr = RegCreate(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun); ExitOnFailure(hr, "Failed to create run key."); hr = RegWriteString(hkRun, pRegistration->sczId, sczRunOnceCommandLine); ExitOnFailure(hr, "Failed to write run key value."); hr = RegWriteString(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE, pRegistration->sczResumeCommandLine); ExitOnFailure(hr, "Failed to write resume command line value."); } else // delete run key value { hr = RegOpen(pRegistration->hkRoot, sczResumeKey, KEY_WRITE, &hkRun); if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) { hr = S_OK; } else { ExitOnFailure(hr, "Failed to open run key."); er = ::RegDeleteValueW(hkRun, pRegistration->sczId); if (ERROR_FILE_NOT_FOUND == er) { er = ERROR_SUCCESS; } ExitOnWin32Error(er, hr, "Failed to delete run key value."); } if (hkRegistration) { er = ::RegDeleteValueW(hkRegistration, REGISTRY_BUNDLE_RESUME_COMMAND_LINE); if (ERROR_FILE_NOT_FOUND == er) { er = ERROR_SUCCESS; } ExitOnWin32Error(er, hr, "Failed to delete resume command line value."); } } LExit: ReleaseStr(sczRunOnceCommandLine); ReleaseRegKey(hkRun); return hr; } static HRESULT FormatUpdateRegistrationKey( __in BURN_REGISTRATION* pRegistration, __out_z LPWSTR* psczKey ) { HRESULT hr = S_OK; LPWSTR sczKey = NULL; hr = StrAllocFormatted(&sczKey, L"SOFTWARE\\%ls\\Updates\\", pRegistration->update.sczManufacturer); ExitOnFailure(hr, "Failed to format the key path for update registration."); if (pRegistration->update.sczProductFamily) { hr = StrAllocFormatted(&sczKey, L"%ls%ls\\", sczKey, pRegistration->update.sczProductFamily); ExitOnFailure(hr, "Failed to format the key path for update registration."); } hr = StrAllocConcat(&sczKey, pRegistration->update.sczName, 0); ExitOnFailure(hr, "Failed to format the key path for update registration."); *psczKey = sczKey; sczKey = NULL; LExit: ReleaseStr(sczKey); return hr; } static HRESULT WriteSoftwareTags( __in BURN_VARIABLES* pVariables, __in BURN_SOFTWARE_TAGS* pSoftwareTags ) { HRESULT hr = S_OK; LPWSTR sczRootFolder = NULL; LPWSTR sczTagFolder = NULL; LPWSTR sczPath = NULL; for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag) { BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag; hr = VariableFormatString(pVariables, pSoftwareTag->sczPath, &sczRootFolder, NULL); ExitOnFailure(hr, "Failed to format tag folder path."); hr = PathConcat(sczRootFolder, SWIDTAG_FOLDER, &sczTagFolder); ExitOnFailure(hr, "Failed to allocate regid folder path."); hr = PathConcatRelativeToFullyQualifiedBase(sczTagFolder, pSoftwareTag->sczFilename, &sczPath); ExitOnFailure(hr, "Failed to allocate regid file path."); hr = DirEnsureExists(sczTagFolder, NULL); ExitOnFailure(hr, "Failed to create regid folder: %ls", sczTagFolder); hr = FileWrite(sczPath, FILE_ATTRIBUTE_NORMAL, reinterpret_cast<LPBYTE>(pSoftwareTag->sczTag), lstrlenA(pSoftwareTag->sczTag), NULL); ExitOnFailure(hr, "Failed to write tag xml to file: %ls", sczPath); } LExit: ReleaseStr(sczPath); ReleaseStr(sczTagFolder); ReleaseStr(sczRootFolder); return hr; } static HRESULT RemoveSoftwareTags( __in BURN_VARIABLES* pVariables, __in BURN_SOFTWARE_TAGS* pSoftwareTags ) { HRESULT hr = S_OK; LPWSTR sczRootFolder = NULL; LPWSTR sczTagFolder = NULL; LPWSTR sczPath = NULL; for (DWORD iTag = 0; iTag < pSoftwareTags->cSoftwareTags; ++iTag) { BURN_SOFTWARE_TAG* pSoftwareTag = pSoftwareTags->rgSoftwareTags + iTag; hr = VariableFormatString(pVariables, pSoftwareTag->sczPath, &sczRootFolder, NULL); ExitOnFailure(hr, "Failed to format tag folder path."); hr = PathConcat(sczRootFolder, SWIDTAG_FOLDER, &sczTagFolder); ExitOnFailure(hr, "Failed to allocate regid folder path."); hr = PathConcatRelativeToFullyQualifiedBase(sczTagFolder, pSoftwareTag->sczFilename, &sczPath); ExitOnFailure(hr, "Failed to allocate regid file path."); // Best effort to delete the software tag file and the regid folder. FileEnsureDelete(sczPath); DirDeleteEmptyDirectoriesToRoot(sczTagFolder, 0); } LExit: ReleaseStr(sczPath); ReleaseStr(sczTagFolder); ReleaseStr(sczRootFolder); return hr; } static HRESULT WriteUpdateRegistration( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables ) { HRESULT hr = S_OK; LPWSTR sczKey = NULL; HKEY hkKey = NULL; hr = FormatUpdateRegistrationKey(pRegistration, &sczKey); ExitOnFailure(hr, "Failed to get the formatted key path for update registration."); hr = RegCreate(pRegistration->hkRoot, sczKey, KEY_WRITE, &hkKey); ExitOnFailure(hr, "Failed to create the key for update registration."); hr = RegWriteString(hkKey, L"ThisVersionInstalled", L"Y"); ExitOnFailure(hr, "Failed to write %ls value.", L"ThisVersionInstalled"); hr = RegWriteString(hkKey, L"PackageName", pRegistration->sczDisplayName); ExitOnFailure(hr, "Failed to write %ls value.", L"PackageName"); hr = RegWriteString(hkKey, L"PackageVersion", pRegistration->sczDisplayVersion); ExitOnFailure(hr, "Failed to write %ls value.", L"PackageVersion"); hr = RegWriteString(hkKey, L"Publisher", pRegistration->sczPublisher); ExitOnFailure(hr, "Failed to write %ls value.", L"Publisher"); if (pRegistration->update.sczDepartment) { hr = RegWriteString(hkKey, L"PublishingGroup", pRegistration->update.sczDepartment); ExitOnFailure(hr, "Failed to write %ls value.", L"PublishingGroup"); } hr = RegWriteString(hkKey, L"ReleaseType", pRegistration->update.sczClassification); ExitOnFailure(hr, "Failed to write %ls value.", L"ReleaseType"); hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_LOGONUSER, L"InstalledBy"); ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledBy"); hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_DATE, L"InstalledDate"); ExitOnFailure(hr, "Failed to write %ls value.", L"InstalledDate"); hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERNAME, L"InstallerName"); ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerName"); hr = RegWriteStringVariable(hkKey, pVariables, VARIABLE_INSTALLERVERSION, L"InstallerVersion"); ExitOnFailure(hr, "Failed to write %ls value.", L"InstallerVersion"); LExit: ReleaseRegKey(hkKey); ReleaseStr(sczKey); return hr; } static HRESULT RemoveUpdateRegistration( __in BURN_REGISTRATION* pRegistration ) { HRESULT hr = S_OK; LPWSTR sczKey = NULL; LPWSTR sczPackageVersion = NULL; HKEY hkKey = NULL; BOOL fDeleteRegKey = TRUE; BOOL fDeleted = FALSE; hr = FormatUpdateRegistrationKey(pRegistration, &sczKey); ExitOnFailure(hr, "Failed to format key for update registration."); // Only delete if the uninstalling bundle's PackageVersion is the same as the // PackageVersion in the update registration key. // This is to support build to build upgrades hr = RegOpen(pRegistration->hkRoot, sczKey, KEY_QUERY_VALUE, &hkKey); if (SUCCEEDED(hr)) { hr = RegReadString(hkKey, L"PackageVersion", &sczPackageVersion); if (SUCCEEDED(hr)) { if (CSTR_EQUAL != ::CompareStringW(LOCALE_INVARIANT, 0, sczPackageVersion, -1, pRegistration->sczDisplayVersion, -1)) { fDeleteRegKey = FALSE; } } ReleaseRegKey(hkKey); } // Unable to open the key or read the value is okay. hr = S_OK; if (fDeleteRegKey) { hr = RegDelete(pRegistration->hkRoot, sczKey, REG_KEY_DEFAULT, FALSE); ExitOnPathFailure(hr, fDeleted, "Failed to remove update registration key: %ls", sczKey); } LExit: ReleaseStr(sczPackageVersion); ReleaseStr(sczKey); return hr; } static HRESULT RegWriteStringVariable( __in HKEY hk, __in BURN_VARIABLES* pVariables, __in LPCWSTR wzVariable, __in LPCWSTR wzName ) { HRESULT hr = S_OK; LPWSTR sczValue = NULL; hr = VariableGetString(pVariables, wzVariable, &sczValue); ExitOnFailure(hr, "Failed to get the %ls variable.", wzVariable); hr = RegWriteString(hk, wzName, sczValue); ExitOnFailure(hr, "Failed to write %ls value.", wzName); LExit: StrSecureZeroFreeString(sczValue); return hr; } static HRESULT UpdateBundleNameRegistration( __in BURN_REGISTRATION* pRegistration, __in BURN_VARIABLES* pVariables, __in HKEY hkRegistration, __in BOOL fInProgressRegistration ) { HRESULT hr = S_OK; LPWSTR sczDisplayName = NULL; if (fInProgressRegistration) { hr = GetBundleInProgressName(pRegistration, pVariables, &sczDisplayName); ExitOnFailure(hr, "Failed to get bundle in-progress name."); } if (!sczDisplayName || !*sczDisplayName) { hr = GetBundleName(pRegistration, pVariables, &sczDisplayName); ExitOnFailure(hr, "Failed to get bundle name."); } hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME, sczDisplayName); ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_DISPLAY_NAME); LExit: ReleaseStr(sczDisplayName); return hr; } static HRESULT UpdateEstimatedSize( __in HKEY hkRegistration, __in DWORD64 qwEstimatedSize ) { HRESULT hr = S_OK; DWORD dwSize = 0; qwEstimatedSize /= 1024; // Convert bytes to KB if (0 < qwEstimatedSize) { if (DWORD_MAX < qwEstimatedSize) { // ARP doesn't support QWORDs here dwSize = DWORD_MAX; } else { dwSize = static_cast<DWORD>(qwEstimatedSize); } hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_ESTIMATED_SIZE, dwSize); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_ESTIMATED_SIZE); } LExit: return hr; } static BOOL IsWuRebootPending() { HRESULT hr = S_OK; BOOL fUninitializeCom = FALSE; BOOL fRebootPending = FALSE; // Do a best effort to ask WU if a reboot is required. If anything goes // wrong then let's pretend a reboot is not required. hr = ::CoInitialize(NULL); fUninitializeCom = SUCCEEDED(hr); if (fUninitializeCom || RPC_E_CHANGED_MODE == hr) { hr = WuaRestartRequired(&fRebootPending); if (FAILED(hr)) { fRebootPending = FALSE; } if (fUninitializeCom) { ::CoUninitialize(); } } return fRebootPending; } static BOOL IsRegistryRebootPending() { HRESULT hr = S_OK; DWORD dwValue; HKEY hk = NULL; BOOL fRebootPending = FALSE; hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\ServerManager", L"CurrentRebootAttempts", REG_KEY_DEFAULT, &dwValue); fRebootPending = SUCCEEDED(hr) && 0 < dwValue; if (!fRebootPending) { hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Updates", L"UpdateExeVolatile", REG_KEY_DEFAULT, &dwValue); fRebootPending = SUCCEEDED(hr) && 0 < dwValue; if (!fRebootPending) { fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", NULL, REG_KEY_DEFAULT); if (!fRebootPending) { fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress", NULL, REG_KEY_DEFAULT); if (!fRebootPending) { hr = RegKeyReadNumber(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate\\Auto Update", L"AUState", REG_KEY_DEFAULT, &dwValue); fRebootPending = SUCCEEDED(hr) && 8 == dwValue; if (!fRebootPending) { fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations", REG_KEY_DEFAULT); if (!fRebootPending) { fRebootPending = RegValueExists(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager", L"PendingFileRenameOperations2", REG_KEY_DEFAULT); if (!fRebootPending) { hr = RegOpenEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\FileRenameOperations", KEY_READ, REG_KEY_DEFAULT, &hk); if (SUCCEEDED(hr)) { DWORD cSubKeys = 0; DWORD cValues = 0; hr = RegQueryKey(hk, &cSubKeys, &cValues); fRebootPending = SUCCEEDED(hr) && (0 < cSubKeys || 0 < cValues); } } } } } } } } ReleaseRegKey(hk); return fRebootPending; }