@joebigelow / wix-1 / commits / 65b90566

Include bundle reboot-pending in RebootPending variable.

Fixes https://github.com/wixtoolset/issues/issues/5332

Bob Arnson committed Mar 28, 2021 at 22:17 UTC 65b905667b8567cd9b40c220eb18bd729276e7a6
4 files changed +163 -54
src/engine/core.h
+1
@@ -46,6 +46,7 @@ const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder
46 const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag";
47 const LPCWSTR BURN_BUNDLE_UILEVEL = L"WixBundleUILevel";
48 const LPCWSTR BURN_BUNDLE_VERSION = L"WixBundleVersion";
49 +const LPCWSTR BURN_REBOOT_PENDING = L"RebootPending";
50
51 // The following constants must stay in sync with src\wix\Binder.cs
52 const LPCWSTR BURN_BUNDLE_NAME = L"WixBundleName";
src/engine/registration.cpp
+52 -11
@@ -92,6 +92,10 @@ static HRESULT UpdateBundleNameRegistration(
92 __in BURN_VARIABLES* pVariables,
93 __in HKEY hkRegistration
94 );
95 +static BOOL IsWuRebootPending();
96 +static BOOL IsBundleRebootPending(
97 + __in BURN_REGISTRATION* pRegistration
98 +);
99
100 // function definitions
101
@@ -443,7 +447,10 @@ extern "C" HRESULT RegistrationSetVariables(
447 ExitOnFailure(hr, "Failed to overwrite the bundle tag built-in variable.");
448
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.");
454
455 LExit:
456 ReleaseStr(sczBundleManufacturer);
@@ -491,17 +498,10 @@ extern "C" HRESULT RegistrationDetectResumeType(
498 )
499 {
500 HRESULT hr = S_OK;
494 - LPWSTR sczRebootRequiredKey = NULL;
495 - HKEY hkRebootRequired = NULL;
501 HKEY hkRegistration = NULL;
502 DWORD dwResume = 0;
503
499 - // Check to see if a restart is pending for this bundle.
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))
504 + if (IsBundleRebootPending(pRegistration))
505 {
506 *pResumeType = BOOTSTRAPPER_RESUME_TYPE_REBOOT_PENDING;
507 ExitFunction1(hr = S_OK);
@@ -554,8 +554,6 @@ extern "C" HRESULT RegistrationDetectResumeType(
554
555 LExit:
556 ReleaseRegKey(hkRegistration);
557 - ReleaseRegKey(hkRebootRequired);
558 - ReleaseStr(sczRebootRequiredKey);
557
558 return hr;
559 }
@@ -1591,3 +1589,46 @@ LExit:
1589
1590 return hr;
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 +}
src/engine/variable.cpp
-43
@@ -133,10 +133,6 @@ static HRESULT InitializeVariablePrivileged(
133 __in DWORD_PTR dwpData,
134 __inout BURN_VARIANT* pValue
135 );
136 -static HRESULT InitializeVariableRebootPending(
137 - __in DWORD_PTR dwpData,
138 - __inout BURN_VARIANT* pValue
139 - );
136 static HRESULT InitializeSystemLanguageID(
137 __in DWORD_PTR dwpData,
138 __inout BURN_VARIANT* pValue
@@ -247,7 +243,6 @@ extern "C" HRESULT VariableInitialize(
243 #endif
244 {L"ProgramFiles6432Folder", InitializeVariable6432Folder, CSIDL_PROGRAM_FILES},
245 {L"ProgramMenuFolder", InitializeVariableCsidlFolder, CSIDL_PROGRAMS},
250 - {L"RebootPending", InitializeVariableRebootPending, 0},
246 {L"SendToFolder", InitializeVariableCsidlFolder, CSIDL_SENDTO},
247 {L"ServicePackLevel", InitializeVariableVersionNT, OS_INFO_VARIABLE_ServicePackLevel},
248 {L"StartMenuFolder", InitializeVariableCsidlFolder, CSIDL_STARTMENU},
@@ -2031,44 +2026,6 @@ LExit:
2026 return hr;
2027 }
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 -
2029 static HRESULT InitializeSystemLanguageID(
2030 __in DWORD_PTR dwpData,
2031 __inout BURN_VARIANT* pValue
src/test/BurnUnitTest/RegistrationTest.cpp
+110
@@ -73,6 +73,7 @@ namespace Bootstrapper
73 BURN_LOGGING logging = { };
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}"));
76 +
77 try
78 {
79 // set mock API's
@@ -260,6 +261,115 @@ namespace Bootstrapper
261 }
262 }
263
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(&registration, pixeBundle);
306 + TestThrowOnFailure(hr, L"Failed to parse registration from XML.");
307 +
308 + hr = PlanSetResumeCommand(&registration, 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, &registration, &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(&registration, &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(&registration, &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(&registration, &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(&registration);
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]
374 void RegisterArpFullTest()
375 {