From 1bdf42c558d6923380b9f3ea409027816f972f98 Mon Sep 17 00:00:00 2001
From: Sean Hall <r.sean.hall@gmail.com>
Date: Sun, 18 Jul 2021 19:52:05 -0500
Subject: Refactor butil while cleaning up other things.

---
 src/CustomizedNativeRecommendedRules.ruleset       |   8 +
 src/Directory.vcxproj.props                        |   2 +-
 .../burn/CustomizedNativeRecommendedRules.ruleset  |   8 -
 src/burn/CustomizedNativeRecommendedRules.ruleset  |   8 -
 src/burn/engine/registration.cpp                   |  15 +-
 src/burn/test/BurnUnitTest/RegistrationTest.cpp    |  80 ++++-----
 .../Bal/CustomizedNativeRecommendedRules.ruleset   |   8 -
 .../NetFx/CustomizedNativeRecommendedRules.ruleset |   8 -
 .../Util/CustomizedNativeRecommendedRules.ruleset  |   8 -
 .../dutil/CustomizedNativeRecommendedRules.ruleset |   8 -
 src/libs/dutil/WixToolset.DUtil/butil.cpp          | 192 +++++++++++++--------
 src/libs/dutil/WixToolset.DUtil/inc/butil.h        |  22 ++-
 src/libs/dutil/WixToolset.DUtil/inc/regutil.h      | 161 ++++++++++++++++-
 src/libs/dutil/WixToolset.DUtil/regutil.cpp        | 180 ++++---------------
 14 files changed, 377 insertions(+), 331 deletions(-)
 create mode 100644 src/CustomizedNativeRecommendedRules.ruleset
 delete mode 100644 src/api/burn/CustomizedNativeRecommendedRules.ruleset
 delete mode 100644 src/burn/CustomizedNativeRecommendedRules.ruleset
 delete mode 100644 src/ext/Bal/CustomizedNativeRecommendedRules.ruleset
 delete mode 100644 src/ext/NetFx/CustomizedNativeRecommendedRules.ruleset
 delete mode 100644 src/ext/Util/CustomizedNativeRecommendedRules.ruleset
 delete mode 100644 src/libs/dutil/CustomizedNativeRecommendedRules.ruleset

(limited to 'src')

diff --git a/src/CustomizedNativeRecommendedRules.ruleset b/src/CustomizedNativeRecommendedRules.ruleset
new file mode 100644
index 00000000..142b141c
--- /dev/null
+++ b/src/CustomizedNativeRecommendedRules.ruleset
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RuleSet Name="Customized Microsoft Native Recommended Rules" Description="Microsoft Native Recommended Rules, -C26812" ToolsVersion="16.0">
+  <Include Path="nativerecommendedrules.ruleset" Action="Default" />
+  <Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
+    <!-- We need C style enums since we support BAs written in C -->
+    <Rule Id="C26812" Action="None" />
+  </Rules>
+</RuleSet>
\ No newline at end of file
diff --git a/src/Directory.vcxproj.props b/src/Directory.vcxproj.props
index d150cc2e..d0953841 100644
--- a/src/Directory.vcxproj.props
+++ b/src/Directory.vcxproj.props
@@ -20,7 +20,7 @@
   </PropertyGroup>
 
   <PropertyGroup>
-    <CodeAnalysisRuleSet Condition=" Exists('$(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset') ">$(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
+    <CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CustomizedNativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
   </PropertyGroup>
 
   <ItemDefinitionGroup>
diff --git a/src/api/burn/CustomizedNativeRecommendedRules.ruleset b/src/api/burn/CustomizedNativeRecommendedRules.ruleset
deleted file mode 100644
index 142b141c..00000000
--- a/src/api/burn/CustomizedNativeRecommendedRules.ruleset
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RuleSet Name="Customized Microsoft Native Recommended Rules" Description="Microsoft Native Recommended Rules, -C26812" ToolsVersion="16.0">
-  <Include Path="nativerecommendedrules.ruleset" Action="Default" />
-  <Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
-    <!-- We need C style enums since we support BAs written in C -->
-    <Rule Id="C26812" Action="None" />
-  </Rules>
-</RuleSet>
\ No newline at end of file
diff --git a/src/burn/CustomizedNativeRecommendedRules.ruleset b/src/burn/CustomizedNativeRecommendedRules.ruleset
deleted file mode 100644
index 142b141c..00000000
--- a/src/burn/CustomizedNativeRecommendedRules.ruleset
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RuleSet Name="Customized Microsoft Native Recommended Rules" Description="Microsoft Native Recommended Rules, -C26812" ToolsVersion="16.0">
-  <Include Path="nativerecommendedrules.ruleset" Action="Default" />
-  <Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
-    <!-- We need C style enums since we support BAs written in C -->
-    <Rule Id="C26812" Action="None" />
-  </Rules>
-</RuleSet>
\ No newline at end of file
diff --git a/src/burn/engine/registration.cpp b/src/burn/engine/registration.cpp
index 4088004d..147865fa 100644
--- a/src/burn/engine/registration.cpp
+++ b/src/burn/engine/registration.cpp
@@ -910,7 +910,6 @@ extern "C" HRESULT RegistrationSessionEnd(
 {
     HRESULT hr = S_OK;
     LPWSTR sczRebootRequiredKey = NULL;
-    LPWSTR sczVariableKey = NULL;
     HKEY hkRebootRequired = NULL;
     HKEY hkRegistration = NULL;
 
@@ -958,19 +957,8 @@ extern "C" HRESULT RegistrationSessionEnd(
 
         RemoveSoftwareTags(pVariables, &pRegistration->softwareTags);
 
-        // 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.");
-
-        // Delete registration variable key.
-        hr = RegDelete(pRegistration->hkRoot, sczVariableKey, REG_KEY_DEFAULT, FALSE);
-        if (E_FILENOTFOUND != hr)
-        {
-            ExitOnFailure(hr, "Failed to delete registration variable key: %ls", sczVariableKey);
-        }
-
         // Delete registration key.
-        hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, FALSE);
+        hr = RegDelete(pRegistration->hkRoot, pRegistration->sczRegistrationKey, REG_KEY_DEFAULT, TRUE);
         if (E_FILENOTFOUND != hr)
         {
             ExitOnFailure(hr, "Failed to delete registration key: %ls", pRegistration->sczRegistrationKey);
@@ -998,7 +986,6 @@ extern "C" HRESULT RegistrationSessionEnd(
 LExit:
     ReleaseRegKey(hkRegistration);
     ReleaseRegKey(hkRebootRequired);
-    ReleaseStr(sczVariableKey);
     ReleaseStr(sczRebootRequiredKey);
 
     return hr;
diff --git a/src/burn/test/BurnUnitTest/RegistrationTest.cpp b/src/burn/test/BurnUnitTest/RegistrationTest.cpp
index 298d4631..a8319bb2 100644
--- a/src/burn/test/BurnUnitTest/RegistrationTest.cpp
+++ b/src/burn/test/BurnUnitTest/RegistrationTest.cpp
@@ -4,16 +4,16 @@
 
 
 #define ROOT_PATH L"SOFTWARE\\WiX_Burn_UnitTest"
-#define HKLM_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKLM"
-#define HKCU_PATH L"SOFTWARE\\WiX_Burn_UnitTest\\HKCU"
+#define HKLM_PATH ROOT_PATH L"\\HKLM"
+#define HKCU_PATH ROOT_PATH L"\\HKCU"
 #define REGISTRY_UNINSTALL_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
 #define REGISTRY_RUN_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"
-#define TEST_BUNDLE_ID L"{D54F896D-1952-43e6-9C67-B5652240618C}"
+#define TEST_BUNDLE_ID L"{D54F896D-1952-43E6-9C67-B5652240618C}"
 #define TEST_BUNDLE_UPGRADE_CODE L"{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}"
 
-#define TEST_UNINSTALL_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\{D54F896D-1952-43e6-9C67-B5652240618C}"
+#define TEST_UNINSTALL_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\" TEST_BUNDLE_ID
 #define TEST_RUN_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_RUN_KEY
-#define TEST_VARIABLE_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\{D54F896D-1952-43e6-9C67-B5652240618C}\\variables"
+#define TEST_VARIABLE_KEY L"HKEY_CURRENT_USER\\" HKCU_PATH L"\\" REGISTRY_UNINSTALL_KEY L"\\" TEST_BUNDLE_ID L"\\variables"
 
 
 static LSTATUS APIENTRY RegistrationTest_RegCreateKeyExW(
@@ -75,7 +75,7 @@ namespace Bootstrapper
             BURN_REGISTRATION registration = { };
             BURN_LOGGING logging = { };
             BURN_PACKAGES packages = { };
-            String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
+            String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
 
             try
             {
@@ -91,7 +91,7 @@ namespace Bootstrapper
                     L"    <UX>"
                     L"        <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
                     L"    </UX>"
-                    L"    <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
+                    L"    <Registration Id='{D54F896D-1952-43E6-9C67-B5652240618C}' UpgradeCode='{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
                     L"        <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />"
                     L"    </Registration>"
                     L"</Bundle>";
@@ -123,7 +123,7 @@ namespace Bootstrapper
                 Assert::True(File::Exists(Path::Combine(cacheDirectory, gcnew String(L"setup.exe"))));
 
                 Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
-                Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)));
+                Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)(Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr)));
 
                 // end session
                 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
@@ -133,7 +133,7 @@ namespace Bootstrapper
                 Assert::False(Directory::Exists(cacheDirectory));
 
                 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
-                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
             }
             finally
             {
@@ -165,7 +165,7 @@ namespace Bootstrapper
             BURN_REGISTRATION registration = { };
             BURN_LOGGING logging = { };
             BURN_PACKAGES packages = { };
-            String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
+            String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
             try
             {
                 // set mock API's
@@ -180,7 +180,7 @@ namespace Bootstrapper
                     L"    <UX>"
                     L"        <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
                     L"    </UX>"
-                    L"    <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
+                    L"    <Registration Id='{D54F896D-1952-43E6-9C67-B5652240618C}' UpgradeCode='{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
                     L"        <Arp Register='yes' Publisher='WiX Toolset' DisplayName='Product1' InProgressDisplayName='Product1 Installation' DisplayVersion='1.0.0.0' />"
                     L"    </Registration>"
                     L"</Bundle>";
@@ -214,7 +214,7 @@ namespace Bootstrapper
                 // verify that registration was created
                 Assert::Equal<String^>(gcnew String(L"Product1 Installation"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayName"), nullptr));
                 Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
-                Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
 
                 // complete registration
                 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
@@ -223,7 +223,7 @@ namespace Bootstrapper
                 // verify that registration was updated
                 Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
                 Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
-                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
 
                 //
                 // uninstall
@@ -236,7 +236,7 @@ namespace Bootstrapper
                 // verify that registration was updated
                 Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
                 Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
-                Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
 
                 // delete registration
                 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
@@ -245,7 +245,7 @@ namespace Bootstrapper
                 // verify that registration was removed
                 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
                 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
-                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
             }
             finally
             {
@@ -277,7 +277,7 @@ namespace Bootstrapper
             BURN_REGISTRATION registration = { };
             BURN_LOGGING logging = { };
             BURN_PACKAGES packages = { };
-            String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
+            String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
             try
             {
                 // set mock API's
@@ -292,7 +292,7 @@ namespace Bootstrapper
                     L"    <UX>"
                     L"        <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
                     L"    </UX>"
-                    L"    <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='bar' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
+                    L"    <Registration Id='{D54F896D-1952-43E6-9C67-B5652240618C}' UpgradeCode='{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}' Tag='foo' ProviderKey='bar' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
                     L"        <Arp Register='yes' Publisher='WiX Toolset' DisplayName='Product1' DisplayVersion='1.0.0.0' />"
                     L"    </Registration>"
                     L"</Bundle>";
@@ -325,7 +325,7 @@ namespace Bootstrapper
 
                 // verify that registration was created
                 Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
-                Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
 
                 // complete registration
                 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, BOOTSTRAPPER_REGISTRATION_TYPE_FULL);
@@ -355,7 +355,7 @@ namespace Bootstrapper
                 // verify that registration was removed
                 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
                 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
-                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
             }
             finally
             {
@@ -387,7 +387,7 @@ namespace Bootstrapper
             BURN_REGISTRATION registration = { };
             BURN_LOGGING logging = { };
             BURN_PACKAGES packages = { };
-            String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
+            String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
             try
             {
                 // set mock API's
@@ -402,7 +402,7 @@ namespace Bootstrapper
                     L"    <UX UxDllPayloadId='ux.dll'>"
                     L"        <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
                     L"    </UX>"
-                    L"    <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
+                    L"    <Registration Id='{D54F896D-1952-43E6-9C67-B5652240618C}' UpgradeCode='{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
                     L"        <Arp Register='yes' DisplayName='DisplayName1' DisplayVersion='1.2.3.4' Publisher='Publisher1' HelpLink='http://www.microsoft.com/help'"
                     L"             HelpTelephone='555-555-5555' AboutUrl='http://www.microsoft.com/about' UpdateUrl='http://www.microsoft.com/update'"
                     L"             Comments='Comments1' Contact='Contact1' DisableModify='yes' DisableRemove='yes' />"
@@ -437,7 +437,7 @@ namespace Bootstrapper
 
                 // verify that registration was created
                 Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
-                Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
 
                 // finish registration
                 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, BOOTSTRAPPER_REGISTRATION_TYPE_FULL);
@@ -446,7 +446,7 @@ namespace Bootstrapper
                 // verify that registration was updated
                 Assert::Equal(Int32(BURN_RESUME_MODE_ARP), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
                 Assert::Equal(1, (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
-                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
 
                 Assert::Equal<String^>(gcnew String(L"DisplayName1"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayName"), nullptr));
                 Assert::Equal<String^>(gcnew String(L"1.2.3.4"), (String^)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"DisplayVersion"), nullptr));
@@ -470,7 +470,7 @@ namespace Bootstrapper
 
                 // verify that registration was updated
                 Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
-                Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::Equal<String^>(String::Concat(L"\"", Path::Combine(cacheDirectory, gcnew String(L"setup.exe")), L"\" /burn.runonce"), (String^)Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
 
                 // delete registration
                 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
@@ -479,7 +479,7 @@ namespace Bootstrapper
                 // verify that registration was removed
                 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr));
                 Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr));
-                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
             }
             finally
             {
@@ -517,7 +517,7 @@ namespace Bootstrapper
             BYTE* pbBuffer = NULL;
             SIZE_T cbBuffer = 0;
             
-            String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
+            String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
             try
             {
                 // set mock API's
@@ -532,8 +532,8 @@ namespace Bootstrapper
                     L"    <UX>"
                     L"        <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
                     L"    </UX>"
-                    L"    <RelatedBundle Id='" TEST_BUNDLE_UPGRADE_CODE "' Action='Upgrade' />"
-                    L"    <Registration Id='" TEST_BUNDLE_ID "' Tag='foo' ProviderKey='" TEST_BUNDLE_ID "' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
+                    L"    <RelatedBundle Id='" TEST_BUNDLE_UPGRADE_CODE L"' Action='Upgrade' />"
+                    L"    <Registration Id='" TEST_BUNDLE_ID L"' Tag='foo' ProviderKey='" TEST_BUNDLE_ID L"' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
                     L"        <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />"
                     L"    </Registration>"
                     L"    <Variable Id='MyBurnVariable1' Type='numeric' Value='0' Hidden='no' Persisted='yes' />"
@@ -585,6 +585,7 @@ namespace Bootstrapper
 
                 ReleaseNullBuffer(pbBuffer);
                 cbBuffer = 0;
+
                 // Verify the variables exist
                 Assert::Equal<String^>(gcnew String(L"42"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable1"), nullptr));
                 Assert::Equal<String^>(gcnew String(L"bar"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable2"), nullptr));
@@ -596,12 +597,14 @@ namespace Bootstrapper
                 // Verify we can find ourself via the UpgradeCode
                 hr = BundleEnumRelatedBundle(TEST_BUNDLE_UPGRADE_CODE, BUNDLE_INSTALL_CONTEXT_USER, &dwRelatedBundleIndex, sczRelatedBundleId);
                 TestThrowOnFailure(hr, L"Failed to enumerate related bundle.");
-                Assert::Equal<String^>(gcnew String(TEST_BUNDLE_ID), gcnew String(sczRelatedBundleId));
+
+                NativeAssert::StringEqual(TEST_BUNDLE_ID, sczRelatedBundleId);
 
                 // Verify we can read the bundle variables via the API
                 hr = BundleGetBundleVariable(TEST_BUNDLE_ID, L"MyBurnVariable1", &sczValue);
                 TestThrowOnFailure(hr, L"Failed to read MyBurnVariable1.");
-                Assert::Equal<String^>(gcnew String(L"42"), gcnew String(sczValue));
+
+                NativeAssert::StringEqual(L"42", sczValue);
 
                 // end session
                 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
@@ -626,7 +629,7 @@ namespace Bootstrapper
             }
         }
 
-        [Fact]//(Skip = "Currently fails")]
+        [Fact]
         void ResumeTest()
         {
             HRESULT hr = S_OK;
@@ -643,7 +646,7 @@ namespace Bootstrapper
             BYTE* pbBuffer = NULL;
             SIZE_T cbBuffer = 0;
             SIZE_T piBuffer = 0;
-            String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"));
+            String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(TEST_BUNDLE_ID));
             try
             {
                 // set mock API's
@@ -658,7 +661,7 @@ namespace Bootstrapper
                     L"    <UX>"
                     L"        <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />"
                     L"    </UX>"
-                    L"    <Registration Id='{D54F896D-1952-43e6-9C67-B5652240618C}' UpgradeCode='{D54F896D-1952-43e6-9C67-B5652240618C}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
+                    L"    <Registration Id='{D54F896D-1952-43E6-9C67-B5652240618C}' UpgradeCode='{89FDAE1F-8CC1-48B9-B930-3945E0D3E7F0}' Tag='foo' ProviderKey='foo' Version='1.0.0.0' ExecutableName='setup.exe' PerMachine='no'>"
                     L"        <Arp Register='yes' Publisher='WiX Toolset' DisplayName='RegisterBasicTest' DisplayVersion='1.0.0.0' />"
                     L"    </Registration>"
                     L"    <Variable Id='MyBurnVariable1' Type='numeric' Value='0' Hidden='no' Persisted='yes' />"
@@ -715,6 +718,7 @@ namespace Bootstrapper
 
                 ReleaseNullBuffer(pbBuffer);
                 cbBuffer = 0;
+
                 // Verify the variables exist
                 Assert::Equal<String^>(gcnew String(L"42"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable1"), nullptr));
                 Assert::Equal<String^>(gcnew String(L"bar"), (String^)Registry::GetValue(gcnew String(TEST_VARIABLE_KEY), gcnew String(L"MyBurnVariable2"), nullptr));
@@ -723,7 +727,8 @@ namespace Bootstrapper
 
                 hr = BundleGetBundleVariable(TEST_BUNDLE_ID, L"MyBurnVariable1", &sczValue);
                 TestThrowOnFailure(hr, L"Failed to read MyBurnVariable1.");
-                Assert::Equal<String^>(gcnew String(L"42"), gcnew String(sczValue));
+
+                NativeAssert::StringEqual(L"42", sczValue);
 
                 // read interrupted resume type
                 hr = RegistrationDetectResumeType(&registration, &resumeType);
@@ -736,7 +741,7 @@ namespace Bootstrapper
                 TestThrowOnFailure(hr, L"Failed to suspend session.");
 
                 // verify that run key was removed
-                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
 
                 // read suspend resume type
                 hr = RegistrationDetectResumeType(&registration, &resumeType);
@@ -751,15 +756,12 @@ namespace Bootstrapper
                 hr = VariableDeserialize(&variables, TRUE, pbBuffer, cbBuffer, &piBuffer);
                 TestThrowOnFailure(hr, L"Failed to deserialize variables.");
 
-                //Assert::Equal((SIZE_T)sizeof(rgbData), cbBuffer);
-                //Assert::True(0 == memcmp(pbBuffer, rgbData, sizeof(rgbData)));
-
                 // write active resume mode
                 hr = RegistrationSessionResume(&registration, &variables, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS);
                 TestThrowOnFailure(hr, L"Failed to write active resume mode.");
 
                 // verify that run key was put back
-                Assert::NotEqual((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr));
+                Assert::NotEqual((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(TEST_BUNDLE_ID), nullptr));
 
                 // end session
                 hr = RegistrationSessionEnd(&registration, &variables, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER, BOOTSTRAPPER_REGISTRATION_TYPE_NONE);
diff --git a/src/ext/Bal/CustomizedNativeRecommendedRules.ruleset b/src/ext/Bal/CustomizedNativeRecommendedRules.ruleset
deleted file mode 100644
index 142b141c..00000000
--- a/src/ext/Bal/CustomizedNativeRecommendedRules.ruleset
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RuleSet Name="Customized Microsoft Native Recommended Rules" Description="Microsoft Native Recommended Rules, -C26812" ToolsVersion="16.0">
-  <Include Path="nativerecommendedrules.ruleset" Action="Default" />
-  <Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
-    <!-- We need C style enums since we support BAs written in C -->
-    <Rule Id="C26812" Action="None" />
-  </Rules>
-</RuleSet>
\ No newline at end of file
diff --git a/src/ext/NetFx/CustomizedNativeRecommendedRules.ruleset b/src/ext/NetFx/CustomizedNativeRecommendedRules.ruleset
deleted file mode 100644
index 142b141c..00000000
--- a/src/ext/NetFx/CustomizedNativeRecommendedRules.ruleset
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RuleSet Name="Customized Microsoft Native Recommended Rules" Description="Microsoft Native Recommended Rules, -C26812" ToolsVersion="16.0">
-  <Include Path="nativerecommendedrules.ruleset" Action="Default" />
-  <Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
-    <!-- We need C style enums since we support BAs written in C -->
-    <Rule Id="C26812" Action="None" />
-  </Rules>
-</RuleSet>
\ No newline at end of file
diff --git a/src/ext/Util/CustomizedNativeRecommendedRules.ruleset b/src/ext/Util/CustomizedNativeRecommendedRules.ruleset
deleted file mode 100644
index 142b141c..00000000
--- a/src/ext/Util/CustomizedNativeRecommendedRules.ruleset
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RuleSet Name="Customized Microsoft Native Recommended Rules" Description="Microsoft Native Recommended Rules, -C26812" ToolsVersion="16.0">
-  <Include Path="nativerecommendedrules.ruleset" Action="Default" />
-  <Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
-    <!-- We need C style enums since we support BAs written in C -->
-    <Rule Id="C26812" Action="None" />
-  </Rules>
-</RuleSet>
\ No newline at end of file
diff --git a/src/libs/dutil/CustomizedNativeRecommendedRules.ruleset b/src/libs/dutil/CustomizedNativeRecommendedRules.ruleset
deleted file mode 100644
index 142b141c..00000000
--- a/src/libs/dutil/CustomizedNativeRecommendedRules.ruleset
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RuleSet Name="Customized Microsoft Native Recommended Rules" Description="Microsoft Native Recommended Rules, -C26812" ToolsVersion="16.0">
-  <Include Path="nativerecommendedrules.ruleset" Action="Default" />
-  <Rules AnalyzerId="Microsoft.Analyzers.NativeCodeAnalysis" RuleNamespace="Microsoft.Rules.Native">
-    <!-- We need C style enums since we support BAs written in C -->
-    <Rule Id="C26812" Action="None" />
-  </Rules>
-</RuleSet>
\ No newline at end of file
diff --git a/src/libs/dutil/WixToolset.DUtil/butil.cpp b/src/libs/dutil/WixToolset.DUtil/butil.cpp
index cda2a658..ca73f0c3 100644
--- a/src/libs/dutil/WixToolset.DUtil/butil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/butil.cpp
@@ -8,6 +8,7 @@
 #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 ButilExitWithRootFailure(x, e, s, ...) ExitWithRootFailureSource(DUTIL_SOURCE_BUTIL, x, e, 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__)
@@ -22,7 +23,29 @@ const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE = L"BundleUpgrade
 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey";
 const LPCWSTR BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY = L"variables";
 
+enum INTERNAL_BUNDLE_STATUS
+{
+    INTERNAL_BUNDLE_STATUS_SUCCESS,
+    INTERNAL_BUNDLE_STATUS_UNKNOWN_BUNDLE,
+    INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY,
+};
+
 // Forward declarations.
+/********************************************************************
+LocateAndQueryBundleValue - Locates the requested key for the bundle,
+    then queries the registry type for requested value.
+
+NOTE: caller is responsible for closing key
+********************************************************************/
+static HRESULT LocateAndQueryBundleValue(
+    __in_z LPCWSTR wzBundleId,
+    __in_opt LPCWSTR wzSubKey,
+    __in LPCWSTR wzValueName,
+    __inout HKEY* phKey,
+    __inout DWORD* pdwType,
+    __out INTERNAL_BUNDLE_STATUS* pStatus
+    );
+
 /********************************************************************
 OpenBundleKey - Opens the bundle uninstallation key for a given bundle
 
@@ -31,43 +54,44 @@ NOTE: caller is responsible for closing key
 static HRESULT OpenBundleKey(
     __in_z LPCWSTR wzBundleId,
     __in BUNDLE_INSTALL_CONTEXT context,
-    __in_opt LPCWSTR szSubKey,
-    __inout HKEY* key);
+    __in_opt LPCWSTR wzSubKey,
+    __inout HKEY* phKey
+    );
 
-/********************************************************************
-BundleGetBundleInfo - Read the registration data for a gven bundle
-********************************************************************/
-extern "C" HRESULT DAPI BundleGetBundleInfo(
-  __in_z LPCWSTR wzBundleId,
-  __in_z LPCWSTR wzAttribute,
-  __out_ecount_opt(*pcchValueBuf) LPWSTR lpValueBuf,
-  __inout_opt LPDWORD pcchValueBuf
-  )
+DAPI_(HRESULT) 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;
     LPWSTR sczValue = NULL;
     HKEY hkBundle = NULL;
+    INTERNAL_BUNDLE_STATUS status = INTERNAL_BUNDLE_STATUS_SUCCESS;
     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.");
+        ButilExitWithRootFailure(hr, E_INVALIDARG, "An invalid parameter was passed to the function.");
     }
 
-    if (FAILED(hr = OpenBundleKey(wzBundleId, BUNDLE_INSTALL_CONTEXT_MACHINE, NULL, &hkBundle)) &&
-        FAILED(hr = OpenBundleKey(wzBundleId, BUNDLE_INSTALL_CONTEXT_USER, NULL, &hkBundle)))
+    hr = LocateAndQueryBundleValue(wzBundleId, NULL, wzAttribute, &hkBundle, &dwType, &status);
+    ButilExitOnFailure(hr, "Failed to locate and query bundle attribute.");
+
+    switch (status)
     {
-        ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key path.");
+    case INTERNAL_BUNDLE_STATUS_UNKNOWN_BUNDLE:
+        ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT));
+    case INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY:
+        // If the bundle doesn't have the property defined, return ERROR_UNKNOWN_PROPERTY
+        ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY));
     }
 
-    // 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:
@@ -82,12 +106,11 @@ extern "C" HRESULT DAPI BundleGetBundleInfo(
             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);
-
+            ButilExitWithRootFailure(hr, E_NOTIMPL, "Reading bundle info of type 0x%x not implemented.", dwType);
     }
 
     hr = ::StringCchLengthW(sczValue, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchSource));
-    ButilExitOnFailure(hr, "Failed to calculate length of string");
+    ButilExitOnRootFailure(hr, "Failed to calculate length of string.");
 
     if (lpValueBuf)
     {
@@ -95,11 +118,11 @@ extern "C" HRESULT DAPI BundleGetBundleInfo(
         if (*pcchValueBuf <= cchSource)
         {
             *pcchValueBuf = ++cchSource;
-            ButilExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_MORE_DATA), "A buffer is too small to hold the requested data.");
+            ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_MORE_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.");
+        ButilExitOnRootFailure(hr, "Failed to copy the property value to the output buffer.");
         
         *pcchValueBuf = cchSource++;        
     }
@@ -111,10 +134,8 @@ LExit:
     return hr;
 }
 
-/********************************************************************
-********************************************************************/
 
-extern "C" HRESULT DAPI BundleEnumRelatedBundle(
+DAPI_(HRESULT) BundleEnumRelatedBundle(
   __in_z LPCWSTR wzUpgradeCode,
   __in BUNDLE_INSTALL_CONTEXT context,
   __inout PDWORD pdwStartIndex,
@@ -170,6 +191,7 @@ extern "C" HRESULT DAPI BundleEnumRelatedBundle(
             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;
@@ -202,8 +224,7 @@ extern "C" HRESULT DAPI BundleEnumRelatedBundle(
                 break;
 
             default:
-                ButilExitOnFailure(hr = E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType);
-
+                ButilExitWithRootFailure(hr, E_NOTIMPL, "BundleUpgradeCode of type 0x%x not implemented.", dwType);
         }
 
         if (fUpgradeCodeFound)
@@ -211,10 +232,10 @@ extern "C" HRESULT DAPI BundleEnumRelatedBundle(
             if (lpBundleIdBuf)
             {
                 hr = ::StringCchLengthW(sczUninstallSubKey, STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchUninstallSubKey));
-                ButilExitOnFailure(hr, "Failed to calculate length of string");
+                ButilExitOnRootFailure(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.");
+                ButilExitOnRootFailure(hr, "Failed to copy the property value to the output buffer.");
             }
 
             break;
@@ -237,52 +258,37 @@ LExit:
     return hr;
 }
 
-/********************************************************************
-BundleGetBundleVariable - Queries the bundle installation metadata for a given variable,
-the caller is expected to free the memory returned vis psczValue
-RETURNS:
-S_OK
- Success, if the variable had a value, it's returned in psczValue
-E_INVALIDARG
- An invalid parameter was passed to the function.
-HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT)
- The bundle is not installed
-HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY)
- The variable is unrecognized
-E_NOTIMPL:
- Tried to read a bundle variable for a type which has not been implemented
-
-All other returns are unexpected returns from other dutil methods.
-********************************************************************/
 
-extern "C" HRESULT DAPI BundleGetBundleVariable(
+DAPI_(HRESULT) BundleGetBundleVariable(
     __in_z LPCWSTR wzBundleId,
     __in_z LPCWSTR wzVariable,
-    __deref_out_z LPWSTR * psczValue
-)
+    __deref_out_z LPWSTR* psczValue
+    )
 {
     Assert(wzBundleId && wzVariable);
 
     HRESULT hr = S_OK;
-    BUNDLE_INSTALL_CONTEXT context = BUNDLE_INSTALL_CONTEXT_MACHINE;
     HKEY hkBundle = NULL;
+    INTERNAL_BUNDLE_STATUS status = INTERNAL_BUNDLE_STATUS_SUCCESS;
     DWORD dwType = 0;
 
     if (!wzBundleId || !wzVariable || !psczValue)
     {
-        ButilExitOnFailure(hr = E_INVALIDARG, "An invalid parameter was passed to the function.");
+        ButilExitWithRootFailure(hr, E_INVALIDARG, "An invalid parameter was passed to the function.");
     }
 
-    if (FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_MACHINE, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY, &hkBundle)) &&
-        FAILED(hr = OpenBundleKey(wzBundleId, context = BUNDLE_INSTALL_CONTEXT_USER, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY, &hkBundle)))
+    hr = LocateAndQueryBundleValue(wzBundleId, BUNDLE_REGISTRATION_REGISTRY_BUNDLE_VARIABLE_KEY, wzVariable, &hkBundle, &dwType, &status);
+    ButilExitOnFailure(hr, "Failed to locate and query bundle variable.");
+
+    switch (status)
     {
-        ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) : hr, "Failed to locate bundle uninstall key variable path.");
+    case INTERNAL_BUNDLE_STATUS_UNKNOWN_BUNDLE:
+        ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT));
+    case INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY:
+        // If the bundle doesn't have the shared variable defined, return ERROR_UNKNOWN_PROPERTY
+        ExitFunction1(hr = HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY));
     }
 
-    // If the bundle doesn't have the shared variable defined, return ERROR_UNKNOWN_PROPERTY
-    hr = RegGetType(hkBundle, wzVariable, &dwType);
-    ButilExitOnFailure(E_FILENOTFOUND == hr ? HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) : hr, "Failed to locate bundle variable.");
-
     switch (dwType)
     {
     case REG_SZ:
@@ -293,35 +299,74 @@ extern "C" HRESULT DAPI BundleGetBundleVariable(
         hr = S_OK;
         break;
     default:
-        ButilExitOnFailure(hr = E_NOTIMPL, "Reading bundle variable of type 0x%x not implemented.", dwType);
-
+        ButilExitWithRootFailure(hr, E_NOTIMPL, "Reading bundle variable of type 0x%x not implemented.", dwType);
     }
 
 LExit:
     ReleaseRegKey(hkBundle);
 
     return hr;
+}
+
+static HRESULT LocateAndQueryBundleValue(
+    __in_z LPCWSTR wzBundleId,
+    __in_opt LPCWSTR wzSubKey,
+    __in LPCWSTR wzValueName,
+    __inout HKEY* phKey,
+    __inout DWORD* pdwType,
+    __out INTERNAL_BUNDLE_STATUS* pStatus
+    )
+{
+    HRESULT hr = S_OK;
+
+    *pStatus = INTERNAL_BUNDLE_STATUS_SUCCESS;
 
+    if (FAILED(hr = OpenBundleKey(wzBundleId, BUNDLE_INSTALL_CONTEXT_MACHINE, wzSubKey, phKey)) &&
+        FAILED(hr = OpenBundleKey(wzBundleId, BUNDLE_INSTALL_CONTEXT_USER, wzSubKey, phKey)))
+    {
+        if (E_FILENOTFOUND == hr)
+        {
+            *pStatus = INTERNAL_BUNDLE_STATUS_UNKNOWN_BUNDLE;
+            ExitFunction1(hr = S_OK);
+        }
+
+        ButilExitOnFailure(hr, "Failed to open bundle key.");
+    }
+
+    // If the bundle doesn't have the value defined, return ERROR_UNKNOWN_PROPERTY
+    hr = RegGetType(*phKey, wzValueName, pdwType);
+    if (FAILED(hr))
+    {
+        if (E_FILENOTFOUND == hr)
+        {
+            *pStatus = INTERNAL_BUNDLE_STATUS_UNKNOWN_PROPERTY;
+            ExitFunction1(hr = S_OK);
+        }
+
+        ButilExitOnFailure(hr, "Failed to read bundle value.");
+    }
+
+LExit:
+    return hr;
 }
-/********************************************************************
-*
-********************************************************************/
-HRESULT OpenBundleKey(
+
+static HRESULT OpenBundleKey(
     __in_z LPCWSTR wzBundleId,
     __in BUNDLE_INSTALL_CONTEXT context,
-    __in_opt LPCWSTR szSubKey,
-    __inout HKEY* key)
+    __in_opt LPCWSTR wzSubKey,
+    __inout HKEY* phKey
+    )
 {
-    Assert(key && wzBundleId);
-    AssertSz(NULL == *key, "*key should be null");
+    Assert(phKey && wzBundleId);
+    AssertSz(NULL == *phKey, "*key should be null");
 
     HRESULT hr = S_OK;
     HKEY hkRoot = BUNDLE_INSTALL_CONTEXT_USER == context ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
     LPWSTR sczKeypath = NULL;
 
-    if (szSubKey)
+    if (wzSubKey)
     {
-        hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId, szSubKey);
+        hr = StrAllocFormatted(&sczKeypath, L"%ls\\%ls\\%ls", BUNDLE_REGISTRATION_REGISTRY_UNINSTALL_KEY, wzBundleId, wzSubKey);
     }
     else
     {
@@ -329,7 +374,7 @@ HRESULT OpenBundleKey(
     }
     ButilExitOnFailure(hr, "Failed to allocate bundle uninstall key path.");
 
-    hr = RegOpen(hkRoot, sczKeypath, KEY_READ, key);
+    hr = RegOpen(hkRoot, sczKeypath, KEY_READ, phKey);
     ButilExitOnFailure(hr, "Failed to open bundle uninstall key path.");
 
 LExit:
@@ -337,4 +382,3 @@ LExit:
 
     return hr;
 }
-
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/butil.h b/src/libs/dutil/WixToolset.DUtil/inc/butil.h
index d910c113..721d9ad6 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/butil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/butil.h
@@ -6,7 +6,7 @@
 extern "C" {
 #endif
 
-typedef enum BUNDLE_INSTALL_CONTEXT
+typedef enum _BUNDLE_INSTALL_CONTEXT
 {
     BUNDLE_INSTALL_CONTEXT_MACHINE,
     BUNDLE_INSTALL_CONTEXT_USER,
@@ -55,11 +55,29 @@ HRESULT DAPI BundleEnumRelatedBundle(
   __out_ecount(MAX_GUID_CHARS+1)  LPWSTR lpBundleIdBuf
     );
 
+/********************************************************************
+BundleGetBundleVariable - Queries the bundle installation metadata for a given variable,
+    the caller is expected to free the memory returned vis psczValue
+
+RETURNS:
+    S_OK
+        Success, if the variable had a value, it's returned in psczValue
+    E_INVALIDARG
+        An invalid parameter was passed to the function.
+    HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT)
+        The bundle is not installed
+    HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY)
+        The variable is unrecognized
+    E_NOTIMPL:
+        Tried to read a bundle variable for a type which has not been implemented
+
+    All other returns are unexpected returns from other dutil methods.
+********************************************************************/
 HRESULT DAPI BundleGetBundleVariable(
     __in_z LPCWSTR wzBundleId,
     __in_z LPCWSTR wzVariable,
     __deref_out_z LPWSTR* psczValue
-);
+    );
 
 
 #ifdef __cplusplus
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/regutil.h b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
index fcf13054..ae47f75e 100644
--- a/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
+++ b/src/libs/dutil/WixToolset.DUtil/inc/regutil.h
@@ -99,9 +99,23 @@ typedef LSTATUS (APIENTRY *PFN_REGDELETEVALUEW)(
     __in_opt LPCWSTR lpValueName
     );
 
+/********************************************************************
+ RegInitialize - initializes regutil
+
+*********************************************************************/
 HRESULT DAPI RegInitialize();
+
+/********************************************************************
+ RegUninitialize - uninitializes regutil
+
+*********************************************************************/
 void DAPI RegUninitialize();
 
+/********************************************************************
+ RegFunctionOverride - overrides the registry functions. Typically used
+                       for unit testing.
+
+*********************************************************************/
 void DAPI RegFunctionOverride(
     __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW,
     __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW,
@@ -113,12 +127,22 @@ void DAPI RegFunctionOverride(
     __in_opt PFN_REGSETVALUEEXW pfnRegSetValueExW,
     __in_opt PFN_REGDELETEVALUEW pfnRegDeleteValueW
     );
+
+/********************************************************************
+ RegCreate - creates a registry key.
+
+*********************************************************************/
 HRESULT DAPI RegCreate(
     __in HKEY hkRoot,
     __in_z LPCWSTR wzSubKey,
     __in DWORD dwAccess,
     __out HKEY* phk
     );
+
+/********************************************************************
+ RegCreateEx - creates a registry key with extra options.
+
+*********************************************************************/
 HRESULT DAPI RegCreateEx(
     __in HKEY hkRoot,
     __in_z LPCWSTR wzSubKey,
@@ -128,112 +152,229 @@ HRESULT DAPI RegCreateEx(
     __out HKEY* phk,
     __out_opt BOOL* pfCreated
     );
+
+/********************************************************************
+ RegOpen - opens a registry key.
+
+*********************************************************************/
 HRESULT DAPI RegOpen(
     __in HKEY hkRoot,
     __in_z LPCWSTR wzSubKey,
     __in DWORD dwAccess,
     __out HKEY* phk
     );
+
+/********************************************************************
+ RegDelete - deletes a registry key (and optionally it's whole tree).
+
+*********************************************************************/
 HRESULT DAPI RegDelete(
     __in HKEY hkRoot,
     __in_z LPCWSTR wzSubKey,
     __in REG_KEY_BITNESS kbKeyBitness,
     __in BOOL fDeleteTree
     );
+
+/********************************************************************
+ RegKeyEnum - enumerates child registry keys.
+
+*********************************************************************/
 HRESULT DAPI RegKeyEnum(
     __in HKEY hk,
     __in DWORD dwIndex,
     __deref_out_z LPWSTR* psczKey
     );
+
+/********************************************************************
+ RegValueEnum - enumerates registry values.
+
+*********************************************************************/
 HRESULT DAPI RegValueEnum(
     __in HKEY hk,
     __in DWORD dwIndex,
     __deref_out_z LPWSTR* psczName,
     __out_opt DWORD *pdwType
     );
+
+/********************************************************************
+ RegGetType - reads a registry key value type.
+ *********************************************************************/
 HRESULT DAPI RegGetType(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __out DWORD *pdwType
      );
+
+/********************************************************************
+ RegReadBinary - reads a registry key binary value.
+ NOTE: caller is responsible for freeing *ppbBuffer
+*********************************************************************/
 HRESULT DAPI RegReadBinary(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer,
     __out SIZE_T *pcbBuffer
      );
+
+/********************************************************************
+ RegReadString - reads a registry key value as a string.
+
+*********************************************************************/
 HRESULT DAPI RegReadString(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __deref_out_z LPWSTR* psczValue
     );
+
+/********************************************************************
+ RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array.
+
+*********************************************************************/
 HRESULT DAPI RegReadStringArray(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings,
     __out DWORD *pcStrings
     );
+
+/********************************************************************
+ RegReadVersion - reads a registry key value as a version.
+
+*********************************************************************/
 HRESULT DAPI RegReadVersion(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __out DWORD64* pdw64Version
     );
+
+/********************************************************************
+ RegReadNone - reads a NONE registry key value.
+
+*********************************************************************/
 HRESULT DAPI RegReadNone(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName
-);
+    );
+
+/********************************************************************
+ RegReadNumber - reads a DWORD registry key value as a number.
+
+*********************************************************************/
 HRESULT DAPI RegReadNumber(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __out DWORD* pdwValue
     );
+
+/********************************************************************
+ RegReadQword - reads a QWORD registry key value as a number.
+
+*********************************************************************/
 HRESULT DAPI RegReadQword(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __out DWORD64* pqwValue
     );
+
+/********************************************************************
+ RegWriteBinary - writes a registry key value as a binary.
+
+*********************************************************************/
 HRESULT DAPI RegWriteBinary(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __in_bcount(cbBuffer) const BYTE *pbBuffer,
     __in DWORD cbBuffer
     );
-HRESULT DAPI RegWriteString(
+
+/********************************************************************
+RegWriteExpandString - writes a registry key value as an expand string.
+
+Note: if wzValue is NULL the value will be removed.
+*********************************************************************/
+HRESULT DAPI RegWriteExpandString(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __in_z_opt LPCWSTR wzValue
     );
-HRESULT DAPI RegWriteStringArray(
+
+/********************************************************************
+ RegWriteString - writes a registry key value as a string.
+
+ Note: if wzValue is NULL the value will be removed.
+*********************************************************************/
+HRESULT DAPI RegWriteString(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
-    __in_ecount(cStrings) LPWSTR *rgwzStrings,
-    __in DWORD cStrings
+    __in_z_opt LPCWSTR wzValue
     );
-HRESULT DAPI RegWriteStringFormatted(
+
+/********************************************************************
+ RegWriteStringFormatted - writes a registry key value as a formatted string.
+
+*********************************************************************/
+HRESULT DAPIV RegWriteStringFormatted(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __in __format_string LPCWSTR szFormat,
     ...
     );
+
+/********************************************************************
+ RegWriteStringArray - writes an array of strings as a REG_MULTI_SZ value
+
+*********************************************************************/
+HRESULT DAPI RegWriteStringArray(
+    __in HKEY hk,
+    __in_z_opt LPCWSTR wzName,
+    __in_ecount(cStrings) LPWSTR* rgwzStrings,
+    __in DWORD cStrings
+    );
+
+/********************************************************************
+ RegWriteNone - writes a registry key value as none.
+
+*********************************************************************/
 HRESULT DAPI RegWriteNone(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName
-);
+    );
+
+/********************************************************************
+ RegWriteNumber - writes a registry key value as a number.
+
+*********************************************************************/
 HRESULT DAPI RegWriteNumber(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __in DWORD dwValue
     );
+
+/********************************************************************
+ RegWriteQword - writes a registry key value as a Qword.
+
+*********************************************************************/
 HRESULT DAPI RegWriteQword(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __in DWORD64 qwValue
     );
+
+/********************************************************************
+ RegQueryKey - queries the key for the number of subkeys and values.
+
+*********************************************************************/
 HRESULT DAPI RegQueryKey(
     __in HKEY hk,
     __out_opt DWORD* pcSubKeys,
     __out_opt DWORD* pcValues
     );
+
+/********************************************************************
+RegKeyReadNumber - reads a DWORD registry key value as a number from
+a specified subkey.
+
+*********************************************************************/
 HRESULT DAPI RegKeyReadNumber(
     __in HKEY hk,
     __in_z LPCWSTR wzSubKey,
@@ -241,6 +382,12 @@ HRESULT DAPI RegKeyReadNumber(
     __in BOOL f64Bit,
     __out DWORD* pdwValue
     );
+
+/********************************************************************
+RegValueExists - determines whether a named value exists in a
+specified subkey.
+
+*********************************************************************/
 BOOL DAPI RegValueExists(
     __in HKEY hk,
     __in_z LPCWSTR wzSubKey,
diff --git a/src/libs/dutil/WixToolset.DUtil/regutil.cpp b/src/libs/dutil/WixToolset.DUtil/regutil.cpp
index 458d8586..57093f97 100644
--- a/src/libs/dutil/WixToolset.DUtil/regutil.cpp
+++ b/src/libs/dutil/WixToolset.DUtil/regutil.cpp
@@ -37,14 +37,9 @@ static HRESULT WriteStringToRegistry(
     __in_z_opt LPCWSTR wzName,
     __in_z_opt LPCWSTR wzValue,
     __in DWORD dwType
-);
+    );
 
-/********************************************************************
- RegInitialize - initializes regutil
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegInitialize(
-    )
+DAPI_(HRESULT) RegInitialize()
 {
     HRESULT hr = S_OK;
 
@@ -66,12 +61,7 @@ LExit:
 }
 
 
-/********************************************************************
- RegUninitialize - uninitializes regutil
-
-*********************************************************************/
-extern "C" void DAPI RegUninitialize(
-    )
+DAPI_(void) RegUninitialize()
 {
     if (vhAdvApi32Dll)
     {
@@ -85,12 +75,7 @@ extern "C" void DAPI RegUninitialize(
 }
 
 
-/********************************************************************
- RegFunctionOverride - overrides the registry functions. Typically used
-                       for unit testing.
-
-*********************************************************************/
-extern "C" void DAPI RegFunctionOverride(
+DAPI_(void) RegFunctionOverride(
     __in_opt PFN_REGCREATEKEYEXW pfnRegCreateKeyExW,
     __in_opt PFN_REGOPENKEYEXW pfnRegOpenKeyExW,
     __in_opt PFN_REGDELETEKEYEXW pfnRegDeleteKeyExW,
@@ -114,11 +99,7 @@ extern "C" void DAPI RegFunctionOverride(
 }
 
 
-/********************************************************************
- RegCreate - creates a registry key.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegCreate(
+DAPI_(HRESULT) RegCreate(
     __in HKEY hkRoot,
     __in_z LPCWSTR wzSubKey,
     __in DWORD dwAccess,
@@ -136,11 +117,7 @@ LExit:
 }
 
 
-/********************************************************************
- RegCreate - creates a registry key with extra options.
-
-*********************************************************************/
-HRESULT DAPI RegCreateEx(
+DAPI_(HRESULT) RegCreateEx(
     __in HKEY hkRoot,
     __in_z LPCWSTR wzSubKey,
     __in DWORD dwAccess,
@@ -167,11 +144,7 @@ LExit:
 }
 
 
-/********************************************************************
- RegOpen - opens a registry key.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegOpen(
+DAPI_(HRESULT) RegOpen(
     __in HKEY hkRoot,
     __in_z LPCWSTR wzSubKey,
     __in DWORD dwAccess,
@@ -193,11 +166,7 @@ LExit:
 }
 
 
-/********************************************************************
- RegDelete - deletes a registry key (and optionally it's whole tree).
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegDelete(
+DAPI_(HRESULT) RegDelete(
     __in HKEY hkRoot,
     __in_z LPCWSTR wzSubKey,
     __in REG_KEY_BITNESS kbKeyBitness,
@@ -282,11 +251,7 @@ LExit:
 }
 
 
-/********************************************************************
- RegKeyEnum - enumerates child registry keys.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegKeyEnum(
+DAPI_(HRESULT) RegKeyEnum(
     __in HKEY hk,
     __in DWORD dwIndex,
     __deref_out_z LPWSTR* psczKey
@@ -342,11 +307,7 @@ LExit:
 }
 
 
-/********************************************************************
- RegValueEnum - enumerates registry values.
-
-*********************************************************************/
-HRESULT DAPI RegValueEnum(
+DAPI_(HRESULT) RegValueEnum(
     __in HKEY hk,
     __in DWORD dwIndex,
     __deref_out_z LPWSTR* psczName,
@@ -377,10 +338,7 @@ LExit:
     return hr;
 }
 
-/********************************************************************
- RegGetType - reads a registry key value type.
- *********************************************************************/
-HRESULT DAPI RegGetType(
+DAPI_(HRESULT) RegGetType(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __out DWORD *pdwType
@@ -400,11 +358,7 @@ LExit:
     return hr;
 }
 
-/********************************************************************
- RegReadBinary - reads a registry key binary value.
- NOTE: caller is responsible for freeing *ppbBuffer
-*********************************************************************/
-HRESULT DAPI RegReadBinary(
+DAPI_(HRESULT) RegReadBinary(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __deref_out_bcount_opt(*pcbBuffer) BYTE** ppbBuffer,
@@ -453,11 +407,7 @@ LExit:
 }
 
 
-/********************************************************************
- RegReadString - reads a registry key value as a string.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegReadString(
+DAPI_(HRESULT) RegReadString(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __deref_out_z LPWSTR* psczValue
@@ -530,11 +480,7 @@ LExit:
 }
 
 
-/********************************************************************
- RegReadStringArray - reads a registry key value REG_MULTI_SZ value as a string array.
-
-*********************************************************************/
-HRESULT DAPI RegReadStringArray(
+DAPI_(HRESULT) RegReadStringArray(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __deref_out_ecount_opt(*pcStrings) LPWSTR** prgsczStrings,
@@ -626,11 +572,7 @@ LExit:
 }
 
 
-/********************************************************************
- RegReadVersion - reads a registry key value as a version.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegReadVersion(
+DAPI_(HRESULT) RegReadVersion(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __out DWORD64* pdw64Version
@@ -672,13 +614,10 @@ LExit:
     return hr;
 }
 
-/********************************************************************
- RegReadNone - reads a NONE registry key value.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegReadNone(
+DAPI_(HRESULT) RegReadNone(
     __in HKEY hk,
-    __in_z_opt LPCWSTR wzName)
+    __in_z_opt LPCWSTR wzName
+    )
 {
     HRESULT hr = S_OK;
     DWORD er = ERROR_SUCCESS;
@@ -694,18 +633,14 @@ extern "C" HRESULT DAPI RegReadNone(
     if (REG_NONE != dwType)
     {
         hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
-        RegExitOnRootFailure(hr, "Error reading version registry value due to unexpected data type: %u", dwType);
+        RegExitOnRootFailure(hr, "Error reading none registry value due to unexpected data type: %u", dwType);
     }
 
 LExit:
     return hr;
 }
 
-/********************************************************************
- RegReadNumber - reads a DWORD registry key value as a number.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegReadNumber(
+DAPI_(HRESULT) RegReadNumber(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __out DWORD* pdwValue
@@ -734,11 +669,7 @@ LExit:
 }
 
 
-/********************************************************************
- RegReadQword - reads a QWORD registry key value as a number.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegReadQword(
+DAPI_(HRESULT) RegReadQword(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __out DWORD64* pqwValue
@@ -767,11 +698,7 @@ LExit:
 }
 
 
-/********************************************************************
- RegWriteBinary - writes a registry key value as a binary.
-
-*********************************************************************/
-HRESULT DAPI RegWriteBinary(
+DAPI_(HRESULT) RegWriteBinary(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __in_bcount(cbBuffer) const BYTE *pbBuffer,
@@ -789,27 +716,17 @@ LExit:
 }
 
 
-/********************************************************************
-RegWriteExpandString - writes a registry key value as an expand string.
-
-Note: if wzValue is NULL the value will be removed.
-*********************************************************************/
-extern "C" HRESULT DAPI RegWriteExpandString(
+DAPI_(HRESULT) RegWriteExpandString(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __in_z_opt LPCWSTR wzValue
-)
+    )
 {
     return WriteStringToRegistry(hk, wzName, wzValue, REG_EXPAND_SZ);
 }
 
 
-/********************************************************************
- RegWriteString - writes a registry key value as a string.
-
- Note: if wzValue is NULL the value will be removed.
-*********************************************************************/
-extern "C" HRESULT DAPI RegWriteString(
+DAPI_(HRESULT) RegWriteString(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __in_z_opt LPCWSTR wzValue
@@ -819,11 +736,7 @@ extern "C" HRESULT DAPI RegWriteString(
 }
 
 
-/********************************************************************
- RegWriteStringFormatted - writes a registry key value as a formatted string.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegWriteStringFormatted(
+DAPIV_(HRESULT) RegWriteStringFormatted(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __in __format_string LPCWSTR szFormat,
@@ -848,11 +761,7 @@ LExit:
 }
 
 
-/********************************************************************
- RegWriteStringArray - writes an array of strings as a REG_MULTI_SZ value
-
-*********************************************************************/
-HRESULT DAPI RegWriteStringArray(
+DAPI_(HRESULT) RegWriteStringArray(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __in_ecount(cValues) LPWSTR *rgwzValues,
@@ -913,14 +822,10 @@ LExit:
     return hr;
 }
 
-/********************************************************************
- RegWriteNone - writes a registry key value as none.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegWriteNone(
+DAPI_(HRESULT) RegWriteNone(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName
-)
+    )
 {
     HRESULT hr = S_OK;
     DWORD er = ERROR_SUCCESS;
@@ -932,11 +837,7 @@ LExit:
     return hr;
 }
 
-/********************************************************************
- RegWriteNumber - writes a registry key value as a number.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegWriteNumber(
+DAPI_(HRESULT) RegWriteNumber(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __in DWORD dwValue
@@ -952,11 +853,7 @@ LExit:
     return hr;
 }
 
-/********************************************************************
- RegWriteQword - writes a registry key value as a Qword.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegWriteQword(
+DAPI_(HRESULT) RegWriteQword(
     __in HKEY hk,
     __in_z_opt LPCWSTR wzName,
     __in DWORD64 qwValue
@@ -972,11 +869,7 @@ LExit:
     return hr;
 }
 
-/********************************************************************
- RegQueryKey - queries the key for the number of subkeys and values.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegQueryKey(
+DAPI_(HRESULT) RegQueryKey(
     __in HKEY hk,
     __out_opt DWORD* pcSubKeys,
     __out_opt DWORD* pcValues
@@ -992,12 +885,7 @@ LExit:
     return hr;
 }
 
-/********************************************************************
-RegKeyReadNumber - reads a DWORD registry key value as a number from
-a specified subkey.
-
-*********************************************************************/
-extern "C" HRESULT DAPI RegKeyReadNumber(
+DAPI_(HRESULT) RegKeyReadNumber(
     __in HKEY hk,
     __in_z LPCWSTR wzSubKey,
     __in_z_opt LPCWSTR wzName,
@@ -1025,7 +913,7 @@ RegValueExists - determines whether a named value exists in a
 specified subkey.
 
 *********************************************************************/
-extern "C" BOOL DAPI RegValueExists(
+DAPI_(BOOL) RegValueExists(
     __in HKEY hk,
     __in_z LPCWSTR wzSubKey,
     __in_z_opt LPCWSTR wzName,
-- 
cgit v1.2.3-55-g6feb