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(®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]
374
void RegisterArpFullTest()
375
{