From 7f642e51670bc38a4ef782a363936850bc2b0ba9 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Thu, 22 Apr 2021 06:38:23 -0700 Subject: Move dutil into libs/dutil --- src/libs/dutil/WixToolset.DUtil/butil.cpp | 257 ++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 src/libs/dutil/WixToolset.DUtil/butil.cpp (limited to 'src/libs/dutil/WixToolset.DUtil/butil.cpp') diff --git a/src/libs/dutil/WixToolset.DUtil/butil.cpp b/src/libs/dutil/WixToolset.DUtil/butil.cpp new file mode 100644 index 00000000..e04b52e9 --- /dev/null +++ b/src/libs/dutil/WixToolset.DUtil/butil.cpp @@ -0,0 +1,257 @@ +// 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" + +// Exit macros +#define ButilExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_BUTIL, x, s, __VA_ARGS__) +#define ButilExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) +#define ButilExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) +#define ButilExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_BUTIL, p, x, e, s, __VA_ARGS__) +#define ButilExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_BUTIL, p, x, s, __VA_ARGS__) +#define ButilExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_BUTIL, e, x, s, __VA_ARGS__) + +// constants +// From engine/registration.h +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgradeCode"; +const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; + +// Forward declarations. +/******************************************************************** +OpenBundleKey - Opens the bundle uninstallation key for a given bundle + +NOTE: caller is responsible for closing key +********************************************************************/ +static HRESULT OpenBundleKey( + __in_z LPCWSTR wzBundleId, + __in BUNDLE_INSTALL_CONTEXT context, + __inout HKEY *key); + + +extern "C" HRESULT DAPI BundleGetBundleInfo( + __in_z LPCWSTR wzBundleId, + __in_z LPCWSTR wzAttribute, + __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf, + __inout_opt LPDWORD pcchValueBuf + ) +{ + Assert(wzBundleId && wzAttribute); + + HRESULT hr = S_OK; + BUNDLE_INSTALL_CONTEXT context = BUNDLE_INSTALL_CONTEXT_MACHINE; + LPWSTR sczValue = NULL; + HKEY hkBundle = NULL; + DWORD cchSource = 0; + DWORD dwType = 0; + DWORD dwValue = 0; + + if ((lpValueBuf && !pcchValueBuf) || !wzBundleId || !wzAttribute) + { + ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); + } + + if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, &hkBundle)) && + FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, &hkBundle))) + { + ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path."); + } + + // If the bundle doesn't have the property defined, return ERROR_UNKNOWN_PROPERTY + hr = RegGetType(hkBundle, wzAttribute, &dwType); + ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) : hr, "Failed to locate bundle property."); + + switch (dwType) + { + case REG_SZ: + hr = RegReadString(hkBundle, wzAttribute, &sczValue); + ButilExitOnFailure(hr, "Failed to read string property."); + break; + case REG_DWORD: + hr = RegReadNumber(hkBundle, wzAttribute, &dwValue); + ButilExitOnFailure(hr, "Failed to read dword property."); + + hr = StrAllocFormatted(&sczValue, L"%d", dwValue); + ButilExitOnFailure(hr, "Failed to format dword property as string."); + break; + default: + ButilExitOnFailure(hr = E_NOTIMPL, "Reading bundle info of type 0x%x not implemented.", dwType); + + } + + hr = ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, reinterpret_cast(&cchSource)); + ButilExitOnFailure(hr, "Failed to calculate length of string"); + + if (lpValueBuf) + { + // cchSource is the length of the string not including the terminating null character + if (*pcchValueBuf <= cchSource) + { + *pcchValueBuf = ++cchSource; + ButilExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA), "A buffer is too small to hold the requested data."); + } + + hr = ::StringCchCatNExW(lpValueBuf, *pcchValueBuf, sczValue, cchSource, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer."); + + *pcchValueBuf = cchSource++; + } + +LExit: + ReleaseRegKey(hkBundle); + ReleaseStr(sczValue); + + return hr; +} + +HRESULT DAPI BundleEnumRelatedBundle( + __in_z LPCWSTR wzUpgradeCode, + __in BUNDLE_INSTALL_CONTEXT context, + __inout PDWORD pdwStartIndex, + __out_ecount(MAX_GUID_CHARS+1) LPWSTR lpBundleIdBuf + ) +{ + HRESULT hr = S_OK; + HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + HKEY hkUninstall = NULL; + HKEY hkBundle = NULL; + LPWSTR sczUninstallSubKey = NULL; + DWORD cchUninstallSubKey = 0; + LPWSTR sczUninstallSubKeyPath = NULL; + LPWSTR sczValue = NULL; + DWORD dwType = 0; + + LPWSTR* rgsczBundleUpgradeCodes = NULL; + DWORD cBundleUpgradeCodes = 0; + BOOL fUpgradeCodeFound = FALSE; + + if (!wzUpgradeCode || !lpBundleIdBuf || !pdwStartIndex) + { + ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function."); + } + + hr = RegOpen(hkRoot, BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, KEY_READ, &hkUninstall); + ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); + + for (DWORD dwIndex = *pdwStartIndex; !fUpgradeCodeFound; dwIndex++) + { + hr = RegKeyEnum(hkUninstall, dwIndex, &sczUninstallSubKey); + ButilExitOnFailure(hr, "Failed to enumerate bundle uninstall key path."); + + hr = StrAllocFormatted(&sczUninstallSubKeyPath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, sczUninstallSubKey); + ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); + + hr = RegOpen(hkRoot, sczUninstallSubKeyPath, KEY_READ, &hkBundle); + ButilExitOnFailure(hr, "Failed to open uninstall key path."); + + // If it's a bundle, it should have a BundleUpgradeCode value of type REG_SZ (old) or REG_MULTI_SZ + hr = RegGetType(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &dwType); + if (FAILED(hr)) + { + ReleaseRegKey(hkBundle); + ReleaseNullStr(sczUninstallSubKey); + ReleaseNullStr(sczUninstallSubKeyPath); + // Not a bundle + continue; + } + + switch (dwType) + { + case REG_SZ: + hr = RegReadString(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &sczValue); + ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode string property."); + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, sczValue, -1, wzUpgradeCode, -1)) + { + *pdwStartIndex = dwIndex; + fUpgradeCodeFound = TRUE; + break; + } + + ReleaseNullStr(sczValue); + + break; + case REG_MULTI_SZ: + hr = RegReadStringArray(hkBundle, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &rgsczBundleUpgradeCodes, &cBundleUpgradeCodes); + ButilExitOnFailure(hr, "Failed to read BundleUpgradeCode multi-string property."); + + for (DWORD i = 0; i < cBundleUpgradeCodes; i++) + { + LPWSTR wzBundleUpgradeCode = rgsczBundleUpgradeCodes[i]; + if (wzBundleUpgradeCode && *wzBundleUpgradeCode) + { + if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, wzBundleUpgradeCode, -1, wzUpgradeCode, -1)) + { + *pdwStartIndex = dwIndex; + fUpgradeCodeFound = TRUE; + break; + } + } + } + ReleaseNullStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes); + + break; + + default: + ButilExitOnFailure(hr = E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType); + + } + + if (fUpgradeCodeFound) + { + if (lpBundleIdBuf) + { + hr = ::StringCchLengthW(sczUninstallSubKey, STRSAFE_MAX_CCH, reinterpret_cast(&cchUninstallSubKey)); + ButilExitOnFailure(hr, "Failed to calculate length of string"); + + hr = ::StringCchCopyNExW(lpBundleIdBuf, MAX_GUID_CHARS + 1, sczUninstallSubKey, cchUninstallSubKey, NULL, NULL, STRSAFE_FILL_BEHIND_NULL); + ButilExitOnFailure(hr, "Failed to copy the property value to the output buffer."); + } + + break; + } + + // Cleanup before next iteration + ReleaseRegKey(hkBundle); + ReleaseNullStr(sczUninstallSubKey); + ReleaseNullStr(sczUninstallSubKeyPath); + } + +LExit: + ReleaseStr(sczValue); + ReleaseStr(sczUninstallSubKey); + ReleaseStr(sczUninstallSubKeyPath); + ReleaseRegKey(hkBundle); + ReleaseRegKey(hkUninstall); + ReleaseStrArray(rgsczBundleUpgradeCodes, cBundleUpgradeCodes); + + return hr; +} + + +HRESULT OpenBundleKey( + __in_z LPCWSTR wzBundleId, + __in BUNDLE_INSTALL_CONTEXT context, + __inout HKEY *key) +{ + Assert(key && wzBundleId); + AssertSz(NULL == *key, "*key should be null"); + + HRESULT hr = S_OK; + HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + LPWSTR sczKeypath = NULL; + + hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId); + ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path."); + + hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key); + ButilExitOnFailure(hr, "Failed to open bundle uninstall key path."); + +LExit: + ReleaseStr(sczKeypath); + + return hr; +} -- cgit v1.2.3-55-g6feb