// 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" // structs typedef const struct _BUILT_IN_VARIABLE_DECLARATION { LPCWSTR wzVariable; PFN_INITIALIZEVARIABLE pfnInitialize; DWORD_PTR dwpInitializeData; BOOL fPersist; BOOL fOverridable; } BUILT_IN_VARIABLE_DECLARATION; typedef const struct _WELL_KNOWN_VARIABLE_DECLARATION { LPCWSTR wzVariable; BOOL fPersist; } WELL_KNOWN_VARIABLE_DECLARATION; // constants const DWORD GROW_VARIABLE_ARRAY = 3; enum OS_INFO_VARIABLE { OS_INFO_VARIABLE_NONE, OS_INFO_VARIABLE_VersionNT, OS_INFO_VARIABLE_VersionNT64, OS_INFO_VARIABLE_ServicePackLevel, OS_INFO_VARIABLE_NTProductType, OS_INFO_VARIABLE_NTSuiteBackOffice, OS_INFO_VARIABLE_NTSuiteDataCenter, OS_INFO_VARIABLE_NTSuiteEnterprise, OS_INFO_VARIABLE_NTSuitePersonal, OS_INFO_VARIABLE_NTSuiteSmallBusiness, OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted, OS_INFO_VARIABLE_NTSuiteWebServer, OS_INFO_VARIABLE_CompatibilityMode, OS_INFO_VARIABLE_TerminalServer, OS_INFO_VARIABLE_ProcessorArchitecture, OS_INFO_VARIABLE_WindowsBuildNumber, }; enum SET_VARIABLE { SET_VARIABLE_NOT_BUILTIN, SET_VARIABLE_OVERRIDE_BUILTIN, SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS, SET_VARIABLE_ANY, }; // internal function declarations static HRESULT FormatString( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzIn, __out_z_opt LPWSTR* psczOut, __out_opt SIZE_T* pcchOut, __in BOOL fObfuscateHiddenVariables, __out BOOL* pfContainsHiddenVariable ); static HRESULT GetFormatted( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __out_z LPWSTR* psczValue, __out BOOL* pfContainsHiddenVariable ); static HRESULT AddBuiltInVariable( __in BURN_VARIABLES* pVariables, __in LPCWSTR wzVariable, __in PFN_INITIALIZEVARIABLE pfnInitialize, __in DWORD_PTR dwpInitializeData, __in BOOL fPersist, __in BOOL fOverridable ); static HRESULT AddWellKnownVariable( __in BURN_VARIABLES* pVariables, __in LPCWSTR wzVariable, __in BOOL fPersisted ); static HRESULT GetVariable( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __out BURN_VARIABLE** ppVariable ); static HRESULT FindVariableIndexByName( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __out DWORD* piVariable ); static HRESULT InsertUserVariable( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in DWORD iPosition ); static HRESULT InsertVariable( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in DWORD iPosition ); static HRESULT SetVariableValue( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in BURN_VARIANT* pVariant, __in SET_VARIABLE setBuiltin, __in BOOL fLog ); static HRESULT InitializeVariableVersionNT( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableNativeMachine( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableOsInfo( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableSystemInfo( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableComputerName( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableVersionMsi( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableCsidlFolder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableWindowsVolumeFolder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableTempFolder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableSystemFolder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariablePrivileged( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableProcessTokenPrivilege( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeSystemLanguageID( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeUserUILanguageID( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeUserLanguageID( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableString( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableNumeric( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariable6432Folder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableDate( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableInstallerName( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableInstallerVersion( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableInstallerInformationalVersion( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableVersion( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT InitializeVariableLogonUser( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); static HRESULT Get64bitFolderFromRegistry( __in int nFolder, __deref_out_z LPWSTR* psczPath ); #if !defined(_WIN64) static HRESULT InitializeVariableRegistryFolder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ); #endif // function definitions extern "C" HRESULT VariableInitialize( __in BURN_VARIABLES* pVariables ) { HRESULT hr = S_OK; ::InitializeCriticalSection(&pVariables->csAccess); const BUILT_IN_VARIABLE_DECLARATION vrgBuiltInVariables[] = { {L"AdminToolsFolder", InitializeVariableCsidlFolder, CSIDL_ADMINTOOLS}, {L"AppDataFolder", InitializeVariableCsidlFolder, CSIDL_APPDATA}, {L"CommonAppDataFolder", InitializeVariableCsidlFolder, CSIDL_COMMON_APPDATA}, #if defined(_WIN64) {L"CommonFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON}, {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMONX86}, #else {L"CommonFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES_COMMON}, {L"CommonFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES_COMMON}, #endif {L"CommonFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES_COMMON}, {L"CompatibilityMode", InitializeVariableOsInfo, OS_INFO_VARIABLE_CompatibilityMode}, {VARIABLE_DATE, InitializeVariableDate, 0}, {L"ComputerName", InitializeVariableComputerName, 0}, {L"DesktopFolder", InitializeVariableCsidlFolder, CSIDL_DESKTOP}, {L"FavoritesFolder", InitializeVariableCsidlFolder, CSIDL_FAVORITES}, {L"FontsFolder", InitializeVariableCsidlFolder, CSIDL_FONTS}, {VARIABLE_INSTALLERNAME, InitializeVariableInstallerName, 0}, {VARIABLE_INSTALLERVERSION, InitializeVariableInstallerVersion, 0}, {VARIABLE_INSTALLERINFORMATIONALVERSION, InitializeVariableInstallerInformationalVersion, 0}, {L"LocalAppDataFolder", InitializeVariableCsidlFolder, CSIDL_LOCAL_APPDATA}, {VARIABLE_LOGONUSER, InitializeVariableLogonUser, 0}, {L"MyPicturesFolder", InitializeVariableCsidlFolder, CSIDL_MYPICTURES}, {L"NativeMachine", InitializeVariableNativeMachine, 0}, {L"NTProductType", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTProductType}, {L"NTSuiteBackOffice", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteBackOffice}, {L"NTSuiteDataCenter", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteDataCenter}, {L"NTSuiteEnterprise", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteEnterprise}, {L"NTSuitePersonal", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuitePersonal}, {L"NTSuiteSmallBusiness", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusiness}, {L"NTSuiteSmallBusinessRestricted", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted}, {L"NTSuiteWebServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_NTSuiteWebServer}, {L"PersonalFolder", InitializeVariableCsidlFolder, CSIDL_PERSONAL}, {L"Privileged", InitializeVariablePrivileged, 0}, {L"ProcessorArchitecture", InitializeVariableSystemInfo, OS_INFO_VARIABLE_ProcessorArchitecture}, #if defined(_WIN64) {L"ProgramFiles64Folder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES}, {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILESX86}, #else {L"ProgramFiles64Folder", InitializeVariableRegistryFolder, CSIDL_PROGRAM_FILES}, {L"ProgramFilesFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAM_FILES}, #endif {L"ProgramFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES}, {L"ProgramMenuFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAMS}, {VARIABLE_REBOOTPENDING, InitializeVariableNumeric, 0}, {L"SendToFolder", InitializeVariableCsidlFolder, CSIDL_SENDTO}, {L"ServicePackLevel", InitializeVariableVersionNT, OS_INFO_VARIABLE_ServicePackLevel}, {L"StartMenuFolder", InitializeVariableCsidlFolder, CSIDL_STARTMENU}, {L"StartupFolder", InitializeVariableCsidlFolder, CSIDL_STARTUP}, {L"SystemFolder", InitializeVariableSystemFolder, FALSE}, {L"System64Folder", InitializeVariableSystemFolder, TRUE}, {L"SystemLanguageID", InitializeSystemLanguageID, 0}, {L"TempFolder", InitializeVariableTempFolder, 0}, {L"TemplateFolder", InitializeVariableCsidlFolder, CSIDL_TEMPLATES}, {L"TerminalServer", InitializeVariableOsInfo, OS_INFO_VARIABLE_TerminalServer}, {L"UserUILanguageID", InitializeUserUILanguageID, 0}, {L"UserLanguageID", InitializeUserLanguageID, 0}, {L"VersionMsi", InitializeVariableVersionMsi, 0}, {L"VersionNT", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT}, {L"VersionNT64", InitializeVariableVersionNT, OS_INFO_VARIABLE_VersionNT64}, {L"WindowsBuildNumber", InitializeVariableVersionNT, OS_INFO_VARIABLE_WindowsBuildNumber}, {L"WindowsFolder", InitializeVariableCsidlFolder, CSIDL_WINDOWS}, {L"WindowsVolume", InitializeVariableWindowsVolumeFolder, 0}, {BURN_BUNDLE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE}, {L"WixCanRestart", InitializeVariableProcessTokenPrivilege, (DWORD_PTR)SE_SHUTDOWN_NAME}, {BURN_BUNDLE_COMMAND_LINE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE}, {BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, InitializeVariableString, NULL, FALSE, TRUE}, {BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, InitializeVariableString, NULL, FALSE, TRUE}, {BURN_BUNDLE_FORCED_RESTART_PACKAGE, InitializeVariableString, NULL, TRUE, TRUE}, {BURN_BUNDLE_INSTALLED, InitializeVariableNumeric, 0}, {BURN_BUNDLE_ELEVATED, InitializeVariableNumeric, 0, FALSE, TRUE}, {BURN_BUNDLE_ACTIVE_PARENT, InitializeVariableString, NULL, FALSE, TRUE}, {BURN_BUNDLE_PROVIDER_KEY, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, {BURN_BUNDLE_TAG, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, {BURN_BUNDLE_UILEVEL, InitializeVariableNumeric, 0, FALSE, TRUE}, {BURN_BUNDLE_VERSION, InitializeVariableVersion, (DWORD_PTR)L"0", FALSE, TRUE}, }; const WELL_KNOWN_VARIABLE_DECLARATION vrgWellKnownVariableNames[] = { { BURN_BUNDLE_LAYOUT_DIRECTORY }, { BURN_BUNDLE_NAME, TRUE }, { BURN_BUNDLE_INPROGRESS_NAME, TRUE }, { BURN_BUNDLE_LAST_USED_SOURCE, TRUE }, { BURN_BUNDLE_MANUFACTURER, TRUE }, { BURN_BUNDLE_ORIGINAL_SOURCE, TRUE }, { BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER, TRUE }, }; for (DWORD i = 0; i < countof(vrgBuiltInVariables); ++i) { BUILT_IN_VARIABLE_DECLARATION* pBuiltInVariable = &vrgBuiltInVariables[i]; hr = AddBuiltInVariable(pVariables, pBuiltInVariable->wzVariable, pBuiltInVariable->pfnInitialize, pBuiltInVariable->dwpInitializeData, pBuiltInVariable->fPersist, pBuiltInVariable->fOverridable); ExitOnFailure(hr, "Failed to add built-in variable: %ls.", pBuiltInVariable->wzVariable); } for (DWORD i = 0; i < countof(vrgWellKnownVariableNames); ++i) { WELL_KNOWN_VARIABLE_DECLARATION* pWellKnownVariable = &vrgWellKnownVariableNames[i]; hr = AddWellKnownVariable(pVariables, pWellKnownVariable->wzVariable, pWellKnownVariable->fPersist); ExitOnFailure(hr, "Failed to add well-known variable: %ls.", pWellKnownVariable->wzVariable); } LExit: return hr; } extern "C" HRESULT VariablesParseFromXml( __in BURN_VARIABLES* pVariables, __in IXMLDOMNode* pixnBundle ) { HRESULT hr = S_OK; BOOL fXmlFound = FALSE; IXMLDOMNodeList* pixnNodes = NULL; IXMLDOMNode* pixnNode = NULL; DWORD cNodes = 0; LPWSTR sczId = NULL; LPWSTR scz = NULL; BURN_VARIANT value = { }; BURN_VARIANT_TYPE valueType = BURN_VARIANT_TYPE_NONE; BOOL fHidden = FALSE; BOOL fPersisted = FALSE; DWORD iVariable = 0; ::EnterCriticalSection(&pVariables->csAccess); // select variable nodes hr = XmlSelectNodes(pixnBundle, L"Variable", &pixnNodes); ExitOnFailure(hr, "Failed to select variable nodes."); // get variable node count hr = pixnNodes->get_length((long*)&cNodes); ExitOnFailure(hr, "Failed to get variable node count."); // parse variable elements for (DWORD i = 0; i < cNodes; ++i) { hr = XmlNextElement(pixnNodes, &pixnNode, NULL); ExitOnFailure(hr, "Failed to get next node."); // @Id hr = XmlGetAttributeEx(pixnNode, L"Id", &sczId); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Id."); // @Hidden hr = XmlGetYesNoAttribute(pixnNode, L"Hidden", &fHidden); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Hidden."); // @Persisted hr = XmlGetYesNoAttribute(pixnNode, L"Persisted", &fPersisted); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Persisted."); // @Value hr = XmlGetAttributeEx(pixnNode, L"Value", &scz); ExitOnOptionalXmlQueryFailure(hr, fXmlFound, "Failed to get @Value."); if (fXmlFound) { hr = BVariantSetString(&value, scz, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); // @Type hr = XmlGetAttributeEx(pixnNode, L"Type", &scz); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Type."); if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"formatted", -1)) { if (!fHidden) { LogStringLine(REPORT_STANDARD, "Initializing formatted variable '%ls' to value '%ls'", sczId, value.sczValue); } valueType = BURN_VARIANT_TYPE_FORMATTED; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"numeric", -1)) { if (!fHidden) { LogStringLine(REPORT_STANDARD, "Initializing numeric variable '%ls' to value '%ls'", sczId, value.sczValue); } valueType = BURN_VARIANT_TYPE_NUMERIC; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"string", -1)) { if (!fHidden) { LogStringLine(REPORT_STANDARD, "Initializing string variable '%ls' to value '%ls'", sczId, value.sczValue); } valueType = BURN_VARIANT_TYPE_STRING; } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, scz, -1, L"version", -1)) { if (!fHidden) { LogStringLine(REPORT_STANDARD, "Initializing version variable '%ls' to value '%ls'", sczId, value.sczValue); } valueType = BURN_VARIANT_TYPE_VERSION; } else { ExitWithRootFailure(hr, E_INVALIDARG, "Invalid value for @Type: %ls", scz); } } else { valueType = BURN_VARIANT_TYPE_NONE; } if (fHidden) { LogStringLine(REPORT_STANDARD, "Initializing hidden variable '%ls'", sczId); } // change value variant to correct type hr = BVariantChangeType(&value, valueType); ExitOnFailure(hr, "Failed to change variant type."); if (BURN_VARIANT_TYPE_VERSION == valueType && value.pValue->fInvalid) { LogId(REPORT_WARNING, MSG_VARIABLE_INVALID_VERSION, sczId); } // find existing variable hr = FindVariableIndexByName(pVariables, sczId, &iVariable); ExitOnFailure(hr, "Failed to find variable value '%ls'.", sczId); // insert element if not found if (S_FALSE == hr) { hr = InsertUserVariable(pVariables, sczId, iVariable); ExitOnFailure(hr, "Failed to insert variable '%ls'.", sczId); } else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) { ExitWithRootFailure(hr, E_INVALIDARG, "Attempt to add built-in variable: %ls", sczId); } else { ExitWithRootFailure(hr, E_INVALIDARG, "Attempt to add variable again: %ls", sczId); } pVariables->rgVariables[iVariable].fHidden = fHidden; pVariables->rgVariables[iVariable].fPersisted = fPersisted; // update variable value hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, &value); ExitOnFailure(hr, "Failed to set value of variable: %ls", sczId); // prepare next iteration ReleaseNullObject(pixnNode); BVariantUninitialize(&value); ReleaseNullStrSecure(scz); } LExit: ::LeaveCriticalSection(&pVariables->csAccess); ReleaseObject(pixnNodes); ReleaseObject(pixnNode); ReleaseStr(scz); ReleaseStr(sczId); BVariantUninitialize(&value); return hr; } extern "C" void VariablesUninitialize( __in BURN_VARIABLES* pVariables ) { ::DeleteCriticalSection(&pVariables->csAccess); if (pVariables->rgVariables) { for (DWORD i = 0; i < pVariables->cVariables; ++i) { BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; if (pVariable) { ReleaseStr(pVariable->sczName); BVariantUninitialize(&pVariable->Value); } } MemFree(pVariables->rgVariables); } } extern "C" void VariablesDump( __in BURN_VARIABLES* pVariables ) { HRESULT hr = S_OK; LPWSTR sczValue = NULL; for (DWORD i = 0; i < pVariables->cVariables; ++i) { BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; if (pVariable && BURN_VARIANT_TYPE_NONE != pVariable->Value.Type) { hr = StrAllocFormatted(&sczValue, L"%ls = [%ls]", pVariable->sczName, pVariable->sczName); if (SUCCEEDED(hr)) { if (pVariable->fHidden) { hr = VariableFormatStringObfuscated(pVariables, sczValue, &sczValue, NULL); } else { hr = VariableFormatString(pVariables, sczValue, &sczValue, NULL); } } if (FAILED(hr)) { // already logged; best-effort to dump the rest on our way out the door continue; } LogId(REPORT_VERBOSE, MSG_VARIABLE_DUMP, sczValue); ReleaseNullStrSecure(sczValue); } } StrSecureZeroFreeString(sczValue); } extern "C" HRESULT VariableGetNumeric( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __out LONGLONG* pllValue ) { HRESULT hr = S_OK; BURN_VARIABLE* pVariable = NULL; ::EnterCriticalSection(&pVariables->csAccess); hr = GetVariable(pVariables, wzVariable, &pVariable); if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) { ExitFunction1(hr = E_NOTFOUND); } else if (E_NOTFOUND == hr) { ExitFunction(); } ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); hr = BVariantGetNumeric(&pVariable->Value, pllValue); ExitOnFailure(hr, "Failed to get value as numeric for variable: %ls", wzVariable); LExit: ::LeaveCriticalSection(&pVariables->csAccess); return hr; } extern "C" HRESULT VariableGetString( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __out_z LPWSTR* psczValue ) { HRESULT hr = S_OK; BURN_VARIABLE* pVariable = NULL; ::EnterCriticalSection(&pVariables->csAccess); hr = GetVariable(pVariables, wzVariable, &pVariable); if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) { ExitFunction1(hr = E_NOTFOUND); } else if (E_NOTFOUND == hr) { ExitFunction(); } ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); hr = BVariantGetString(&pVariable->Value, psczValue); ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable); LExit: ::LeaveCriticalSection(&pVariables->csAccess); return hr; } extern "C" HRESULT VariableGetVersion( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in VERUTIL_VERSION** ppValue ) { HRESULT hr = S_OK; BURN_VARIABLE* pVariable = NULL; ::EnterCriticalSection(&pVariables->csAccess); hr = GetVariable(pVariables, wzVariable, &pVariable); if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) { ExitFunction1(hr = E_NOTFOUND); } else if (E_NOTFOUND == hr) { ExitFunction(); } ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); hr = BVariantGetVersionHidden(&pVariable->Value, pVariable->fHidden, ppValue); ExitOnFailure(hr, "Failed to get value as version for variable: %ls", wzVariable); LExit: ::LeaveCriticalSection(&pVariables->csAccess); return hr; } extern "C" HRESULT VariableGetVariant( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in BURN_VARIANT* pValue ) { HRESULT hr = S_OK; BURN_VARIABLE* pVariable = NULL; ::EnterCriticalSection(&pVariables->csAccess); hr = GetVariable(pVariables, wzVariable, &pVariable); if (E_NOTFOUND == hr) { ExitFunction(); } ExitOnFailure(hr, "Failed to get value of variable: %ls", wzVariable); hr = BVariantCopy(&pVariable->Value, pValue); ExitOnFailure(hr, "Failed to copy value of variable: %ls", wzVariable); LExit: ::LeaveCriticalSection(&pVariables->csAccess); return hr; } extern "C" HRESULT VariableGetFormatted( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __out_z LPWSTR* psczValue, __out BOOL* pfContainsHiddenVariable ) { HRESULT hr = S_OK; if (pfContainsHiddenVariable) { *pfContainsHiddenVariable = FALSE; } hr = GetFormatted(pVariables, wzVariable, psczValue, pfContainsHiddenVariable); return hr; } extern "C" HRESULT VariableSetNumeric( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in LONGLONG llValue, __in BOOL fOverwriteBuiltIn ) { BURN_VARIANT variant = { }; variant.llValue = llValue; variant.Type = BURN_VARIANT_TYPE_NUMERIC; return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); } extern "C" HRESULT VariableSetString( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in_z_opt LPCWSTR wzValue, __in BOOL fOverwriteBuiltIn, __in BOOL fFormatted ) { BURN_VARIANT variant = { }; variant.sczValue = (LPWSTR)wzValue; variant.Type = fFormatted ? BURN_VARIANT_TYPE_FORMATTED : BURN_VARIANT_TYPE_STRING; return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); } extern "C" HRESULT VariableSetVersion( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in VERUTIL_VERSION* pValue, __in BOOL fOverwriteBuiltIn ) { BURN_VARIANT variant = { }; variant.pValue = pValue; variant.Type = BURN_VARIANT_TYPE_VERSION; return SetVariableValue(pVariables, wzVariable, &variant, fOverwriteBuiltIn ? SET_VARIABLE_OVERRIDE_BUILTIN : SET_VARIABLE_NOT_BUILTIN, TRUE); } extern "C" HRESULT VariableSetVariant( __in BURN_VARIABLES * pVariables, __in_z LPCWSTR wzVariable, __in BURN_VARIANT * pVariant ) { return SetVariableValue(pVariables, wzVariable, pVariant, SET_VARIABLE_NOT_BUILTIN, TRUE); } extern "C" HRESULT VariableFormatString( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzIn, __out_z_opt LPWSTR* psczOut, __out_opt SIZE_T* pcchOut ) { return FormatString(pVariables, wzIn, psczOut, pcchOut, FALSE, NULL); } extern "C" HRESULT VariableFormatStringObfuscated( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzIn, __out_z_opt LPWSTR* psczOut, __out_opt SIZE_T* pcchOut ) { return FormatString(pVariables, wzIn, psczOut, pcchOut, TRUE, NULL); } extern "C" HRESULT VariableEscapeString( __in_z LPCWSTR wzIn, __out_z LPWSTR* psczOut ) { HRESULT hr = S_OK; LPCWSTR wzRead = NULL; LPWSTR pwzEscaped = NULL; LPWSTR pwz = NULL; SIZE_T i = 0; // allocate buffer for escaped string hr = StrAlloc(&pwzEscaped, lstrlenW(wzIn) + 1); ExitOnFailure(hr, "Failed to allocate buffer for escaped string."); // read through string and move characters, inserting escapes as needed wzRead = wzIn; for (;;) { // find next character needing escaping i = wcscspn(wzRead, L"[]{}"); // copy skipped characters if (0 < i) { hr = StrAllocConcat(&pwzEscaped, wzRead, i); ExitOnFailure(hr, "Failed to append characters."); } if (L'\0' == wzRead[i]) { break; // end reached } // escape character hr = StrAllocFormatted(&pwz, L"[\\%c]", wzRead[i]); ExitOnFailure(hr, "Failed to format escape sequence."); hr = StrAllocConcat(&pwzEscaped, pwz, 0); ExitOnFailure(hr, "Failed to append escape sequence."); // update read pointer wzRead += i + 1; } // return value hr = StrAllocString(psczOut, pwzEscaped, 0); ExitOnFailure(hr, "Failed to copy string."); LExit: ReleaseStr(pwzEscaped); ReleaseStr(pwz); return hr; } extern "C" HRESULT VariableSerialize( __in BURN_VARIABLES* pVariables, __in BOOL fPersisting, __inout BYTE** ppbBuffer, __inout SIZE_T* piBuffer ) { HRESULT hr = S_OK; BOOL fIncluded = FALSE; LONGLONG ll = 0; LPWSTR scz = NULL; ::EnterCriticalSection(&pVariables->csAccess); // Write variable count. hr = BuffWriteNumber(ppbBuffer, piBuffer, pVariables->cVariables); ExitOnFailure(hr, "Failed to write variable count."); // Write variables. for (DWORD i = 0; i < pVariables->cVariables; ++i) { BURN_VARIABLE* pVariable = &pVariables->rgVariables[i]; // If we aren't persisting, include only variables that aren't rejected by the elevated process. // If we are persisting, include only variables that should be persisted. fIncluded = (!fPersisting && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariable->internalType) || (fPersisting && pVariable->fPersisted); // Write included flag. hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)fIncluded); ExitOnFailure(hr, "Failed to write included flag."); if (!fIncluded) { continue; } // Write variable name. hr = BuffWriteString(ppbBuffer, piBuffer, pVariable->sczName); ExitOnFailure(hr, "Failed to write variable name."); // Write variable value type. hr = BuffWriteNumber(ppbBuffer, piBuffer, (DWORD)pVariable->Value.Type); ExitOnFailure(hr, "Failed to write variable value type."); // Write variable value. switch (pVariable->Value.Type) { case BURN_VARIANT_TYPE_NONE: break; case BURN_VARIANT_TYPE_NUMERIC: hr = BVariantGetNumeric(&pVariable->Value, &ll); ExitOnFailure(hr, "Failed to get numeric."); hr = BuffWriteNumber64(ppbBuffer, piBuffer, static_cast(ll)); ExitOnFailure(hr, "Failed to write variable value as number."); SecureZeroMemory(&ll, sizeof(ll)); break; case BURN_VARIANT_TYPE_VERSION: __fallthrough; case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BVariantGetString(&pVariable->Value, &scz); ExitOnFailure(hr, "Failed to get string."); hr = BuffWriteString(ppbBuffer, piBuffer, scz); ExitOnFailure(hr, "Failed to write variable value as string."); ReleaseNullStrSecure(scz); break; default: hr = E_INVALIDARG; ExitOnFailure(hr, "Unsupported variable type."); } } LExit: ::LeaveCriticalSection(&pVariables->csAccess); SecureZeroMemory(&ll, sizeof(ll)); StrSecureZeroFreeString(scz); return hr; } extern "C" HRESULT VariableDeserialize( __in BURN_VARIABLES* pVariables, __in BOOL fWasPersisted, __in_bcount(cbBuffer) BYTE* pbBuffer, __in SIZE_T cbBuffer, __inout SIZE_T* piBuffer ) { HRESULT hr = S_OK; DWORD cVariables = 0; LPWSTR sczName = NULL; BOOL fIncluded = FALSE; BURN_VARIANT value = { }; LPWSTR scz = NULL; DWORD64 qw = 0; VERUTIL_VERSION* pVersion = NULL; ::EnterCriticalSection(&pVariables->csAccess); // Read variable count. hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, &cVariables); ExitOnFailure(hr, "Failed to read variable count."); // Read variables. for (DWORD i = 0; i < cVariables; ++i) { // Read variable included flag. hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&fIncluded); ExitOnFailure(hr, "Failed to read variable included flag."); if (!fIncluded) { continue; // if variable is not included, skip. } // Read variable name. hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &sczName); ExitOnFailure(hr, "Failed to read variable name."); // Read variable value type. hr = BuffReadNumber(pbBuffer, cbBuffer, piBuffer, (DWORD*)&value.Type); ExitOnFailure(hr, "Failed to read variable value type."); // Read variable value. switch (value.Type) { case BURN_VARIANT_TYPE_NONE: break; case BURN_VARIANT_TYPE_NUMERIC: hr = BuffReadNumber64(pbBuffer, cbBuffer, piBuffer, &qw); ExitOnFailure(hr, "Failed to read variable value as number."); hr = BVariantSetNumeric(&value, static_cast(qw)); ExitOnFailure(hr, "Failed to set variable value."); SecureZeroMemory(&qw, sizeof(qw)); break; case BURN_VARIANT_TYPE_VERSION: hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz); ExitOnFailure(hr, "Failed to read variable value as string."); hr = VerParseVersion(scz, 0, FALSE, &pVersion); ExitOnFailure(hr, "Failed to parse variable value as version."); hr = BVariantSetVersion(&value, pVersion); ExitOnFailure(hr, "Failed to set variable value."); SecureZeroMemory(&qw, sizeof(qw)); break; case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: hr = BuffReadString(pbBuffer, cbBuffer, piBuffer, &scz); ExitOnFailure(hr, "Failed to read variable value as string."); hr = BVariantSetString(&value, scz, NULL, BURN_VARIANT_TYPE_FORMATTED == value.Type); ExitOnFailure(hr, "Failed to set variable value."); ReleaseNullStrSecure(scz); break; default: hr = E_INVALIDARG; ExitOnFailure(hr, "Unsupported variable type."); } // Set variable. hr = SetVariableValue(pVariables, sczName, &value, fWasPersisted ? SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS : SET_VARIABLE_ANY, FALSE); ExitOnFailure(hr, "Failed to set variable."); // Clean up. BVariantUninitialize(&value); } LExit: ::LeaveCriticalSection(&pVariables->csAccess); ReleaseVerutilVersion(pVersion); ReleaseStr(sczName); BVariantUninitialize(&value); SecureZeroMemory(&qw, sizeof(qw)); StrSecureZeroFreeString(scz); return hr; } extern "C" HRESULT VariableStrAlloc( __in BOOL fZeroOnRealloc, __deref_out_ecount_part(cch, 0) LPWSTR* ppwz, __in DWORD_PTR cch ) { HRESULT hr = S_OK; if (fZeroOnRealloc) { hr = StrAllocSecure(ppwz, cch); } else { hr = StrAlloc(ppwz, cch); } return hr; } extern "C" HRESULT VariableStrAllocString( __in BOOL fZeroOnRealloc, __deref_out_ecount_z(cchSource + 1) LPWSTR* ppwz, __in_z LPCWSTR wzSource, __in DWORD_PTR cchSource ) { HRESULT hr = S_OK; if (fZeroOnRealloc) { hr = StrAllocStringSecure(ppwz, wzSource, cchSource); } else { hr = StrAllocString(ppwz, wzSource, cchSource); } return hr; } extern "C" HRESULT VariableStrAllocConcat( __in BOOL fZeroOnRealloc, __deref_out_z LPWSTR* ppwz, __in_z LPCWSTR wzSource, __in DWORD_PTR cchSource ) { HRESULT hr = S_OK; if (fZeroOnRealloc) { hr = StrAllocConcatSecure(ppwz, wzSource, cchSource); } else { hr = StrAllocConcat(ppwz, wzSource, cchSource); } return hr; } extern "C" HRESULT __cdecl VariableStrAllocFormatted( __in BOOL fZeroOnRealloc, __deref_out_z LPWSTR* ppwz, __in __format_string LPCWSTR wzFormat, ... ) { HRESULT hr = S_OK; va_list args; va_start(args, wzFormat); if (fZeroOnRealloc) { hr = StrAllocFormattedArgsSecure(ppwz, wzFormat, args); } else { hr = StrAllocFormattedArgs(ppwz, wzFormat, args); } va_end(args); return hr; } extern "C" HRESULT VariableIsHidden( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __out BOOL* pfHidden ) { HRESULT hr = S_OK; BURN_VARIABLE* pVariable = NULL; ::EnterCriticalSection(&pVariables->csAccess); hr = GetVariable(pVariables, wzVariable, &pVariable); if (E_NOTFOUND == hr) { // A missing variable does not need its data hidden. *pfHidden = FALSE; ExitFunction1(hr = S_OK); } ExitOnFailure(hr, "Failed to get visibility of variable: %ls", wzVariable); *pfHidden = pVariable->fHidden; LExit: ::LeaveCriticalSection(&pVariables->csAccess); return hr; } extern "C" BOOL VariableIsHiddenCommandLine( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable ) { BURN_VARIABLE* pVariable = NULL; BOOL fHidden = FALSE; ::EnterCriticalSection(&pVariables->csAccess); for (DWORD i = 0; i < pVariables->cVariables; ++i) { pVariable = pVariables->rgVariables + i; if (pVariable->fHidden && CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pVariable->sczName, -1, wzVariable, -1)) { fHidden = TRUE; break; } } ::LeaveCriticalSection(&pVariables->csAccess); return fHidden; } // internal function definitions static HRESULT FormatString( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzIn, __out_z_opt LPWSTR* psczOut, __out_opt SIZE_T* pcchOut, __in BOOL fObfuscateHiddenVariables, __out BOOL* pfContainsHiddenVariable ) { HRESULT hr = S_OK; DWORD er = ERROR_SUCCESS; LPWSTR sczUnformatted = NULL; LPWSTR sczFormat = NULL; LPCWSTR wzRead = NULL; LPCWSTR wzOpen = NULL; LPCWSTR wzClose = NULL; LPWSTR scz = NULL; LPWSTR* rgVariables = NULL; DWORD cVariables = 0; DWORD cch = 0; size_t cchIn = 0; BOOL fHidden = FALSE; MSIHANDLE hRecord = NULL; ::EnterCriticalSection(&pVariables->csAccess); // allocate buffer for format string hr = ::StringCchLengthW(wzIn, STRSAFE_MAX_LENGTH, &cchIn); ExitOnFailure(hr, "Failed to length of format string."); hr = StrAlloc(&sczFormat, cchIn + 1); ExitOnFailure(hr, "Failed to allocate buffer for format string."); // read out variables from the unformatted string and build a format string wzRead = wzIn; for (;;) { // scan for opening '[' wzOpen = wcschr(wzRead, L'['); if (!wzOpen) { // end reached, append the remainder of the string and end loop hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0); ExitOnFailure(hr, "Failed to append string."); break; } // scan for closing ']' wzClose = wcschr(wzOpen + 1, L']'); if (!wzClose) { // end reached, treat unterminated expander as literal hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, 0); ExitOnFailure(hr, "Failed to append string."); break; } cch = (DWORD)(wzClose - wzOpen - 1); if (0 == cch) { // blank, copy all text including the terminator hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzClose - wzRead) + 1); ExitOnFailure(hr, "Failed to append string."); } else { // append text preceding expander if (wzOpen > wzRead) { hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, wzRead, (DWORD_PTR)(wzOpen - wzRead)); ExitOnFailure(hr, "Failed to append string."); } // get variable name hr = VariableStrAllocString(!fObfuscateHiddenVariables, &scz, wzOpen + 1, cch); ExitOnFailure(hr, "Failed to get variable name."); // allocate space in variable array if (rgVariables) { LPVOID pv = MemReAlloc(rgVariables, sizeof(LPWSTR) * (cVariables + 1), TRUE); ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to reallocate variable array."); rgVariables = (LPWSTR*)pv; } else { rgVariables = (LPWSTR*)MemAlloc(sizeof(LPWSTR) * (cVariables + 1), TRUE); ExitOnNull(rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate variable array."); } // set variable value if (2 <= cch && L'\\' == wzOpen[1]) { // escape sequence, copy character hr = VariableStrAllocString(!fObfuscateHiddenVariables, &rgVariables[cVariables], &wzOpen[2], 1); } else { hr = VariableIsHidden(pVariables, scz, &fHidden); ExitOnFailure(hr, "Failed to determine variable visibility: '%ls'.", scz); if (pfContainsHiddenVariable) { *pfContainsHiddenVariable |= fHidden; } if (fObfuscateHiddenVariables && fHidden) { hr = StrAllocString(&rgVariables[cVariables], L"*****", 0); } else { // get formatted variable value hr = GetFormatted(pVariables, scz, &rgVariables[cVariables], pfContainsHiddenVariable); if (E_NOTFOUND == hr) // variable not found { hr = StrAllocStringSecure(&rgVariables[cVariables], L"", 0); } } } ExitOnFailure(hr, "Failed to set variable value."); ++cVariables; // append placeholder to format string hr = VariableStrAllocFormatted(!fObfuscateHiddenVariables, &scz, L"[%d]", cVariables); ExitOnFailure(hr, "Failed to format placeholder string."); hr = VariableStrAllocConcat(!fObfuscateHiddenVariables, &sczFormat, scz, 0); ExitOnFailure(hr, "Failed to append placeholder."); } // update read pointer wzRead = wzClose + 1; } // create record hRecord = ::MsiCreateRecord(cVariables); ExitOnNull(hRecord, hr, E_OUTOFMEMORY, "Failed to allocate record."); // set format string er = ::MsiRecordSetStringW(hRecord, 0, sczFormat); ExitOnWin32Error(er, hr, "Failed to set record format string."); // copy record fields for (DWORD i = 0; i < cVariables; ++i) { if (*rgVariables[i]) // not setting if blank { er = ::MsiRecordSetStringW(hRecord, i + 1, rgVariables[i]); ExitOnWin32Error(er, hr, "Failed to set record string."); } } // get formatted character count cch = 0; #pragma prefast(push) #pragma prefast(disable:6298) er = ::MsiFormatRecordW(NULL, hRecord, L"", &cch); #pragma prefast(pop) if (ERROR_MORE_DATA != er) { ExitOnWin32Error(er, hr, "Failed to get formatted length."); } // return formatted string if (psczOut) { hr = VariableStrAlloc(!fObfuscateHiddenVariables, &scz, ++cch); ExitOnFailure(hr, "Failed to allocate string."); er = ::MsiFormatRecordW(NULL, hRecord, scz, &cch); ExitOnWin32Error(er, hr, "Failed to format record."); hr = VariableStrAllocString(!fObfuscateHiddenVariables, psczOut, scz, 0); ExitOnFailure(hr, "Failed to copy string."); } // return character count if (pcchOut) { *pcchOut = cch; } LExit: ::LeaveCriticalSection(&pVariables->csAccess); if (rgVariables) { for (DWORD i = 0; i < cVariables; ++i) { if (fObfuscateHiddenVariables) { ReleaseStr(rgVariables[i]); } else { StrSecureZeroFreeString(rgVariables[i]); } } MemFree(rgVariables); } if (hRecord) { ::MsiCloseHandle(hRecord); } if (fObfuscateHiddenVariables) { ReleaseStr(sczUnformatted); ReleaseStr(sczFormat); ReleaseStr(scz); } else { StrSecureZeroFreeString(sczUnformatted); StrSecureZeroFreeString(sczFormat); StrSecureZeroFreeString(scz); } return hr; } static HRESULT GetFormatted( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __out_z LPWSTR* psczValue, __out BOOL* pfContainsHiddenVariable ) { HRESULT hr = S_OK; BURN_VARIABLE* pVariable = NULL; LPWSTR scz = NULL; ::EnterCriticalSection(&pVariables->csAccess); hr = GetVariable(pVariables, wzVariable, &pVariable); if (SUCCEEDED(hr) && BURN_VARIANT_TYPE_NONE == pVariable->Value.Type) { ExitFunction1(hr = E_NOTFOUND); } else if (E_NOTFOUND == hr) { ExitFunction(); } ExitOnFailure(hr, "Failed to get variable: %ls", wzVariable); if (pfContainsHiddenVariable) { *pfContainsHiddenVariable |= pVariable->fHidden; } if (BURN_VARIANT_TYPE_FORMATTED == pVariable->Value.Type) { hr = BVariantGetString(&pVariable->Value, &scz); ExitOnFailure(hr, "Failed to get unformatted string."); hr = FormatString(pVariables, scz, psczValue, NULL, FALSE, pfContainsHiddenVariable); ExitOnFailure(hr, "Failed to format value '%ls' of variable: %ls", pVariable->fHidden ? L"*****" : pVariable->Value.sczValue, wzVariable); } else { hr = BVariantGetString(&pVariable->Value, psczValue); ExitOnFailure(hr, "Failed to get value as string for variable: %ls", wzVariable); } LExit: ::LeaveCriticalSection(&pVariables->csAccess); StrSecureZeroFreeString(scz); return hr; } static HRESULT AddBuiltInVariable( __in BURN_VARIABLES* pVariables, __in LPCWSTR wzVariable, __in PFN_INITIALIZEVARIABLE pfnInitialize, __in DWORD_PTR dwpInitializeData, __in BOOL fPersist, __in BOOL fOverridable ) { HRESULT hr = S_OK; DWORD iVariable = 0; BURN_VARIABLE* pVariable = NULL; hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); ExitOnFailure(hr, "Failed to find variable value."); // insert element if not found if (S_FALSE == hr) { hr = InsertVariable(pVariables, wzVariable, iVariable); ExitOnFailure(hr, "Failed to insert variable."); } else { ExitWithRootFailure(hr, E_INVALIDSTATE, "Attempted to add built-in variable again: %ls", wzVariable); } // set variable details pVariable = &pVariables->rgVariables[iVariable]; pVariable->fPersisted = fPersist; pVariable->internalType = fOverridable ? BURN_VARIABLE_INTERNAL_TYPE_OVERRIDABLE_BUILTIN : BURN_VARIABLE_INTERNAL_TYPE_BUILTIN; pVariable->pfnInitialize = pfnInitialize; pVariable->dwpInitializeData = dwpInitializeData; LExit: return hr; } static HRESULT AddWellKnownVariable( __in BURN_VARIABLES* pVariables, __in LPCWSTR wzVariable, __in BOOL fPersisted ) { HRESULT hr = S_OK; DWORD iVariable = 0; BURN_VARIABLE* pVariable = NULL; hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); ExitOnFailure(hr, "Failed to find variable value."); // insert element if not found if (S_FALSE == hr) { hr = InsertVariable(pVariables, wzVariable, iVariable); ExitOnFailure(hr, "Failed to insert variable."); } else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL != pVariables->rgVariables[iVariable].internalType) { ExitWithRootFailure(hr, E_INVALIDSTATE, "Attempted to add built-in variable as a well-known variable: %ls", wzVariable); } else { ExitWithRootFailure(hr, E_INVALIDSTATE, "Attempted to add well-known variable again: %ls", wzVariable); } // set variable details pVariable = &pVariables->rgVariables[iVariable]; pVariable->fPersisted = fPersisted; pVariable->internalType = BURN_VARIABLE_INTERNAL_TYPE_NORMAL; LExit: return hr; } static HRESULT GetVariable( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __out BURN_VARIABLE** ppVariable ) { HRESULT hr = S_OK; DWORD iVariable = 0; BURN_VARIABLE* pVariable = NULL; hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable); if (S_FALSE == hr) { ExitFunction1(hr = E_NOTFOUND); } pVariable = &pVariables->rgVariables[iVariable]; // initialize built-in variable if (BURN_VARIANT_TYPE_NONE == pVariable->Value.Type && BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariable->internalType) { hr = pVariable->pfnInitialize(pVariable->dwpInitializeData, &pVariable->Value); ExitOnFailure(hr, "Failed to initialize built-in variable value '%ls'.", wzVariable); } *ppVariable = pVariable; LExit: return hr; } static HRESULT FindVariableIndexByName( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __out DWORD* piVariable ) { HRESULT hr = S_OK; DWORD iRangeFirst = 0; DWORD cRangeLength = pVariables->cVariables; while (cRangeLength) { // get variable in middle of range DWORD iPosition = cRangeLength / 2; BURN_VARIABLE* pVariable = &pVariables->rgVariables[iRangeFirst + iPosition]; switch (::CompareStringW(LOCALE_INVARIANT, SORT_STRINGSORT, wzVariable, -1, pVariable->sczName, -1)) { case CSTR_LESS_THAN: // restrict range to elements before the current cRangeLength = iPosition; break; case CSTR_EQUAL: // variable found *piVariable = iRangeFirst + iPosition; ExitFunction1(hr = S_OK); case CSTR_GREATER_THAN: // restrict range to elements after the current iRangeFirst += iPosition + 1; cRangeLength -= iPosition + 1; break; default: ExitWithLastError(hr, "Failed to compare strings."); } } *piVariable = iRangeFirst; hr = S_FALSE; // variable not found LExit: return hr; } static HRESULT InsertUserVariable( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in DWORD iPosition ) { HRESULT hr = S_OK; if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, wzVariable, 3, L"Wix", 3)) { ExitWithRootFailure(hr, E_INVALIDARG, "Attempted to insert variable with reserved prefix: %ls", wzVariable); } hr = InsertVariable(pVariables, wzVariable, iPosition); LExit: return hr; } static HRESULT InsertVariable( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in DWORD iPosition ) { HRESULT hr = S_OK; size_t cbAllocSize = 0; // ensure there is room in the variable array if (pVariables->cVariables == pVariables->dwMaxVariables) { hr = ::DWordAdd(pVariables->dwMaxVariables, GROW_VARIABLE_ARRAY, &(pVariables->dwMaxVariables)); ExitOnRootFailure(hr, "Overflow while growing variable array size"); if (pVariables->rgVariables) { hr = ::SizeTMult(sizeof(BURN_VARIABLE), pVariables->dwMaxVariables, &cbAllocSize); ExitOnRootFailure(hr, "Overflow while calculating size of variable array buffer"); LPVOID pv = MemReAlloc(pVariables->rgVariables, cbAllocSize, FALSE); ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate room for more variables."); // Prefast claims it's possible to hit this. Putting the check in just in case. if (pVariables->dwMaxVariables < pVariables->cVariables) { hr = INTSAFE_E_ARITHMETIC_OVERFLOW; ExitOnRootFailure(hr, "Overflow while dealing with variable array buffer allocation"); } pVariables->rgVariables = (BURN_VARIABLE*)pv; memset(&pVariables->rgVariables[pVariables->cVariables], 0, sizeof(BURN_VARIABLE) * (pVariables->dwMaxVariables - pVariables->cVariables)); } else { pVariables->rgVariables = (BURN_VARIABLE*)MemAlloc(sizeof(BURN_VARIABLE) * pVariables->dwMaxVariables, TRUE); ExitOnNull(pVariables->rgVariables, hr, E_OUTOFMEMORY, "Failed to allocate room for variables."); } } // move variables if (0 < pVariables->cVariables - iPosition) { memmove(&pVariables->rgVariables[iPosition + 1], &pVariables->rgVariables[iPosition], sizeof(BURN_VARIABLE) * (pVariables->cVariables - iPosition)); memset(&pVariables->rgVariables[iPosition], 0, sizeof(BURN_VARIABLE)); } ++pVariables->cVariables; // allocate name hr = StrAllocString(&pVariables->rgVariables[iPosition].sczName, wzVariable, 0); ExitOnFailure(hr, "Failed to copy variable name."); LExit: return hr; } static HRESULT SetVariableValue( __in BURN_VARIABLES* pVariables, __in_z LPCWSTR wzVariable, __in BURN_VARIANT* pVariant, __in SET_VARIABLE setBuiltin, __in BOOL fLog ) { HRESULT hr = S_OK; DWORD iVariable = 0; ::EnterCriticalSection(&pVariables->csAccess); hr = FindVariableIndexByName(pVariables, wzVariable, &iVariable); ExitOnFailure(hr, "Failed to find variable value '%ls'.", wzVariable); // Insert element if not found. if (S_FALSE == hr) { // Not possible from external callers so just assert. AssertSz(SET_VARIABLE_OVERRIDE_BUILTIN != setBuiltin, "Intent to set missing built-in variable."); hr = InsertVariable(pVariables, wzVariable, iVariable); ExitOnFailure(hr, "Failed to insert variable '%ls'.", wzVariable); } else if (BURN_VARIABLE_INTERNAL_TYPE_NORMAL < pVariables->rgVariables[iVariable].internalType) // built-in variables must be overridden. { if (SET_VARIABLE_OVERRIDE_BUILTIN == setBuiltin || (SET_VARIABLE_OVERRIDE_PERSISTED_BUILTINS == setBuiltin && pVariables->rgVariables[iVariable].fPersisted) || SET_VARIABLE_ANY == setBuiltin && BURN_VARIABLE_INTERNAL_TYPE_BUILTIN != pVariables->rgVariables[iVariable].internalType) { hr = S_OK; } else { hr = E_INVALIDARG; ExitOnRootFailure(hr, "Attempt to set built-in variable value: %ls", wzVariable); } } else // must *not* be a built-in variable so caller should not have tried to override it as a built-in. { // Not possible from external callers so just assert. AssertSz(SET_VARIABLE_OVERRIDE_BUILTIN != setBuiltin, "Intent to overwrite non-built-in variable."); } // Log value when not overwriting a built-in variable. if (fLog && BURN_VARIABLE_INTERNAL_TYPE_NORMAL == pVariables->rgVariables[iVariable].internalType) { if (pVariables->rgVariables[iVariable].fHidden) { LogStringLine(REPORT_STANDARD, "Setting hidden variable '%ls'", wzVariable); } else { switch (pVariant->Type) { case BURN_VARIANT_TYPE_NONE: if (BURN_VARIANT_TYPE_NONE != pVariables->rgVariables[iVariable].Value.Type) { LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable); } break; case BURN_VARIANT_TYPE_NUMERIC: LogStringLine(REPORT_STANDARD, "Setting numeric variable '%ls' to value %lld", wzVariable, pVariant->llValue); break; case BURN_VARIANT_TYPE_FORMATTED: __fallthrough; case BURN_VARIANT_TYPE_STRING: if (!pVariant->sczValue) { LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable); } else { LogStringLine(REPORT_STANDARD, "Setting %ls variable '%ls' to value '%ls'", BURN_VARIANT_TYPE_FORMATTED == pVariant->Type ? L"formatted" : L"string", wzVariable, pVariant->sczValue); } break; case BURN_VARIANT_TYPE_VERSION: if (!pVariant->pValue) { LogStringLine(REPORT_STANDARD, "Unsetting variable '%ls'", wzVariable); } else { LogStringLine(REPORT_STANDARD, "Setting version variable '%ls' to value '%ls'", wzVariable, pVariant->pValue->sczVersion); } break; default: AssertSz(FALSE, "Unknown variant type."); break; } } if (BURN_VARIANT_TYPE_VERSION == pVariant->Type && pVariant->pValue && pVariant->pValue->fInvalid) { LogId(REPORT_WARNING, MSG_VARIABLE_INVALID_VERSION, wzVariable); } } // Update variable value. hr = BVariantSetValue(&pVariables->rgVariables[iVariable].Value, pVariant); ExitOnFailure(hr, "Failed to set value of variable: %ls", wzVariable); LExit: ::LeaveCriticalSection(&pVariables->csAccess); if (FAILED(hr) && fLog) { LogStringLine(REPORT_STANDARD, "Setting variable failed: ID '%ls', HRESULT 0x%x", wzVariable, hr); } return hr; } static HRESULT InitializeVariableVersionNT( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; RTL_OSVERSIONINFOEXW ovix = { }; BURN_VARIANT value = { }; VERUTIL_VERSION* pVersion = NULL; hr = OsRtlGetVersion(&ovix); ExitOnFailure(hr, "Failed to get OS info."); switch ((OS_INFO_VARIABLE)dwpData) { case OS_INFO_VARIABLE_ServicePackLevel: if (0 != ovix.wServicePackMajor) { value.llValue = static_cast(ovix.wServicePackMajor); value.Type = BURN_VARIANT_TYPE_NUMERIC; } break; case OS_INFO_VARIABLE_VersionNT: hr = VerVersionFromQword(MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0), &pVersion); ExitOnFailure(hr, "Failed to create VersionNT from QWORD."); value.pValue = pVersion; value.Type = BURN_VARIANT_TYPE_VERSION; break; case OS_INFO_VARIABLE_VersionNT64: { #if !defined(_WIN64) BOOL fIsWow64 = FALSE; ProcWow64(::GetCurrentProcess(), &fIsWow64); if (fIsWow64) #endif { hr = VerVersionFromQword(MAKEQWORDVERSION(ovix.dwMajorVersion, ovix.dwMinorVersion, 0, 0), &pVersion); ExitOnFailure(hr, "Failed to create VersionNT64 from QWORD."); value.pValue = pVersion; value.Type = BURN_VARIANT_TYPE_VERSION; } } break; case OS_INFO_VARIABLE_WindowsBuildNumber: value.llValue = static_cast(ovix.dwBuildNumber); value.Type = BURN_VARIANT_TYPE_NUMERIC; default: AssertSz(FALSE, "Unknown OS info type."); break; } hr = BVariantCopy(&value, pValue); ExitOnFailure(hr, "Failed to set variant value."); LExit: ReleaseVerutilVersion(pVersion); return hr; } static HRESULT InitializeVariableOsInfo( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; RTL_OSVERSIONINFOEXW ovix = { }; BURN_VARIANT value = { }; hr = OsRtlGetVersion(&ovix); ExitOnFailure(hr, "Failed to get OS info."); switch ((OS_INFO_VARIABLE)dwpData) { case OS_INFO_VARIABLE_NTProductType: value.llValue = ovix.wProductType; value.Type = BURN_VARIANT_TYPE_NUMERIC; break; case OS_INFO_VARIABLE_NTSuiteBackOffice: value.llValue = VER_SUITE_BACKOFFICE & ovix.wSuiteMask ? 1 : 0; value.Type = BURN_VARIANT_TYPE_NUMERIC; break; case OS_INFO_VARIABLE_NTSuiteDataCenter: value.llValue = VER_SUITE_DATACENTER & ovix.wSuiteMask ? 1 : 0; value.Type = BURN_VARIANT_TYPE_NUMERIC; break; case OS_INFO_VARIABLE_NTSuiteEnterprise: value.llValue = VER_SUITE_ENTERPRISE & ovix.wSuiteMask ? 1 : 0; value.Type = BURN_VARIANT_TYPE_NUMERIC; break; case OS_INFO_VARIABLE_NTSuitePersonal: value.llValue = VER_SUITE_PERSONAL & ovix.wSuiteMask ? 1 : 0; value.Type = BURN_VARIANT_TYPE_NUMERIC; break; case OS_INFO_VARIABLE_NTSuiteSmallBusiness: value.llValue = VER_SUITE_SMALLBUSINESS & ovix.wSuiteMask ? 1 : 0; value.Type = BURN_VARIANT_TYPE_NUMERIC; break; case OS_INFO_VARIABLE_NTSuiteSmallBusinessRestricted: value.llValue = VER_SUITE_SMALLBUSINESS_RESTRICTED & ovix.wSuiteMask ? 1 : 0; value.Type = BURN_VARIANT_TYPE_NUMERIC; break; case OS_INFO_VARIABLE_NTSuiteWebServer: value.llValue = VER_SUITE_BLADE & ovix.wSuiteMask ? 1 : 0; value.Type = BURN_VARIANT_TYPE_NUMERIC; break; case OS_INFO_VARIABLE_CompatibilityMode: { DWORDLONG dwlConditionMask = 0; VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_EQUAL); VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_EQUAL); VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_EQUAL); VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMINOR, VER_EQUAL); value.llValue = ::VerifyVersionInfoW(&ovix, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, dwlConditionMask); value.Type = BURN_VARIANT_TYPE_NUMERIC; } break; case OS_INFO_VARIABLE_TerminalServer: value.llValue = (VER_SUITE_TERMINAL == (ovix.wSuiteMask & VER_SUITE_TERMINAL)) && (VER_SUITE_SINGLEUSERTS != (ovix.wSuiteMask & VER_SUITE_SINGLEUSERTS)) ? 1 : 0; value.Type = BURN_VARIANT_TYPE_NUMERIC; break; default: AssertSz(FALSE, "Unknown OS info type."); break; } hr = BVariantCopy(&value, pValue); ExitOnFailure(hr, "Failed to set variant value."); LExit: return hr; } static HRESULT InitializeVariableSystemInfo( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; SYSTEM_INFO si = { }; BURN_VARIANT value = { }; ::GetNativeSystemInfo(&si); switch ((OS_INFO_VARIABLE)dwpData) { case OS_INFO_VARIABLE_ProcessorArchitecture: value.llValue = si.wProcessorArchitecture; value.Type = BURN_VARIANT_TYPE_NUMERIC; break; default: AssertSz(FALSE, "Unknown OS info type."); break; } hr = BVariantCopy(&value, pValue); ExitOnFailure(hr, "Failed to set variant value."); LExit: return hr; } static HRESULT InitializeVariableNativeMachine( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { UNREFERENCED_PARAMETER(dwpData); HRESULT hr = S_OK; USHORT usNativeMachine = IMAGE_FILE_MACHINE_UNKNOWN; hr = ProcNativeMachine(::GetCurrentProcess(), &usNativeMachine); ExitOnFailure(hr, "Failed to get native machine value."); if (S_FALSE != hr) { hr = BVariantSetNumeric(pValue, usNativeMachine); ExitOnFailure(hr, "Failed to set variant value."); } LExit: return hr; } static HRESULT InitializeVariableComputerName( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { UNREFERENCED_PARAMETER(dwpData); HRESULT hr = S_OK; WCHAR wzComputerName[MAX_COMPUTERNAME_LENGTH + 1] = { }; DWORD cchComputerName = countof(wzComputerName); // get computer name if (!::GetComputerNameW(wzComputerName, &cchComputerName)) { ExitWithLastError(hr, "Failed to get computer name."); } // set value hr = BVariantSetString(pValue, wzComputerName, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: return hr; } static HRESULT InitializeVariableVersionMsi( __in DWORD_PTR /*dwpData*/, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; DLLGETVERSIONPROC pfnMsiDllGetVersion = NULL; DLLVERSIONINFO msiVersionInfo = { }; VERUTIL_VERSION* pVersion = NULL; // get DllGetVersion proc address pfnMsiDllGetVersion = (DLLGETVERSIONPROC)::GetProcAddress(::GetModuleHandleW(L"msi"), "DllGetVersion"); ExitOnNullWithLastError(pfnMsiDllGetVersion, hr, "Failed to find DllGetVersion entry point in msi.dll."); // get msi.dll version info msiVersionInfo.cbSize = sizeof(DLLVERSIONINFO); hr = pfnMsiDllGetVersion(&msiVersionInfo); ExitOnFailure(hr, "Failed to get msi.dll version info."); hr = VerVersionFromQword(MAKEQWORDVERSION(msiVersionInfo.dwMajorVersion, msiVersionInfo.dwMinorVersion, 0, 0), &pVersion); ExitOnFailure(hr, "Failed to create msi.dll version from QWORD."); hr = BVariantSetVersion(pValue, pVersion); ExitOnFailure(hr, "Failed to set variant value."); LExit: ReleaseVerutilVersion(pVersion); return hr; } static HRESULT InitializeVariableCsidlFolder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; LPWSTR sczPath = NULL; int nFolder = (int)dwpData; // get folder path hr = ShelGetFolder(&sczPath, nFolder); ExitOnRootFailure(hr, "Failed to get shell folder."); // set value hr = BVariantSetString(pValue, sczPath, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: ReleaseStr(sczPath); return hr; } static HRESULT InitializeVariableTempFolder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { UNREFERENCED_PARAMETER(dwpData); HRESULT hr = S_OK; LPWSTR sczPath = NULL; hr = PathGetTempPath(&sczPath, NULL); ExitOnFailure(hr, "Failed to get temp path."); // set value hr = BVariantSetString(pValue, sczPath, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: ReleaseStr(sczPath); return hr; } static HRESULT InitializeVariableSystemFolder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; BOOL f64 = (BOOL)dwpData; LPWSTR sczSystemFolder = NULL; #if !defined(_WIN64) BOOL fIsWow64 = FALSE; ProcWow64(::GetCurrentProcess(), &fIsWow64); if (fIsWow64) { if (f64) { hr = PathGetSystemDirectory(&sczSystemFolder); ExitOnFailure(hr, "Failed to get 64-bit system folder."); } else { hr = PathGetSystemWow64Directory(&sczSystemFolder); ExitOnFailure(hr, "Failed to get 32-bit system folder."); } } else { if (!f64) { hr = PathGetSystemDirectory(&sczSystemFolder); ExitOnFailure(hr, "Failed to get 32-bit system folder."); } } #else if (f64) { hr = PathGetSystemDirectory(&sczSystemFolder); ExitOnFailure(hr, "Failed to get 64-bit system folder."); } else { hr = PathGetSystemWow64Directory(&sczSystemFolder); ExitOnFailure(hr, "Failed to get 32-bit system folder."); } #endif // set value hr = BVariantSetString(pValue, sczSystemFolder, 0, FALSE); ExitOnFailure(hr, "Failed to set system folder variant value."); LExit: ReleaseStr(sczSystemFolder); return hr; } static HRESULT InitializeVariableWindowsVolumeFolder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { UNREFERENCED_PARAMETER(dwpData); HRESULT hr = S_OK; LPWSTR sczWindowsPath = NULL; LPWSTR sczVolumePath = NULL; // get windows directory hr = PathSystemWindowsSubdirectory(NULL, &sczWindowsPath); ExitOnFailure(hr, "Failed to get windows directory."); // get volume path name hr = PathGetVolumePathName(sczWindowsPath, &sczVolumePath); ExitOnFailure(hr, "Failed to get volume path name."); // set value hr = BVariantSetString(pValue, sczVolumePath, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: ReleaseStr(sczWindowsPath); ReleaseStr(sczVolumePath); return hr; } static HRESULT InitializeVariablePrivileged( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { UNREFERENCED_PARAMETER(dwpData); HRESULT hr = S_OK; BOOL fPrivileged = FALSE; // check if process could run privileged. hr = OsCouldRunPrivileged(&fPrivileged); ExitOnFailure(hr, "Failed to check if process could run privileged."); // set value hr = BVariantSetNumeric(pValue, fPrivileged); ExitOnFailure(hr, "Failed to set variant value."); LExit: return hr; } static HRESULT InitializeVariableProcessTokenPrivilege( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; BOOL fHasPrivilege = FALSE; LPCWSTR wzPrivilegeName = (LPCWSTR)dwpData; hr = ProcHasPrivilege(::GetCurrentProcess(), wzPrivilegeName, &fHasPrivilege); ExitOnFailure(hr, "Failed to check if process token has privilege: %ls.", wzPrivilegeName); // set value hr = BVariantSetNumeric(pValue, fHasPrivilege); ExitOnFailure(hr, "Failed to set variant value."); LExit: return hr; } static HRESULT InitializeSystemLanguageID( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { UNREFERENCED_PARAMETER(dwpData); HRESULT hr = S_OK; LANGID langid = ::GetSystemDefaultLangID(); hr = BVariantSetNumeric(pValue, langid); ExitOnFailure(hr, "Failed to set variant value."); LExit: return hr; } static HRESULT InitializeUserUILanguageID( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { UNREFERENCED_PARAMETER(dwpData); HRESULT hr = S_OK; LANGID langid = ::GetUserDefaultUILanguage(); hr = BVariantSetNumeric(pValue, langid); ExitOnFailure(hr, "Failed to set variant value."); LExit: return hr; } static HRESULT InitializeUserLanguageID( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { UNREFERENCED_PARAMETER(dwpData); HRESULT hr = S_OK; LANGID langid = ::GetUserDefaultLangID(); hr = BVariantSetNumeric(pValue, langid); ExitOnFailure(hr, "Failed to set variant value."); LExit: return hr; } static HRESULT InitializeVariableString( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; LPCWSTR wzValue = (LPCWSTR)dwpData; // set value hr = BVariantSetString(pValue, wzValue, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: return hr; } static HRESULT InitializeVariableNumeric( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; LONGLONG llValue = (LONGLONG)dwpData; // set value hr = BVariantSetNumeric(pValue, llValue); ExitOnFailure(hr, "Failed to set variant value."); LExit: return hr; } #if !defined(_WIN64) static HRESULT InitializeVariableRegistryFolder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; int nFolder = (int)dwpData; LPWSTR sczPath = NULL; BOOL fIsWow64 = FALSE; ProcWow64(::GetCurrentProcess(), &fIsWow64); if (!fIsWow64) // on 32-bit machines, variables aren't set { ExitFunction(); } hr = Get64bitFolderFromRegistry(nFolder, &sczPath); ExitOnFailure(hr, "Failed to get 64-bit folder."); // set value hr = BVariantSetString(pValue, sczPath, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: ReleaseStr(sczPath); return hr; } #endif static HRESULT InitializeVariable6432Folder( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; int nFolder = (int)dwpData; LPWSTR sczPath = NULL; #if !defined(_WIN64) BOOL fIsWow64 = FALSE; // If 32-bit use shell-folder. ProcWow64(::GetCurrentProcess(), &fIsWow64); if (!fIsWow64) { hr = ShelGetFolder(&sczPath, nFolder); ExitOnRootFailure(hr, "Failed to get shell folder."); } else #endif { hr = Get64bitFolderFromRegistry(nFolder, &sczPath); ExitOnFailure(hr, "Failed to get 64-bit folder."); } // set value hr = BVariantSetString(pValue, sczPath, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: ReleaseStr(sczPath); return hr; } // Get the date in the same format as Windows Installer. static HRESULT InitializeVariableDate( __in DWORD_PTR /*dwpData*/, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; SYSTEMTIME systime = { }; LPWSTR sczDate = NULL; int cchDate = 0; ::GetSystemTime(&systime); cchDate = ::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, NULL, cchDate); if (!cchDate) { ExitOnLastError(hr, "Failed to get the required buffer length for the Date."); } hr = StrAlloc(&sczDate, cchDate); ExitOnFailure(hr, "Failed to allocate the buffer for the Date."); if (!::GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, NULL, sczDate, cchDate)) { ExitOnLastError(hr, "Failed to get the Date."); } // set value hr = BVariantSetString(pValue, sczDate, cchDate, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: ReleaseStr(sczDate); return hr; } static HRESULT InitializeVariableInstallerName( __in DWORD_PTR /*dwpData*/, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; // set value hr = BVariantSetString(pValue, L"WiX Burn", 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: return hr; } static HRESULT InitializeVariableInstallerVersion( __in DWORD_PTR /*dwpData*/, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; LPWSTR sczVersion = NULL; hr = StrAllocStringAnsi(&sczVersion, szVerMajorMinorBuild, 0, CP_ACP); ExitOnFailure(hr, "Failed to copy the engine version: %hs", szVerMajorMinorBuild); // set value hr = BVariantSetString(pValue, sczVersion, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: ReleaseStr(sczVersion); return hr; } static HRESULT InitializeVariableInstallerInformationalVersion( __in DWORD_PTR /*dwpData*/, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; LPWSTR sczVersion = NULL; hr = StrAllocStringAnsi(&sczVersion, szInformationalVersion, 0, CP_ACP); ExitOnFailure(hr, "Failed to copy the engine informational version: %hs", szInformationalVersion); // set value hr = BVariantSetString(pValue, sczVersion, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: ReleaseStr(sczVersion); return hr; } static HRESULT InitializeVariableVersion( __in DWORD_PTR dwpData, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; LPCWSTR wzValue = (LPCWSTR)dwpData; VERUTIL_VERSION* pVersion = NULL; hr = VerParseVersion(wzValue, 0, FALSE, &pVersion); ExitOnFailure(hr, "Failed to initialize version."); // set value hr = BVariantSetVersion(pValue, pVersion); ExitOnFailure(hr, "Failed to set variant value."); LExit: ReleaseVerutilVersion(pVersion); return hr; } // Get the current user the same as Windows Installer. static HRESULT InitializeVariableLogonUser( __in DWORD_PTR /*dwpData*/, __inout BURN_VARIANT* pValue ) { HRESULT hr = S_OK; WCHAR wzUserName[UNLEN + 1]; DWORD cchUserName = countof(wzUserName); if (!::GetUserNameW(wzUserName, &cchUserName)) { ExitOnLastError(hr, "Failed to get the user name."); } // set value hr = BVariantSetString(pValue, wzUserName, 0, FALSE); ExitOnFailure(hr, "Failed to set variant value."); LExit: return hr; } static HRESULT Get64bitFolderFromRegistry( __in int nFolder, __deref_out_z LPWSTR* psczPath ) { HRESULT hr = S_OK; HKEY hkFolders = NULL; AssertSz(CSIDL_PROGRAM_FILES == nFolder || CSIDL_PROGRAM_FILES_COMMON == nFolder, "Unknown folder CSIDL."); LPCWSTR wzFolderValue = CSIDL_PROGRAM_FILES_COMMON == nFolder ? L"CommonFilesDir" : L"ProgramFilesDir"; hr = RegOpenEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", KEY_READ, REG_KEY_64BIT, &hkFolders); ExitOnFailure(hr, "Failed to open Windows folder key."); hr = RegReadString(hkFolders, wzFolderValue, psczPath); ExitOnFailure(hr, "Failed to read folder path for '%ls'.", wzFolderValue); hr = PathBackslashTerminate(psczPath); ExitOnFailure(hr, "Failed to ensure path was backslash terminated."); LExit: ReleaseRegKey(hkFolders); return hr; }