Use elevated engine to make the restart request when it is available.
Fixes 6145
Sean Hall committed
Sep 2, 2022 at 16:12 UTC
7728e34e48a4fdb710ecc92dd8dca833bff3993f
8 files changed
+240
-133
src/burn/engine/core.cpp
+42
@@ -2028,6 +2028,48 @@ extern "C" HRESULT DAPI CoreWaitForProcCompletion(
2028
return vpfnProcWaitForCompletion(hProcess, dwTimeout, pdwReturnCode);
2029
}
2030
2031
+extern "C" HRESULT DAPI CoreCloseElevatedLoggingThread(
2032
+ __in BURN_ENGINE_STATE* pEngineState
2033
+ )
2034
+{
2035
+ HRESULT hr = S_OK;
2036
+
2037
+ if (INVALID_HANDLE_VALUE == pEngineState->elevatedLoggingContext.hThread)
2038
+ {
2039
+ ExitFunction();
2040
+ }
2041
+
2042
+ if (!::SetEvent(pEngineState->elevatedLoggingContext.hFinishedEvent))
2043
+ {
2044
+ ExitWithLastError(hr, "Failed to set log finished event.");
2045
+ }
2046
+
2047
+ hr = AppWaitForSingleObject(pEngineState->elevatedLoggingContext.hThread, 5 * 60 * 1000); // TODO: is 5 minutes good?
2048
+ ExitOnFailure(hr, "Failed to wait for elevated logging thread.");
2049
+
2050
+LExit:
2051
+ return hr;
2052
+}
2053
+
2054
+extern "C" HRESULT DAPI CoreWaitForUnelevatedLoggingThread(
2055
+ __in HANDLE hUnelevatedLoggingThread
2056
+ )
2057
+{
2058
+ HRESULT hr = S_OK;
2059
+
2060
+ if (INVALID_HANDLE_VALUE == hUnelevatedLoggingThread)
2061
+ {
2062
+ ExitFunction();
2063
+ }
2064
+
2065
+ // Give the thread 15 seconds to exit.
2066
+ hr = AppWaitForSingleObject(hUnelevatedLoggingThread, 15 * 1000);
2067
+ ExitOnFailure(hr, "Failed to wait for unelevated logging thread.");
2068
+
2069
+LExit:
2070
+ return hr;
2071
+}
2072
+
2073
// internal helper functions
2074
2075
static HRESULT AppendEscapedArgumentToCommandLine(
src/burn/engine/core.h
+17
@@ -123,6 +123,16 @@ typedef struct _BURN_ENGINE_COMMAND
123
LPWSTR sczLogFile;
124
} BURN_ENGINE_COMMAND;
125
126
+typedef struct _BURN_REDIRECTED_LOGGING_CONTEXT
127
+{
128
+ CRITICAL_SECTION csBuffer;
129
+ LPSTR sczBuffer;
130
+ HANDLE hPipe;
131
+ HANDLE hLogEvent;
132
+ HANDLE hFinishedEvent;
133
+ HANDLE hThread;
134
+} BURN_REDIRECTED_LOGGING_CONTEXT;
135
+
136
typedef struct _BURN_ENGINE_STATE
137
{
138
// UX flow control
@@ -164,6 +174,7 @@ typedef struct _BURN_ENGINE_STATE
174
175
BURN_PLAN plan;
176
177
+ BURN_REDIRECTED_LOGGING_CONTEXT elevatedLoggingContext;
178
HANDLE hUnelevatedLoggingThread;
179
180
LPWSTR sczBundleEngineWorkingPath;
@@ -336,6 +347,12 @@ HRESULT DAPI CoreWaitForProcCompletion(
347
__in DWORD dwTimeout,
348
__out_opt DWORD* pdwReturnCode
349
);
350
+HRESULT DAPI CoreCloseElevatedLoggingThread(
351
+ __in BURN_ENGINE_STATE* pEngineState
352
+ );
353
+HRESULT DAPI CoreWaitForUnelevatedLoggingThread(
354
+ __in HANDLE hUnelevatedLoggingThread
355
+ );
356
357
#if defined(__cplusplus)
358
}
src/burn/engine/engine.cpp
+52
-100
@@ -3,15 +3,6 @@
3
#include "precomp.h"
4
5
6
-typedef struct _REDIRECTED_LOGGING_CONTEXT
7
-{
8
- CRITICAL_SECTION csBuffer;
9
- LPSTR sczBuffer;
10
- HANDLE hPipe;
11
- HANDLE hLogEvent;
12
- HANDLE hFinishedEvent;
13
-} REDIRECTED_LOGGING_CONTEXT;
14
-
6
// constants
7
8
const DWORD RESTART_RETRIES = 10;
@@ -65,13 +56,6 @@ static HRESULT LogStringOverPipe(
56
static DWORD WINAPI ElevatedLoggingThreadProc(
57
__in LPVOID lpThreadParameter
58
);
68
-static HRESULT WaitForElevatedLoggingThread(
69
- __in REDIRECTED_LOGGING_CONTEXT* pContext,
70
- __in HANDLE hLoggingThread
71
- );
72
-static HRESULT WaitForUnelevatedLoggingThread(
73
- __in HANDLE hUnelevatedLoggingThread
74
- );
59
static HRESULT Restart(
60
__in BURN_ENGINE_STATE* pEngineState
61
);
@@ -308,10 +292,6 @@ LExit:
292
{
293
LogId(REPORT_STANDARD, MSG_EXITING_RUN_ONCE, FAILED(hr) ? (int)hr : *pdwExitCode);
294
}
311
- else if (fRunElevated)
312
- {
313
- LogId(REPORT_STANDARD, MSG_EXITING_ELEVATED, FAILED(hr) ? (int)hr : *pdwExitCode);
314
- }
295
296
if (fLogInitialized)
297
{
@@ -320,7 +300,12 @@ LExit:
300
LogFlush();
301
}
302
323
- if (engineState.fRestart)
303
+ // end per-machine process if running
304
+ if (!fRunElevated && INVALID_HANDLE_VALUE != engineState.companionConnection.hPipe)
305
+ {
306
+ PipeTerminateChildProcess(&engineState.companionConnection, *pdwExitCode, engineState.fRestart);
307
+ }
308
+ else if (engineState.fRestart)
309
{
310
LogId(REPORT_STANDARD, MSG_RESTARTING);
311
@@ -334,6 +319,30 @@ LExit:
319
// If the message window is still around, close it.
320
UiCloseMessageWindow(&engineState);
321
322
+ // If the logging thread is still around, close it.
323
+ if (!fRunElevated)
324
+ {
325
+ CoreWaitForUnelevatedLoggingThread(engineState.hUnelevatedLoggingThread);
326
+ }
327
+ else if (fRunElevated)
328
+ {
329
+ CoreCloseElevatedLoggingThread(&engineState);
330
+
331
+ // We're done talking to the child so always reset logging now.
332
+ LogRedirect(NULL, NULL);
333
+
334
+ // If there was a log message left, try to log it locally.
335
+ if (engineState.elevatedLoggingContext.sczBuffer)
336
+ {
337
+ LogStringWorkRaw(engineState.elevatedLoggingContext.sczBuffer);
338
+
339
+ ReleaseStr(engineState.elevatedLoggingContext.sczBuffer);
340
+ }
341
+
342
+ // Log the exit code here to make sure it gets in the elevated log.
343
+ LogId(REPORT_STANDARD, MSG_EXITING_ELEVATED, FAILED(hr) ? (int)hr : *pdwExitCode);
344
+ }
345
+
346
UninitializeEngineState(&engineState);
347
348
if (fXmlInitialized)
@@ -387,7 +396,12 @@ static HRESULT InitializeEngineState(
396
HANDLE hSectionFile = hEngineFile;
397
HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE;
398
399
+ pEngineState->hUnelevatedLoggingThread = INVALID_HANDLE_VALUE;
400
+ pEngineState->elevatedLoggingContext.hPipe = INVALID_HANDLE_VALUE;
401
+ pEngineState->elevatedLoggingContext.hThread = INVALID_HANDLE_VALUE;
402
+
403
::InitializeCriticalSection(&pEngineState->csRestartState);
404
+ ::InitializeCriticalSection(&pEngineState->elevatedLoggingContext.csBuffer);
405
406
pEngineState->internalCommand.automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED;
407
::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive);
@@ -423,6 +437,12 @@ static void UninitializeEngineState(
437
ReleaseMem(pEngineState->internalCommand.rgSecretArgs);
438
ReleaseMem(pEngineState->internalCommand.rgUnknownArgs);
439
440
+ ReleaseFileHandle(pEngineState->hUnelevatedLoggingThread);
441
+ ReleaseFileHandle(pEngineState->elevatedLoggingContext.hThread);
442
+ ::DeleteCriticalSection(&pEngineState->elevatedLoggingContext.csBuffer);
443
+ ReleaseHandle(pEngineState->elevatedLoggingContext.hLogEvent);
444
+ ReleaseHandle(pEngineState->elevatedLoggingContext.hFinishedEvent);
445
+
446
PipeConnectionUninitialize(&pEngineState->embeddedConnection);
447
PipeConnectionUninitialize(&pEngineState->companionConnection);
448
ReleaseStr(pEngineState->sczBundleEngineWorkingPath)
@@ -643,14 +663,6 @@ LExit:
663
664
VariablesDump(&pEngineState->variables);
665
646
- // end per-machine process if running
647
- if (INVALID_HANDLE_VALUE != pEngineState->companionConnection.hPipe)
648
- {
649
- PipeTerminateChildProcess(&pEngineState->companionConnection, pEngineState->userExperience.dwExitCode, FALSE);
650
-
651
- WaitForUnelevatedLoggingThread(pEngineState->hUnelevatedLoggingThread);
652
- }
653
-
666
// If the splash screen is still around, close it.
667
if (::IsWindow(pEngineState->command.hwndSplashScreen))
668
{
@@ -671,9 +683,7 @@ static HRESULT RunElevated(
683
{
684
HRESULT hr = S_OK;
685
HANDLE hLock = NULL;
674
- HANDLE hLoggingThread = NULL;
675
- REDIRECTED_LOGGING_CONTEXT loggingContext = { };
676
- BOOL fDeleteLoggingCs = FALSE;
686
+ BURN_REDIRECTED_LOGGING_CONTEXT* pLoggingContext = &pEngineState->elevatedLoggingContext;
687
688
// Initialize logging.
689
hr = LoggingOpen(&pEngineState->log, &pEngineState->internalCommand, &pEngineState->command, &pEngineState->variables, pEngineState->registration.sczDisplayName);
@@ -685,21 +695,18 @@ static HRESULT RunElevated(
695
696
// Set up the context for the logging thread then
697
// override logging to write over the pipe.
688
- ::InitializeCriticalSection(&loggingContext.csBuffer);
689
- fDeleteLoggingCs = TRUE;
690
-
691
- loggingContext.hLogEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
692
- ExitOnNullWithLastError(loggingContext.hLogEvent, hr, "Failed to create log event for logging thread.");
698
+ pLoggingContext->hLogEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
699
+ ExitOnNullWithLastError(pLoggingContext->hLogEvent, hr, "Failed to create log event for logging thread.");
700
694
- loggingContext.hFinishedEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
695
- ExitOnNullWithLastError(loggingContext.hFinishedEvent, hr, "Failed to create finished event for logging thread.");
701
+ pLoggingContext->hFinishedEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
702
+ ExitOnNullWithLastError(pLoggingContext->hFinishedEvent, hr, "Failed to create finished event for logging thread.");
703
697
- loggingContext.hPipe = pEngineState->companionConnection.hLoggingPipe;
704
+ pLoggingContext->hPipe = pEngineState->companionConnection.hLoggingPipe;
705
699
- hLoggingThread = ::CreateThread(NULL, 0, ElevatedLoggingThreadProc, &loggingContext, 0, NULL);
700
- ExitOnNullWithLastError(hLoggingThread, hr, "Failed to create elevated logging thread.");
706
+ pLoggingContext->hThread = ::CreateThread(NULL, 0, ElevatedLoggingThreadProc, pLoggingContext, 0, NULL);
707
+ ExitOnNullWithLastError(pLoggingContext->hThread, hr, "Failed to create elevated logging thread.");
708
702
- LogRedirect(RedirectLoggingOverPipe, &loggingContext);
709
+ LogRedirect(RedirectLoggingOverPipe, pLoggingContext);
710
711
if (!pEngineState->internalCommand.fInitiallyElevated)
712
{
@@ -716,29 +723,7 @@ static HRESULT RunElevated(
723
hr = ElevationChildPumpMessages(pEngineState->companionConnection.hPipe, pEngineState->companionConnection.hCachePipe, &pEngineState->approvedExes, &pEngineState->cache, &pEngineState->containers, &pEngineState->packages, &pEngineState->payloads, &pEngineState->variables, &pEngineState->registration, &pEngineState->userExperience, &hLock, &pEngineState->userExperience.dwExitCode, &pEngineState->fRestart, &pEngineState->plan.fApplying);
724
ExitOnFailure(hr, "Failed to pump messages from parent process.");
725
719
- WaitForElevatedLoggingThread(&loggingContext, hLoggingThread);
720
-
726
LExit:
722
- ReleaseHandle(hLoggingThread);
723
-
724
- LogRedirect(NULL, NULL); // we're done talking to the child so always reset logging now.
725
-
726
- if (fDeleteLoggingCs)
727
- {
728
- ::DeleteCriticalSection(&loggingContext.csBuffer);
729
- }
730
-
731
- ReleaseHandle(loggingContext.hLogEvent);
732
- ReleaseHandle(loggingContext.hFinishedEvent);
733
-
734
- // If there was a log message left, try to log it locally.
735
- if (loggingContext.sczBuffer)
736
- {
737
- LogStringWorkRaw(loggingContext.sczBuffer);
738
-
739
- ReleaseStr(loggingContext.sczBuffer);
740
- }
741
-
727
if (hLock)
728
{
729
::ReleaseMutex(hLock);
@@ -936,7 +921,7 @@ static HRESULT DAPI RedirectLoggingOverPipe(
921
)
922
{
923
HRESULT hr = S_OK;
939
- REDIRECTED_LOGGING_CONTEXT* pContext = static_cast<REDIRECTED_LOGGING_CONTEXT*>(pvContext);
924
+ BURN_REDIRECTED_LOGGING_CONTEXT* pContext = static_cast<BURN_REDIRECTED_LOGGING_CONTEXT*>(pvContext);
925
926
::EnterCriticalSection(&pContext->csBuffer);
927
@@ -986,7 +971,7 @@ static DWORD WINAPI ElevatedLoggingThreadProc(
971
{
972
HRESULT hr = S_OK;
973
DWORD dwLastError = ERROR_SUCCESS;
989
- REDIRECTED_LOGGING_CONTEXT* pContext = static_cast<REDIRECTED_LOGGING_CONTEXT*>(lpThreadParameter);
974
+ BURN_REDIRECTED_LOGGING_CONTEXT* pContext = static_cast<BURN_REDIRECTED_LOGGING_CONTEXT*>(lpThreadParameter);
975
DWORD dwSignaledIndex = 0;
976
LPSTR sczBuffer = NULL;
977
BURN_PIPE_RESULT result = { };
@@ -1079,39 +1064,6 @@ LExit:
1064
return (DWORD)hr;
1065
}
1066
1082
-static HRESULT WaitForElevatedLoggingThread(
1083
- __in REDIRECTED_LOGGING_CONTEXT* pContext,
1084
- __in HANDLE hLoggingThread
1085
- )
1086
-{
1087
- HRESULT hr = S_OK;
1088
-
1089
- if (!::SetEvent(pContext->hFinishedEvent))
1090
- {
1091
- ExitWithLastError(hr, "Failed to set log finished event.");
1092
- }
1093
-
1094
- hr = AppWaitForSingleObject(hLoggingThread, 5 * 60 * 1000); // TODO: is 5 minutes good?
1095
- ExitOnFailure(hr, "Failed to wait for elevated logging thread.");
1096
-
1097
-LExit:
1098
- return hr;
1099
-}
1100
-
1101
-static HRESULT WaitForUnelevatedLoggingThread(
1102
- __in HANDLE hUnelevatedLoggingThread
1103
- )
1104
-{
1105
- HRESULT hr = S_OK;
1106
-
1107
- // Give the thread 15 seconds to exit.
1108
- hr = AppWaitForSingleObject(hUnelevatedLoggingThread, 15 * 1000);
1109
- ExitOnFailure(hr, "Failed to wait for unelevated logging thread.");
1110
-
1111
-LExit:
1112
- return hr;
1113
-}
1114
-
1067
static HRESULT Restart(
1068
__in BURN_ENGINE_STATE* pEngineState
1069
)
src/burn/engine/uithread.cpp
+10
@@ -222,6 +222,16 @@ static LRESULT CALLBACK WndProc(
222
::Sleep(250);
223
}
224
225
+ // If this is the per-machine process then close the logging pipe with the parent process.
226
+ if (pInfo->fElevatedEngine)
227
+ {
228
+ CoreCloseElevatedLoggingThread(pInfo->pEngineState);
229
+ }
230
+ else
231
+ {
232
+ CoreWaitForUnelevatedLoggingThread(pInfo->pEngineState->hUnelevatedLoggingThread);
233
+ }
234
+
235
LogStringWorkRaw("=======================================\r\n");
236
237
// Close the log to try to make sure everything is flushed to disk.
src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp
-33
@@ -89,39 +89,6 @@ public: // IBAFunctions
89
return hr;
90
}
91
92
- virtual STDMETHODIMP WndProc(
93
- __in HWND hWnd,
94
- __in UINT uMsg,
95
- __in WPARAM /*wParam*/,
96
- __in LPARAM lParam,
97
- __inout BOOL* pfProcessed,
98
- __inout LRESULT* plResult
99
- )
100
- {
101
- switch (uMsg)
102
- {
103
- case WM_QUERYENDSESSION:
104
- if (BOOTSTRAPPER_DISPLAY_FULL <= m_command.display)
105
- {
106
- DWORD dwEndSession = static_cast<DWORD>(lParam);
107
- if (ENDSESSION_CRITICAL & dwEndSession)
108
- {
109
- // Return false to get the WM_ENDSESSION message so that critical shutdowns can be delayed.
110
- *plResult = FALSE;
111
- *pfProcessed = TRUE;
112
- }
113
- }
114
- break;
115
- case WM_ENDSESSION:
116
- if (BOOTSTRAPPER_DISPLAY_FULL <= m_command.display)
117
- {
118
- ::MessageBoxW(hWnd, L"WM_ENDSESSION", L"BAFunctions WndProc", MB_OK);
119
- }
120
- break;
121
- }
122
- return S_OK;
123
- }
124
-
92
public: //IBootstrapperApplication
93
virtual STDMETHODIMP OnExecuteBegin(
94
__in DWORD /*cExecutingPackages*/,
src/test/burn/TestData/Manual/BundleA/BundleA.wixproj
+4
@@ -11,8 +11,12 @@
11
<ItemGroup>
12
<ProjectReference Include="..\BafThmUtilTesting\BafThmUtilTesting.vcxproj" Properties="Platform=x86" ReferenceOutputAssembly="false" />
13
<ProjectReference Include="..\PackageA\PackageA.wixproj" />
14
+ <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
15
</ItemGroup>
16
<ItemGroup>
17
<PackageReference Include="WixToolset.Bal.wixext" />
18
</ItemGroup>
19
+ <Target Name="CopyManualInstructions" AfterTargets="AfterBuild">
20
+ <Copy SourceFiles="ManualTests.txt" DestinationFiles="$(OutputPath)ManualTests.txt" />
21
+ </Target>
22
</Project>
\ No newline at end of file
src/test/burn/TestData/Manual/BundleA/BundleA.wxs
+5
@@ -11,7 +11,12 @@
11
<MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)">
12
<MsiProperty Name="FORCERESTARTCA" Value="[FORCERESTARTCA]" />
13
</MsiPackage>
14
+ <ExePackage Id="ExeA" Cache="remove" PerMachine="yes"
15
+ DetectCondition="" Permanent="yes" InstallArguments="/ec [EXEEXITCODE]">
16
+ <PayloadGroupRef Id="TestExePayloads" />
17
+ </ExePackage>
18
</PackageGroup>
19
+ <Variable Name="EXEEXITCODE" bal:Overridable="yes" Value="0" />
20
<Variable Name="FORCERESTARTCA" bal:Overridable="yes" />
21
</Fragment>
22
</Wix>
src/test/burn/TestData/Manual/BundleA/ManualTests.txt
new
+110
@@ -0,0 +1,110 @@
1
+VariousWixstdbaAndBAFunctionsAndThmutilFunctionality
2
+1. Run BundleA.exe unelevated.
3
+2. Click Test.
4
+3. Verify that the BafThmUtilTestingTheme window comes up and click Start.
5
+4. There are several pages. Follow the instructions on each page before clicking Next.
6
+5. Click ThemeClose to close the BafThmUtilTestingTheme window.
7
+6. Click the Options button.
8
+7. Click the Browse button.
9
+8. Type in "C:\Program Files (x86)\mytest".
10
+9. Click the Select Folder button.
11
+10. Verify the Install location textbox was populated with "C:\Program Files (x86)\mytest".
12
+11. Click OK.
13
+12. Verify the caption of the window is "~Manual - BundleA Setup" and the text is "~Manual - BundleA".
14
+13. Verify the Install button has the UAC shield and then click it (accept elevation).
15
+14. Click OK on the OnExecuteBegin message box.
16
+15. After it finishes, verify the window text is "Installation Successfully Completed".
17
+16. Click Close.
18
+17. Verify in the bundle log that the variable InstallFolder was set to "C:\Program Files (x86)\mytest".
19
+18. Run BundleA.exe again.
20
+19. Click Uninstall (accept elevation).
21
+20. Click OK on the OnExecuteBegin message box.
22
+21. After it finishes, verify the window text is "Uninstallation Successfully Completed".
23
+22. Click Close.
24
+
25
+CanBlockShutdownDuringApply
26
+1. Run BundleA.exe unelevated.
27
+2. Click Install (accept elevation).
28
+3. Verify the OnExecuteBegin message box comes up with the message "Shutdown requests should be denied right now." and leave it up.
29
+4. Use the Windows UI to try to restart the machine.
30
+5. On current versions of Windows, full screen UI will display that the bundle is blocking restart. Click Cancel to stop the restart.
31
+6. Click OK on the message box from step 3 to allow installation to continue.
32
+7. Verify that the installation was successful and then close the bundle.
33
+8. Look in the bundle log and verify that the restart request didn't cause any errors, and that it logged messages that look like:
34
+
35
+i400: Received system request to shut down the process: allowed: No, elevated: No, critical: No, logoff: No, close app: No
36
+i400: Received system request to shut down the process: allowed: No, elevated: Yes, critical: No, logoff: No, close app: No
37
+i401: Received result of system request to shut down the process: closing: No, elevated: No, critical: No, logoff: No, close app: No
38
+i401: Received result of system request to shut down the process: closing: No, elevated: Yes, critical: No, logoff: No, close app: No
39
+
40
+(9. Uninstall the bundle)
41
+
42
+CanLogRestartFailure
43
+
44
+ Note: this test requires special setup. Change the User Rights Assignment such that no one has the shutdown privilege. Logout and log back in after making changes to make them take effect.
45
+ (mmc.exe -> Group Policy Object Editor -> Local Computer Policy -> Computer Configuration -> Windows Settings -> Security Settings -> Local Policies -> User Rights Assignment -> Shut down the system)
46
+
47
+1. Run BundleA.exe elevated with the command line EXEEXITCODE=3010.
48
+2. Click Install.
49
+3. Click OK on the OnExecuteBegin message box.
50
+4. Click Restart.
51
+5. Look in the bundle log and verify that the restart request failure was logged with messages that look like:
52
+
53
+w005: Restarting computer...
54
+e000: Error 0x80070005: Failed to schedule restart.
55
+e023: The restart request failed, error: 0x80070005. The machine will need to be manually restarted.
56
+
57
+(5. Uninstall the bundle)
58
+
59
+CanGracefullyCutApplyShortInResponseToCriticalShutdown
60
+
61
+ Note: Requires different User Rights Assignment settings from CanLogRestartFailure - Administrators need to have the shutdown privilege.
62
+
63
+1. Run BundleA.exe FORCERESTARTCA=1.
64
+2. Click Install (accept elevation).
65
+3. Click OK on the OnExecuteBegin message box.
66
+4. Verify the machine automatically restarts with no chance to cancel it (because the MSI forces a restart).
67
+5. Login to the machine.
68
+6. Verify that the bundle automatically started running again.
69
+7. Click OK on the OnExecuteBegin message box.
70
+8. Verify that the installation was successful and then close the bundle.
71
+9. Look in the bundle log and verify that the restart request didn't cause any errors, and that it logged messages that look like:
72
+
73
+i301: Applying execute package: PackageA, action: Install, path: C:\ProgramData\Package Cache\{1C6F49C0-B4BA-438D-871D-2B5E91C0EED5}v1.0.0.0\PackageA.msi, arguments: ' FORCERESTARTCA="1" ARPSYSTEMCOMPONENT="1" MSIFASTINSTALL="7" BURNMSIINSTALL=1 REINSTALLMODE="muso" REBOOT=ReallySuppress'
74
+...
75
+i400: Received system request to shut down the process: allowed: No, elevated: No, critical: Yes, logoff: No, close app: No
76
+i400: Received system request to shut down the process: allowed: No, elevated: Yes, critical: Yes, logoff: No, close app: No
77
+i401: Received result of system request to shut down the process: closing: Yes, elevated: No, critical: Yes, logoff: No, close app: No
78
+i401: Received result of system request to shut down the process: closing: Yes, elevated: Yes, critical: Yes, logoff: No, close app: No
79
+...
80
+i319: Applied execute package: PackageA, result: 0x0, restart: Initiated
81
+...
82
+i420: Resuming automatic updates.
83
+i421: Resumed automatic updates.
84
+=======================================
85
+i001: Burn x86 v4.0.0.525, Windows v10.0 x64 (Build 19041: Service Pack 0), path: C:\ProgramData\Package Cache\{30D63455-CD23-4AC3-81CC-4695434F848A}\BundleA.exe
86
+
87
+(10. Uninstall the bundle)
88
+
89
+CanRestartFromUnelevatedPerMachineBundleWithoutShutdownPrivilege
90
+
91
+ Note: Requires different User Rights Assignment settings from CanLogRestartFailure - Only Administrators should have the shutdown privilege. Users should not have it.
92
+
93
+1. Run BundleA.exe unelevated with the command line EXEEXITCODE=3010.
94
+2. Click Install.
95
+3. Verify that the UAC prompt came up and accept elevation.
96
+4. Click OK on the OnExecuteBegin message box.
97
+5. Click Restart.
98
+6. The machine should restart.
99
+7. Login to the machine.
100
+8. Verify that the bundle did not automatically start running.
101
+9. Look in the bundle log and verify that the restart request didn't cause any errors, and that it logged messages that look like (the process id for w005 must match the elevated i400 and i401):
102
+
103
+[0DDC:0448]w005: Restarting computer...
104
+[1228:18CC]i400: Received system request to shut down the process: allowed: Yes, elevated: No, critical: No, logoff: No, close app: No
105
+[1228:18CC]i401: Received result of system request to shut down the process: closing: Yes, elevated: No, critical: No, logoff: No, close app: No
106
+[0DDC:0954]i400: Received system request to shut down the process: allowed: Yes, elevated: Yes, critical: No, logoff: No, close app: No
107
+[0DDC:0954]i401: Received result of system request to shut down the process: closing: Yes, elevated: Yes, critical: No, logoff: No, close app: No
108
+=======================================
109
+
110
+(10. Uninstall the bundle)