diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/engine/core.h | 1 | ||||
| -rw-r--r-- | src/engine/registration.cpp | 63 | ||||
| -rw-r--r-- | src/engine/variable.cpp | 43 | ||||
| -rw-r--r-- | src/test/BurnUnitTest/RegistrationTest.cpp | 110 |
4 files changed, 163 insertions, 54 deletions
diff --git a/src/engine/core.h b/src/engine/core.h index 75a61614..b4e0e0d3 100644 --- a/src/engine/core.h +++ b/src/engine/core.h | |||
| @@ -46,6 +46,7 @@ const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder | |||
| 46 | const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag"; | 46 | const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag"; |
| 47 | const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel"; | 47 | const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel"; |
| 48 | const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion"; | 48 | const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion"; |
| 49 | const LPCWSTR BURN_REBOOT_PENDING = L"RebootPending"; | ||
| 49 | 50 | ||
| 50 | // The following constants must stay in sync with src\wix\Binder.cs | 51 | // The following constants must stay in sync with src\wix\Binder.cs |
| 51 | const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName"; | 52 | const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName"; |
diff --git a/src/engine/registration.cpp b/src/engine/registration.cpp index dc4b88bf..fc5ae627 100644 --- a/src/engine/registration.cpp +++ b/src/engine/registration.cpp | |||
| @@ -92,6 +92,10 @@ static HRESULT UpdateBundleNameRegistration( | |||
| 92 | __in BURN_VARIABLES* pVariables, | 92 | __in BURN_VARIABLES* pVariables, |
| 93 | __in HKEY hkRegistration | 93 | __in HKEY hkRegistration |
| 94 | ); | 94 | ); |
| 95 | static BOOL IsWuRebootPending(); | ||
| 96 | static BOOL IsBundleRebootPending( | ||
| 97 | __in BURN_REGISTRATION* pRegistration | ||
| 98 | ); | ||
| 95 | 99 | ||
| 96 | // function definitions | 100 | // function definitions |
| 97 | 101 | ||
| @@ -443,7 +447,10 @@ extern "C" HRESULT RegistrationSetVariables( | |||
| 443 | ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); | 447 | ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); |
| 444 | 448 | ||
| 445 | hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->pVersion, TRUE); | 449 | hr = VariableSetVersion(pVariables, BURN_BUNDLE_VERSION, pRegistration->pVersion, TRUE); |
| 446 | ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable."); | 450 | ExitOnFailure(hr, "Failed to overwrite the bundle version built-in variable."); |
| 451 | |||
| 452 | hr = VariableSetNumeric(pVariables, BURN_REBOOT_PENDING, IsBundleRebootPending(pRegistration) || IsWuRebootPending(), TRUE); | ||
| 453 | ExitOnFailure(hr, "Failed to overwrite the bundle reboot-pending built-in variable."); | ||
| 447 | 454 | ||
| 448 | LExit: | 455 | LExit: |
| 449 | ReleaseStr(sczBundleManufacturer); | 456 | ReleaseStr(sczBundleManufacturer); |
| @@ -491,17 +498,10 @@ extern "C" HRESULT RegistrationDetectResumeType( | |||
| 491 | ) | 498 | ) |
| 492 | { | 499 | { |
| 493 | HRESULT hr = S_OK; | 500 | HRESULT hr = S_OK; |
| 494 | LPWSTR sczRebootRequiredKey = NULL; | ||
| 495 | HKEY hkRebootRequired = NULL; | ||
| 496 | HKEY hkRegistration = NULL; | 501 | HKEY hkRegistration = NULL; |
| 497 | DWORD dwResume = 0; | 502 | DWORD dwResume = 0; |
| 498 | 503 | ||
| 499 | // Check to see if a restart is pending for this bundle. | 504 | if (IsBundleRebootPending(pRegistration)) |
| 500 | hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey); | ||
| 501 | ExitOnFailure(hr, "Failed to format pending restart registry key to read."); | ||
| 502 | |||
| 503 | hr = RegOpen(pRegistration->hkRoot, sczRebootRequiredKey, KEY_QUERY_VALUE, &hkRebootRequired); | ||
| 504 | if (SUCCEEDED(hr)) | ||
| 505 | { | 505 | { |
| 506 | *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING; | 506 | *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING; |
| 507 | ExitFunction1(hr = S_OK); | 507 | ExitFunction1(hr = S_OK); |
| @@ -554,8 +554,6 @@ extern "C" HRESULT RegistrationDetectResumeType( | |||
| 554 | 554 | ||
| 555 | LExit: | 555 | LExit: |
| 556 | ReleaseRegKey(hkRegistration); | 556 | ReleaseRegKey(hkRegistration); |
| 557 | ReleaseRegKey(hkRebootRequired); | ||
| 558 | ReleaseStr(sczRebootRequiredKey); | ||
| 559 | 557 | ||
| 560 | return hr; | 558 | return hr; |
| 561 | } | 559 | } |
| @@ -1591,3 +1589,46 @@ LExit: | |||
| 1591 | 1589 | ||
| 1592 | return hr; | 1590 | return hr; |
| 1593 | } | 1591 | } |
| 1592 | |||
| 1593 | static BOOL IsWuRebootPending() | ||
| 1594 | { | ||
| 1595 | HRESULT hr = S_OK; | ||
| 1596 | BOOL fRebootPending = FALSE; | ||
| 1597 | |||
| 1598 | // Do a best effort to ask WU if a reboot is required. If anything goes | ||
| 1599 | // wrong then let's pretend a reboot is not required. | ||
| 1600 | hr = ::CoInitialize(NULL); | ||
| 1601 | if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr) | ||
| 1602 | { | ||
| 1603 | hr = WuaRestartRequired(&fRebootPending); | ||
| 1604 | if (FAILED(hr)) | ||
| 1605 | { | ||
| 1606 | fRebootPending = FALSE; | ||
| 1607 | } | ||
| 1608 | |||
| 1609 | ::CoUninitialize(); | ||
| 1610 | } | ||
| 1611 | |||
| 1612 | return fRebootPending; | ||
| 1613 | } | ||
| 1614 | |||
| 1615 | static BOOL IsBundleRebootPending(BURN_REGISTRATION* pRegistration) | ||
| 1616 | { | ||
| 1617 | HRESULT hr = S_OK; | ||
| 1618 | LPWSTR sczRebootRequiredKey = NULL; | ||
| 1619 | HKEY hkRebootRequired = NULL; | ||
| 1620 | BOOL fBundleRebootPending = FALSE; | ||
| 1621 | |||
| 1622 | // Check to see if a restart is pending for this bundle. | ||
| 1623 | hr = StrAllocFormatted(&sczRebootRequiredKey, REGISTRY_REBOOT_PENDING_FORMAT, pRegistration->sczRegistrationKey); | ||
| 1624 | ExitOnFailure(hr, "Failed to format pending restart registry key to read."); | ||
| 1625 | |||
| 1626 | hr = RegOpen(pRegistration->hkRoot, sczRebootRequiredKey, KEY_QUERY_VALUE, &hkRebootRequired); | ||
| 1627 | fBundleRebootPending = SUCCEEDED(hr); | ||
| 1628 | |||
| 1629 | LExit: | ||
| 1630 | ReleaseStr(sczRebootRequiredKey); | ||
| 1631 | ReleaseRegKey(hkRebootRequired); | ||
| 1632 | |||
| 1633 | return fBundleRebootPending; | ||
| 1634 | } | ||
diff --git a/src/engine/variable.cpp b/src/engine/variable.cpp index fed23151..d0c67504 100644 --- a/src/engine/variable.cpp +++ b/src/engine/variable.cpp | |||
| @@ -133,10 +133,6 @@ static HRESULT InitializeVariablePrivileged( | |||
| 133 | __in DWORD_PTR dwpData, | 133 | __in DWORD_PTR dwpData, |
| 134 | __inout BURN_VARIANT* pValue | 134 | __inout BURN_VARIANT* pValue |
| 135 | ); | 135 | ); |
| 136 | static HRESULT InitializeVariableRebootPending( | ||
| 137 | __in DWORD_PTR dwpData, | ||
| 138 | __inout BURN_VARIANT* pValue | ||
| 139 | ); | ||
| 140 | static HRESULT InitializeSystemLanguageID( | 136 | static HRESULT InitializeSystemLanguageID( |
| 141 | __in DWORD_PTR dwpData, | 137 | __in DWORD_PTR dwpData, |
| 142 | __inout BURN_VARIANT* pValue | 138 | __inout BURN_VARIANT* pValue |
| @@ -247,7 +243,6 @@ extern "C" HRESULT VariableInitialize( | |||
| 247 | #endif | 243 | #endif |
| 248 | {L"ProgramFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES}, | 244 | {L"ProgramFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES}, |
| 249 | {L"ProgramMenuFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAMS}, | 245 | {L"ProgramMenuFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAMS}, |
| 250 | {L"RebootPending", InitializeVariableRebootPending, 0}, | ||
| 251 | {L"SendToFolder", InitializeVariableCsidlFolder, CSIDL_SENDTO}, | 246 | {L"SendToFolder", InitializeVariableCsidlFolder, CSIDL_SENDTO}, |
| 252 | {L"ServicePackLevel", InitializeVariableVersionNT, OS_INFO_VARIABLE_ServicePackLevel}, | 247 | {L"ServicePackLevel", InitializeVariableVersionNT, OS_INFO_VARIABLE_ServicePackLevel}, |
| 253 | {L"StartMenuFolder", InitializeVariableCsidlFolder, CSIDL_STARTMENU}, | 248 | {L"StartMenuFolder", InitializeVariableCsidlFolder, CSIDL_STARTMENU}, |
| @@ -2031,44 +2026,6 @@ LExit: | |||
| 2031 | return hr; | 2026 | return hr; |
| 2032 | } | 2027 | } |
| 2033 | 2028 | ||
| 2034 | static HRESULT InitializeVariableRebootPending( | ||
| 2035 | __in DWORD_PTR dwpData, | ||
| 2036 | __inout BURN_VARIANT* pValue | ||
| 2037 | ) | ||
| 2038 | { | ||
| 2039 | UNREFERENCED_PARAMETER(dwpData); | ||
| 2040 | |||
| 2041 | HRESULT hr = S_OK; | ||
| 2042 | BOOL fRebootPending = FALSE; | ||
| 2043 | BOOL fComInitialized = FALSE; | ||
| 2044 | |||
| 2045 | // Do a best effort to ask WU if a reboot is required. If anything goes | ||
| 2046 | // wrong then let's pretend a reboot is not required. | ||
| 2047 | hr = ::CoInitialize(NULL); | ||
| 2048 | if (SUCCEEDED(hr) || RPC_E_CHANGED_MODE == hr) | ||
| 2049 | { | ||
| 2050 | fComInitialized = TRUE; | ||
| 2051 | |||
| 2052 | hr = WuaRestartRequired(&fRebootPending); | ||
| 2053 | if (FAILED(hr)) | ||
| 2054 | { | ||
| 2055 | fRebootPending = FALSE; | ||
| 2056 | hr = S_OK; | ||
| 2057 | } | ||
| 2058 | } | ||
| 2059 | |||
| 2060 | hr = BVariantSetNumeric(pValue, fRebootPending); | ||
| 2061 | ExitOnFailure(hr, "Failed to set reboot pending variant value."); | ||
| 2062 | |||
| 2063 | LExit: | ||
| 2064 | if (fComInitialized) | ||
| 2065 | { | ||
| 2066 | ::CoUninitialize(); | ||
| 2067 | } | ||
| 2068 | |||
| 2069 | return hr; | ||
| 2070 | } | ||
| 2071 | |||
| 2072 | static HRESULT InitializeSystemLanguageID( | 2029 | static HRESULT InitializeSystemLanguageID( |
| 2073 | __in DWORD_PTR dwpData, | 2030 | __in DWORD_PTR dwpData, |
| 2074 | __inout BURN_VARIANT* pValue | 2031 | __inout BURN_VARIANT* pValue |
diff --git a/src/test/BurnUnitTest/RegistrationTest.cpp b/src/test/BurnUnitTest/RegistrationTest.cpp index 1687385b..883a1258 100644 --- a/src/test/BurnUnitTest/RegistrationTest.cpp +++ b/src/test/BurnUnitTest/RegistrationTest.cpp | |||
| @@ -73,6 +73,7 @@ namespace Bootstrapper | |||
| 73 | BURN_LOGGING logging = { }; | 73 | BURN_LOGGING logging = { }; |
| 74 | BURN_PACKAGES packages = { }; | 74 | BURN_PACKAGES packages = { }; |
| 75 | String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); | 75 | String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); |
| 76 | |||
| 76 | try | 77 | try |
| 77 | { | 78 | { |
| 78 | // set mock API's | 79 | // set mock API's |
| @@ -261,6 +262,115 @@ namespace Bootstrapper | |||
| 261 | } | 262 | } |
| 262 | 263 | ||
| 263 | [Fact] | 264 | [Fact] |
| 265 | void RegisterVariablesTest() | ||
| 266 | { | ||
| 267 | HRESULT hr = S_OK; | ||
| 268 | IXMLDOMElement* pixeBundle = NULL; | ||
| 269 | LPWSTR sczCurrentProcess = NULL; | ||
| 270 | BURN_VARIABLES variables = { }; | ||
| 271 | BURN_USER_EXPERIENCE userExperience = { }; | ||
| 272 | BOOTSTRAPPER_COMMAND command = { }; | ||
| 273 | BURN_REGISTRATION registration = { }; | ||
| 274 | BURN_LOGGING logging = { }; | ||
| 275 | BURN_PACKAGES packages = { }; | ||
| 276 | String^ cacheDirectory = Path::Combine(Path::Combine(Environment::GetFolderPath(Environment::SpecialFolder::LocalApplicationData), gcnew String(L"Package Cache")), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}")); | ||
| 277 | try | ||
| 278 | { | ||
| 279 | // set mock API's | ||
| 280 | RegFunctionOverride(RegistrationTest_RegCreateKeyExW, RegistrationTest_RegOpenKeyExW, RegistrationTest_RegDeleteKeyExW, NULL, NULL, NULL, NULL, NULL, NULL); | ||
| 281 | |||
| 282 | Registry::CurrentUser->CreateSubKey(gcnew String(HKCU_PATH)); | ||
| 283 | |||
| 284 | logging.sczPath = L"BurnUnitTest.txt"; | ||
| 285 | |||
| 286 | LPCWSTR wzDocument = | ||
| 287 | L"<Bundle>" | ||
| 288 | L" <UX>" | ||
| 289 | L" <Payload Id='ux.dll' FilePath='ux.dll' Packaging='embedded' SourcePath='ux.dll' Hash='000000000000' />" | ||
| 290 | L" </UX>" | ||
| 291 | 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'>" | ||
| 292 | L" <Arp Register='yes' Publisher='WiX Toolset' DisplayName='Product1' DisplayVersion='1.0.0.0' />" | ||
| 293 | L" </Registration>" | ||
| 294 | L"</Bundle>"; | ||
| 295 | |||
| 296 | // load XML document | ||
| 297 | LoadBundleXmlHelper(wzDocument, &pixeBundle); | ||
| 298 | |||
| 299 | hr = VariableInitialize(&variables); | ||
| 300 | TestThrowOnFailure(hr, L"Failed to initialize variables."); | ||
| 301 | |||
| 302 | hr = UserExperienceParseFromXml(&userExperience, pixeBundle); | ||
| 303 | TestThrowOnFailure(hr, L"Failed to parse UX from XML."); | ||
| 304 | |||
| 305 | hr = RegistrationParseFromXml(®istration, pixeBundle); | ||
| 306 | TestThrowOnFailure(hr, L"Failed to parse registration from XML."); | ||
| 307 | |||
| 308 | hr = PlanSetResumeCommand(®istration, BOOTSTRAPPER_ACTION_INSTALL, &command, &logging); | ||
| 309 | TestThrowOnFailure(hr, L"Failed to set registration resume command."); | ||
| 310 | |||
| 311 | hr = PathForCurrentProcess(&sczCurrentProcess, NULL); | ||
| 312 | TestThrowOnFailure(hr, L"Failed to get current process path."); | ||
| 313 | |||
| 314 | // | ||
| 315 | // install | ||
| 316 | // | ||
| 317 | |||
| 318 | // write registration | ||
| 319 | hr = RegistrationSessionBegin(sczCurrentProcess, ®istration, &variables, &userExperience, BURN_REGISTRATION_ACTION_OPERATIONS_WRITE_REGISTRATION, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER, 0); | ||
| 320 | TestThrowOnFailure(hr, L"Failed to register bundle."); | ||
| 321 | |||
| 322 | // verify that registration was created | ||
| 323 | Assert::Equal(Int32(BURN_RESUME_MODE_ACTIVE), (Int32)Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); | ||
| 324 | 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)); | ||
| 325 | |||
| 326 | // complete registration | ||
| 327 | hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_ARP, BOOTSTRAPPER_APPLY_RESTART_REQUIRED, BURN_DEPENDENCY_REGISTRATION_ACTION_REGISTER); | ||
| 328 | TestThrowOnFailure(hr, L"Failed to unregister bundle."); | ||
| 329 | |||
| 330 | // verify that registration variables were updated | ||
| 331 | registration.fInstalled = TRUE; | ||
| 332 | |||
| 333 | hr = RegistrationSetVariables(®istration, &variables); | ||
| 334 | TestThrowOnFailure(hr, L"Failed to set registration variables."); | ||
| 335 | |||
| 336 | Assert::Equal(1ll, VariableGetNumericHelper(&variables, BURN_BUNDLE_INSTALLED)); | ||
| 337 | Assert::Equal(1ll, VariableGetNumericHelper(&variables, BURN_REBOOT_PENDING)); | ||
| 338 | Assert::Equal<String^>(gcnew String(L"foo"), VariableGetStringHelper(&variables, BURN_BUNDLE_TAG)); | ||
| 339 | Assert::Equal<String^>(gcnew String(L"bar"), VariableGetStringHelper(&variables, BURN_BUNDLE_PROVIDER_KEY)); | ||
| 340 | Assert::Equal<String^>(gcnew String(L"1.0.0.0"), VariableGetVersionHelper(&variables, BURN_BUNDLE_VERSION)); | ||
| 341 | |||
| 342 | // | ||
| 343 | // uninstall | ||
| 344 | // | ||
| 345 | |||
| 346 | // delete registration | ||
| 347 | hr = RegistrationSessionEnd(®istration, &packages, BURN_RESUME_MODE_NONE, BOOTSTRAPPER_APPLY_RESTART_NONE, BURN_DEPENDENCY_REGISTRATION_ACTION_UNREGISTER); | ||
| 348 | TestThrowOnFailure(hr, L"Failed to unregister bundle."); | ||
| 349 | |||
| 350 | // verify that registration was removed | ||
| 351 | Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Resume"), nullptr)); | ||
| 352 | Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_UNINSTALL_KEY), gcnew String(L"Installed"), nullptr)); | ||
| 353 | Assert::Equal((Object^)nullptr, Registry::GetValue(gcnew String(TEST_RUN_KEY), gcnew String(L"{D54F896D-1952-43e6-9C67-B5652240618C}"), nullptr)); | ||
| 354 | } | ||
| 355 | finally | ||
| 356 | { | ||
| 357 | ReleaseStr(sczCurrentProcess); | ||
| 358 | ReleaseObject(pixeBundle); | ||
| 359 | UserExperienceUninitialize(&userExperience); | ||
| 360 | RegistrationUninitialize(®istration); | ||
| 361 | VariablesUninitialize(&variables); | ||
| 362 | |||
| 363 | Registry::CurrentUser->DeleteSubKeyTree(gcnew String(ROOT_PATH)); | ||
| 364 | if (Directory::Exists(cacheDirectory)) | ||
| 365 | { | ||
| 366 | Directory::Delete(cacheDirectory, true); | ||
| 367 | } | ||
| 368 | |||
| 369 | RegFunctionOverride(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); | ||
| 370 | } | ||
| 371 | } | ||
| 372 | |||
| 373 | [Fact] | ||
| 264 | void RegisterArpFullTest() | 374 | void RegisterArpFullTest() |
| 265 | { | 375 | { |
| 266 | HRESULT hr = S_OK; | 376 | HRESULT hr = S_OK; |
