// 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_INSTALLED = L"Installed";
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_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 BOOL fRestartInitiated
);
static HRESULT ParseRelatedCodes(
__in BURN_REGISTRATION* pRegistration,
__in IXMLDOMNode* pixnBundle
);
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 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;
// select registration node
hr = XmlSelectSingleNode(pixnBundle, L"Registration", &pixnRegistrationNode);
if (S_FALSE == hr)
{
hr = E_NOTFOUND;
}
ExitOnFailure(hr, "Failed to select registration node.");
// @Id
hr = XmlGetAttributeEx(pixnRegistrationNode, L"Id", &pRegistration->sczId);
ExitOnFailure(hr, "Failed to get @Id.");
// @Tag
hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag);
ExitOnFailure(hr, "Failed to get @Tag.");
hr = ParseRelatedCodes(pRegistration, pixnBundle);
ExitOnFailure(hr, "Failed to parse related bundles");
// @Version
hr = XmlGetAttributeEx(pixnRegistrationNode, L"Version", &scz);
ExitOnFailure(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);
ExitOnFailure(hr, "Failed to get @ProviderKey.");
// @ExecutableName
hr = XmlGetAttributeEx(pixnRegistrationNode, L"ExecutableName", &pRegistration->sczExecutableName);
ExitOnFailure(hr, "Failed to get @ExecutableName.");
// @PerMachine
hr = XmlGetYesNoAttribute(pixnRegistrationNode, L"PerMachine", &pRegistration->fPerMachine);
ExitOnFailure(hr, "Failed to get @PerMachine.");
// select ARP node
hr = XmlSelectSingleNode(pixnRegistrationNode, L"Arp", &pixnArpNode);
if (S_FALSE != hr)
{
ExitOnFailure(hr, "Failed to select ARP node.");
// @Register
hr = XmlGetYesNoAttribute(pixnArpNode, L"Register", &pRegistration->fRegisterArp);
ExitOnFailure(hr, "Failed to get @Register.");
// @DisplayName
hr = XmlGetAttributeEx(pixnArpNode, L"DisplayName", &pRegistration->sczDisplayName);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @DisplayName.");
}
// @InProgressDisplayName
hr = XmlGetAttributeEx(pixnArpNode, L"InProgressDisplayName", &pRegistration->sczInProgressDisplayName);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @InProgressDisplayName.");
}
// @DisplayVersion
hr = XmlGetAttributeEx(pixnArpNode, L"DisplayVersion", &pRegistration->sczDisplayVersion);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @DisplayVersion.");
}
// @Publisher
hr = XmlGetAttributeEx(pixnArpNode, L"Publisher", &pRegistration->sczPublisher);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @Publisher.");
}
// @HelpLink
hr = XmlGetAttributeEx(pixnArpNode, L"HelpLink", &pRegistration->sczHelpLink);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @HelpLink.");
}
// @HelpTelephone
hr = XmlGetAttributeEx(pixnArpNode, L"HelpTelephone", &pRegistration->sczHelpTelephone);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @HelpTelephone.");
}
// @AboutUrl
hr = XmlGetAttributeEx(pixnArpNode, L"AboutUrl", &pRegistration->sczAboutUrl);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @AboutUrl.");
}
// @UpdateUrl
hr = XmlGetAttributeEx(pixnArpNode, L"UpdateUrl", &pRegistration->sczUpdateUrl);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @UpdateUrl.");
}
// @ParentDisplayName
hr = XmlGetAttributeEx(pixnArpNode, L"ParentDisplayName", &pRegistration->sczParentDisplayName);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @ParentDisplayName.");
}
// @Comments
hr = XmlGetAttributeEx(pixnArpNode, L"Comments", &pRegistration->sczComments);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @Comments.");
}
// @Contact
hr = XmlGetAttributeEx(pixnArpNode, L"Contact", &pRegistration->sczContact);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @Contact.");
}
// @DisableModify
hr = XmlGetAttributeEx(pixnArpNode, L"DisableModify", &scz);
if (SUCCEEDED(hr))
{
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
{
hr = E_UNEXPECTED;
ExitOnRootFailure(hr, "Invalid modify disabled type: %ls", scz);
}
}
else if (E_NOTFOUND == hr)
{
pRegistration->modify = BURN_REGISTRATION_MODIFY_ENABLED;
hr = S_OK;
}
ExitOnFailure(hr, "Failed to get @DisableModify.");
// @DisableRemove
hr = XmlGetYesNoAttribute(pixnArpNode, L"DisableRemove", &pRegistration->fNoRemove);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @DisableRemove.");
pRegistration->fNoRemoveDefined = 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);
if (S_FALSE != hr)
{
ExitOnFailure(hr, "Failed to select Update node.");
pRegistration->update.fRegisterUpdate = TRUE;
// @Manufacturer
hr = XmlGetAttributeEx(pixnUpdateNode, L"Manufacturer", &pRegistration->update.sczManufacturer);
ExitOnFailure(hr, "Failed to get @Manufacturer.");
// @Department
hr = XmlGetAttributeEx(pixnUpdateNode, L"Department", &pRegistration->update.sczDepartment);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @Department.");
}
// @ProductFamily
hr = XmlGetAttributeEx(pixnUpdateNode, L"ProductFamily", &pRegistration->update.sczProductFamily);
if (E_NOTFOUND != hr)
{
ExitOnFailure(hr, "Failed to get @ProductFamily.");
}
// @Name
hr = XmlGetAttributeEx(pixnUpdateNode, L"Name", &pRegistration->update.sczName);
ExitOnFailure(hr, "Failed to get @Name.");
// @Classification
hr = XmlGetAttributeEx(pixnUpdateNode, L"Classification", &pRegistration->update.sczClassification);
ExitOnFailure(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;
if (pRegistration->fInstalled)
{
hr = VariableSetNumeric(pVariables, BURN_BUNDLE_INSTALLED, 1, TRUE);
ExitOnFailure(hr, "Failed to set the bundle installed built-in variable.");
}
// 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.");
hr = VariableSetNumeric(pVariables, BURN_REBOOT_PENDING, IsWuRebootPending() || IsRegistryRebootPending(), TRUE);
ExitOnFailure(hr, "Failed to overwrite the bundle reboot-pending built-in variable.");
LExit:
ReleaseStr(scz);
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);
// open registration key
hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration);
if (SUCCEEDED(hr))
{
hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled);
}
// Not finding the key or value is okay.
if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
{
hr = S_OK;
}
pRegistration->fInstalled = (1 == dwInstalled);
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;
DWORD dwResume = 0;
// open registration key
hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration);
if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
{
*pResumeType = BOOTSTRAPPER_RESUME_TYPE_NONE;
ExitFunction1(hr = S_OK);
}
ExitOnFailure(hr, "Failed to open registration key.");
// read Resume value
hr = RegReadNumber(hkRegistration, L"Resume", &dwResume);
if (E_FILENOTFOUND == hr)
{
*pResumeType = BOOTSTRAPPER_RESUME_TYPE_INVALID;
ExitFunction1(hr = S_OK);
}
ExitOnFailure(hr, "Failed to read Resume value.");
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.");
RelatedBundlesSort(&pRegistration->relatedBundles);
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;
DWORD dwSize = 0;
HKEY hkRegistration = NULL;
LPWSTR sczPublisher = NULL;
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 = RegCreate(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration);
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.
// 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->fNoRemoveDefined)
{
hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_NO_REMOVE, (DWORD)pRegistration->fNoRemove);
ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_NO_REMOVE);
}
// Conditionally hide the ARP entry.
if (!pRegistration->fRegisterArp)
{
hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_SYSTEM_COMPONENT, 1);
ExitOnFailure(hr, "Failed to write %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.");
}
// Update estimated size.
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(qwEstimatedSize);
}
hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_ESTIMATED_SIZE, dwSize);
ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_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, FALSE);
ExitOnFailure(hr, "Failed to update resume mode.");
LExit:
ReleaseStr(sczPublisher);
ReleaseRegKey(hkRegistration);
return hr;
}
/*******************************************************************
RegistrationSessionResume - Resumes a previous run session.
*******************************************************************/
extern "C" HRESULT RegistrationSessionResume(
__in BURN_REGISTRATION* pRegistration,
__in BURN_VARIABLES* pVariables,
__in BOOTSTRAPPER_REGISTRATION_TYPE registrationType
)
{
HRESULT hr = S_OK;
HKEY hkRegistration = NULL;
AssertSz(BOOTSTRAPPER_REGISTRATION_TYPE_NONE != registrationType, "Registration type can't be NONE");
// open registration key
hr = RegOpen(pRegistration->hkRoot, pRegistration->sczRegistrationKey, KEY_WRITE, &hkRegistration);
ExitOnFailure(hr, "Failed to open registration key.");
// update resume mode
hr = UpdateResumeMode(pRegistration, hkRegistration, BURN_RESUME_MODE_ACTIVE, FALSE);
ExitOnFailure(hr, "Failed to update resume mode.");
// update display name
hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS == registrationType);
ExitOnFailure(hr, "Failed to update name and publisher.");
LExit:
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 BOOTSTRAPPER_REGISTRATION_TYPE registrationType
)
{
HRESULT hr = S_OK;
HKEY hkRegistration = NULL;
// 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);
if (E_FILENOTFOUND != hr)
{
ExitOnFailure(hr, "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.");
}
// Update resume mode.
hr = UpdateResumeMode(pRegistration, hkRegistration, resumeMode, 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;
// 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);
hr = RegDeleteValue(hkRegistration, sczValueName);
ExitOnFailure(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);
ExitOnFailure(hr, "Failed to get next node.");
hr = XmlGetAttributeEx(pixnNode, L"Filename", &pSoftwareTag->sczFilename);
ExitOnFailure(hr, "Failed to get @Filename.");
hr = XmlGetAttributeEx(pixnNode, L"Regid", &pSoftwareTag->sczRegid);
ExitOnFailure(hr, "Failed to get @Regid.");
hr = XmlGetAttributeEx(pixnNode, L"Path", &pSoftwareTag->sczPath);
ExitOnFailure(hr, "Failed to get @Path.");
hr = XmlGetText(pixnNode, &bstrTagXml);
ExitOnFailure(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"%s\\%s", 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 = PathConcat(sczCacheDirectory, pRegistration->sczExecutableName, &pRegistration->sczCacheExecutablePath);
ExitOnFailure(hr, "Failed to build cached executable path.");
// build state file path
hr = StrAllocFormatted(&pRegistration->sczStateFile, L"%s\\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 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 the Installed value *only* when the mode is ARP. This will tell us
// that the bundle considers itself "installed" on the machine. Note that we
// never change the value to "0" after that. The bundle will be considered
// "uninstalled" when all of the registration is removed.
if (BURN_RESUME_MODE_ARP == resumeMode)
{
// Write Installed value.
hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, 1);
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
{
ExitOnWin32Error(er, 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 ParseRelatedCodes(
__in BURN_REGISTRATION* pRegistration,
__in IXMLDOMNode* pixnBundle
)
{
HRESULT hr = S_OK;
IXMLDOMNodeList* pixnNodes = NULL;
IXMLDOMNode* pixnElement = NULL;
LPWSTR sczAction = NULL;
LPWSTR sczId = NULL;
DWORD cElements = 0;
hr = XmlSelectNodes(pixnBundle, L"RelatedBundle", &pixnNodes);
ExitOnFailure(hr, "Failed to get RelatedBundle nodes");
hr = pixnNodes->get_length((long*)&cElements);
ExitOnFailure(hr, "Failed to get RelatedBundle element count.");
for (DWORD i = 0; i < cElements; ++i)
{
hr = XmlNextElement(pixnNodes, &pixnElement, NULL);
ExitOnFailure(hr, "Failed to get next RelatedBundle element.");
hr = XmlGetAttributeEx(pixnElement, L"Action", &sczAction);
ExitOnFailure(hr, "Failed to get @Action.");
hr = XmlGetAttributeEx(pixnElement, L"Id", &sczId);
ExitOnFailure(hr, "Failed to get @Id.");
if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Detect", -1))
{
hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes + 1, sizeof(LPWSTR), 5);
ExitOnFailure(hr, "Failed to resize Detect code array in registration");
pRegistration->rgsczDetectCodes[pRegistration->cDetectCodes] = sczId;
sczId = NULL;
++pRegistration->cDetectCodes;
}
else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Upgrade", -1))
{
hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes + 1, sizeof(LPWSTR), 5);
ExitOnFailure(hr, "Failed to resize Upgrade code array in registration");
pRegistration->rgsczUpgradeCodes[pRegistration->cUpgradeCodes] = sczId;
sczId = NULL;
++pRegistration->cUpgradeCodes;
}
else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Addon", -1))
{
hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes + 1, sizeof(LPWSTR), 5);
ExitOnFailure(hr, "Failed to resize Addon code array in registration");
pRegistration->rgsczAddonCodes[pRegistration->cAddonCodes] = sczId;
sczId = NULL;
++pRegistration->cAddonCodes;
}
else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Patch", -1))
{
hr = MemEnsureArraySize(reinterpret_cast(&pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes + 1, sizeof(LPWSTR), 5);
ExitOnFailure(hr, "Failed to resize Patch code array in registration");
pRegistration->rgsczPatchCodes[pRegistration->cPatchCodes] = sczId;
sczId = NULL;
++pRegistration->cPatchCodes;
}
else
{
hr = E_INVALIDARG;
ExitOnFailure(hr, "Invalid value for @Action: %ls", sczAction);
}
}
LExit:
ReleaseObject(pixnNodes);
ReleaseObject(pixnElement);
ReleaseStr(sczAction);
ReleaseStr(sczId);
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 = PathConcat(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(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 = PathConcat(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;
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);
if (E_FILENOTFOUND != hr)
{
ExitOnFailure(hr, "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 BOOL IsWuRebootPending()
{
HRESULT hr = S_OK;
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);
if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr)
{
hr = WuaRestartRequired(&fRebootPending);
if (FAILED(hr))
{
fRebootPending = FALSE;
}
::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;
}