aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/burn/engine/core.cpp42
-rw-r--r--src/burn/engine/core.h17
-rw-r--r--src/burn/engine/engine.cpp152
-rw-r--r--src/burn/engine/uithread.cpp10
-rw-r--r--src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp33
-rw-r--r--src/test/burn/TestData/Manual/BundleA/BundleA.wixproj4
-rw-r--r--src/test/burn/TestData/Manual/BundleA/BundleA.wxs5
-rw-r--r--src/test/burn/TestData/Manual/BundleA/ManualTests.txt110
8 files changed, 240 insertions, 133 deletions
diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp
index 3c1ed117..c8dce17b 100644
--- a/src/burn/engine/core.cpp
+++ b/src/burn/engine/core.cpp
@@ -2028,6 +2028,48 @@ extern "C" HRESULT DAPI CoreWaitForProcCompletion(
2028 return vpfnProcWaitForCompletion(hProcess, dwTimeout, pdwReturnCode); 2028 return vpfnProcWaitForCompletion(hProcess, dwTimeout, pdwReturnCode);
2029} 2029}
2030 2030
2031extern "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
2050LExit:
2051 return hr;
2052}
2053
2054extern "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
2069LExit:
2070 return hr;
2071}
2072
2031// internal helper functions 2073// internal helper functions
2032 2074
2033static HRESULT AppendEscapedArgumentToCommandLine( 2075static HRESULT AppendEscapedArgumentToCommandLine(
diff --git a/src/burn/engine/core.h b/src/burn/engine/core.h
index 28b5ba5d..812b40b1 100644
--- a/src/burn/engine/core.h
+++ b/src/burn/engine/core.h
@@ -123,6 +123,16 @@ typedef struct _BURN_ENGINE_COMMAND
123 LPWSTR sczLogFile; 123 LPWSTR sczLogFile;
124} BURN_ENGINE_COMMAND; 124} BURN_ENGINE_COMMAND;
125 125
126typedef 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
126typedef struct _BURN_ENGINE_STATE 136typedef struct _BURN_ENGINE_STATE
127{ 137{
128 // UX flow control 138 // UX flow control
@@ -164,6 +174,7 @@ typedef struct _BURN_ENGINE_STATE
164 174
165 BURN_PLAN plan; 175 BURN_PLAN plan;
166 176
177 BURN_REDIRECTED_LOGGING_CONTEXT elevatedLoggingContext;
167 HANDLE hUnelevatedLoggingThread; 178 HANDLE hUnelevatedLoggingThread;
168 179
169 LPWSTR sczBundleEngineWorkingPath; 180 LPWSTR sczBundleEngineWorkingPath;
@@ -336,6 +347,12 @@ HRESULT DAPI CoreWaitForProcCompletion(
336 __in DWORD dwTimeout, 347 __in DWORD dwTimeout,
337 __out_opt DWORD* pdwReturnCode 348 __out_opt DWORD* pdwReturnCode
338 ); 349 );
350HRESULT DAPI CoreCloseElevatedLoggingThread(
351 __in BURN_ENGINE_STATE* pEngineState
352 );
353HRESULT DAPI CoreWaitForUnelevatedLoggingThread(
354 __in HANDLE hUnelevatedLoggingThread
355 );
339 356
340#if defined(__cplusplus) 357#if defined(__cplusplus)
341} 358}
diff --git a/src/burn/engine/engine.cpp b/src/burn/engine/engine.cpp
index 9c8b6c1d..89082a88 100644
--- a/src/burn/engine/engine.cpp
+++ b/src/burn/engine/engine.cpp
@@ -3,15 +3,6 @@
3#include "precomp.h" 3#include "precomp.h"
4 4
5 5
6typedef 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
15// constants 6// constants
16 7
17const DWORD RESTART_RETRIES = 10; 8const DWORD RESTART_RETRIES = 10;
@@ -65,13 +56,6 @@ static HRESULT LogStringOverPipe(
65static DWORD WINAPI ElevatedLoggingThreadProc( 56static DWORD WINAPI ElevatedLoggingThreadProc(
66 __in LPVOID lpThreadParameter 57 __in LPVOID lpThreadParameter
67 ); 58 );
68static HRESULT WaitForElevatedLoggingThread(
69 __in REDIRECTED_LOGGING_CONTEXT* pContext,
70 __in HANDLE hLoggingThread
71 );
72static HRESULT WaitForUnelevatedLoggingThread(
73 __in HANDLE hUnelevatedLoggingThread
74 );
75static HRESULT Restart( 59static HRESULT Restart(
76 __in BURN_ENGINE_STATE* pEngineState 60 __in BURN_ENGINE_STATE* pEngineState
77 ); 61 );
@@ -308,10 +292,6 @@ LExit:
308 { 292 {
309 LogId(REPORT_STANDARD, MSG_EXITING_RUN_ONCE, FAILED(hr) ? (int)hr : *pdwExitCode); 293 LogId(REPORT_STANDARD, MSG_EXITING_RUN_ONCE, FAILED(hr) ? (int)hr : *pdwExitCode);
310 } 294 }
311 else if (fRunElevated)
312 {
313 LogId(REPORT_STANDARD, MSG_EXITING_ELEVATED, FAILED(hr) ? (int)hr : *pdwExitCode);
314 }
315 295
316 if (fLogInitialized) 296 if (fLogInitialized)
317 { 297 {
@@ -320,7 +300,12 @@ LExit:
320 LogFlush(); 300 LogFlush();
321 } 301 }
322 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)
324 { 309 {
325 LogId(REPORT_STANDARD, MSG_RESTARTING); 310 LogId(REPORT_STANDARD, MSG_RESTARTING);
326 311
@@ -334,6 +319,30 @@ LExit:
334 // If the message window is still around, close it. 319 // If the message window is still around, close it.
335 UiCloseMessageWindow(&engineState); 320 UiCloseMessageWindow(&engineState);
336 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
337 UninitializeEngineState(&engineState); 346 UninitializeEngineState(&engineState);
338 347
339 if (fXmlInitialized) 348 if (fXmlInitialized)
@@ -387,7 +396,12 @@ static HRESULT InitializeEngineState(
387 HANDLE hSectionFile = hEngineFile; 396 HANDLE hSectionFile = hEngineFile;
388 HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE; 397 HANDLE hSourceEngineFile = INVALID_HANDLE_VALUE;
389 398
399 pEngineState->hUnelevatedLoggingThread = INVALID_HANDLE_VALUE;
400 pEngineState->elevatedLoggingContext.hPipe = INVALID_HANDLE_VALUE;
401 pEngineState->elevatedLoggingContext.hThread = INVALID_HANDLE_VALUE;
402
390 ::InitializeCriticalSection(&pEngineState->csRestartState); 403 ::InitializeCriticalSection(&pEngineState->csRestartState);
404 ::InitializeCriticalSection(&pEngineState->elevatedLoggingContext.csBuffer);
391 405
392 pEngineState->internalCommand.automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED; 406 pEngineState->internalCommand.automaticUpdates = BURN_AU_PAUSE_ACTION_IFELEVATED;
393 ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive); 407 ::InitializeCriticalSection(&pEngineState->userExperience.csEngineActive);
@@ -423,6 +437,12 @@ static void UninitializeEngineState(
423 ReleaseMem(pEngineState->internalCommand.rgSecretArgs); 437 ReleaseMem(pEngineState->internalCommand.rgSecretArgs);
424 ReleaseMem(pEngineState->internalCommand.rgUnknownArgs); 438 ReleaseMem(pEngineState->internalCommand.rgUnknownArgs);
425 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
426 PipeConnectionUninitialize(&pEngineState->embeddedConnection); 446 PipeConnectionUninitialize(&pEngineState->embeddedConnection);
427 PipeConnectionUninitialize(&pEngineState->companionConnection); 447 PipeConnectionUninitialize(&pEngineState->companionConnection);
428 ReleaseStr(pEngineState->sczBundleEngineWorkingPath) 448 ReleaseStr(pEngineState->sczBundleEngineWorkingPath)
@@ -643,14 +663,6 @@ LExit:
643 663
644 VariablesDump(&pEngineState->variables); 664 VariablesDump(&pEngineState->variables);
645 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
654 // If the splash screen is still around, close it. 666 // If the splash screen is still around, close it.
655 if (::IsWindow(pEngineState->command.hwndSplashScreen)) 667 if (::IsWindow(pEngineState->command.hwndSplashScreen))
656 { 668 {
@@ -671,9 +683,7 @@ static HRESULT RunElevated(
671{ 683{
672 HRESULT hr = S_OK; 684 HRESULT hr = S_OK;
673 HANDLE hLock = NULL; 685 HANDLE hLock = NULL;
674 HANDLE hLoggingThread = NULL; 686 BURN_REDIRECTED_LOGGING_CONTEXT* pLoggingContext = &pEngineState->elevatedLoggingContext;
675 REDIRECTED_LOGGING_CONTEXT loggingContext = { };
676 BOOL fDeleteLoggingCs = FALSE;
677 687
678 // Initialize logging. 688 // Initialize logging.
679 hr = LoggingOpen(&pEngineState->log, &pEngineState->internalCommand, &pEngineState->command, &pEngineState->variables, pEngineState->registration.sczDisplayName); 689 hr = LoggingOpen(&pEngineState->log, &pEngineState->internalCommand, &pEngineState->command, &pEngineState->variables, pEngineState->registration.sczDisplayName);
@@ -685,21 +695,18 @@ static HRESULT RunElevated(
685 695
686 // Set up the context for the logging thread then 696 // Set up the context for the logging thread then
687 // override logging to write over the pipe. 697 // override logging to write over the pipe.
688 ::InitializeCriticalSection(&loggingContext.csBuffer); 698 pLoggingContext->hLogEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
689 fDeleteLoggingCs = TRUE; 699 ExitOnNullWithLastError(pLoggingContext->hLogEvent, hr, "Failed to create log event for logging thread.");
690
691 loggingContext.hLogEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
692 ExitOnNullWithLastError(loggingContext.hLogEvent, hr, "Failed to create log event for logging thread.");
693 700
694 loggingContext.hFinishedEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL); 701 pLoggingContext->hFinishedEvent = ::CreateEventW(NULL, TRUE, FALSE, NULL);
695 ExitOnNullWithLastError(loggingContext.hFinishedEvent, hr, "Failed to create finished event for logging thread."); 702 ExitOnNullWithLastError(pLoggingContext->hFinishedEvent, hr, "Failed to create finished event for logging thread.");
696 703
697 loggingContext.hPipe = pEngineState->companionConnection.hLoggingPipe; 704 pLoggingContext->hPipe = pEngineState->companionConnection.hLoggingPipe;
698 705
699 hLoggingThread = ::CreateThread(NULL, 0, ElevatedLoggingThreadProc, &loggingContext, 0, NULL); 706 pLoggingContext->hThread = ::CreateThread(NULL, 0, ElevatedLoggingThreadProc, pLoggingContext, 0, NULL);
700 ExitOnNullWithLastError(hLoggingThread, hr, "Failed to create elevated logging thread."); 707 ExitOnNullWithLastError(pLoggingContext->hThread, hr, "Failed to create elevated logging thread.");
701 708
702 LogRedirect(RedirectLoggingOverPipe, &loggingContext); 709 LogRedirect(RedirectLoggingOverPipe, pLoggingContext);
703 710
704 if (!pEngineState->internalCommand.fInitiallyElevated) 711 if (!pEngineState->internalCommand.fInitiallyElevated)
705 { 712 {
@@ -716,29 +723,7 @@ static HRESULT RunElevated(
716 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); 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);
717 ExitOnFailure(hr, "Failed to pump messages from parent process."); 724 ExitOnFailure(hr, "Failed to pump messages from parent process.");
718 725
719 WaitForElevatedLoggingThread(&loggingContext, hLoggingThread);
720
721LExit: 726LExit:
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
742 if (hLock) 727 if (hLock)
743 { 728 {
744 ::ReleaseMutex(hLock); 729 ::ReleaseMutex(hLock);
@@ -936,7 +921,7 @@ static HRESULT DAPI RedirectLoggingOverPipe(
936 ) 921 )
937{ 922{
938 HRESULT hr = S_OK; 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);
940 925
941 ::EnterCriticalSection(&pContext->csBuffer); 926 ::EnterCriticalSection(&pContext->csBuffer);
942 927
@@ -986,7 +971,7 @@ static DWORD WINAPI ElevatedLoggingThreadProc(
986{ 971{
987 HRESULT hr = S_OK; 972 HRESULT hr = S_OK;
988 DWORD dwLastError = ERROR_SUCCESS; 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);
990 DWORD dwSignaledIndex = 0; 975 DWORD dwSignaledIndex = 0;
991 LPSTR sczBuffer = NULL; 976 LPSTR sczBuffer = NULL;
992 BURN_PIPE_RESULT result = { }; 977 BURN_PIPE_RESULT result = { };
@@ -1079,39 +1064,6 @@ LExit:
1079 return (DWORD)hr; 1064 return (DWORD)hr;
1080} 1065}
1081 1066
1082static 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
1097LExit:
1098 return hr;
1099}
1100
1101static 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
1111LExit:
1112 return hr;
1113}
1114
1115static HRESULT Restart( 1067static HRESULT Restart(
1116 __in BURN_ENGINE_STATE* pEngineState 1068 __in BURN_ENGINE_STATE* pEngineState
1117 ) 1069 )
diff --git a/src/burn/engine/uithread.cpp b/src/burn/engine/uithread.cpp
index 673111f8..8ddd51bd 100644
--- a/src/burn/engine/uithread.cpp
+++ b/src/burn/engine/uithread.cpp
@@ -222,6 +222,16 @@ static LRESULT CALLBACK WndProc(
222 ::Sleep(250); 222 ::Sleep(250);
223 } 223 }
224 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
225 LogStringWorkRaw("=======================================\r\n"); 235 LogStringWorkRaw("=======================================\r\n");
226 236
227 // Close the log to try to make sure everything is flushed to disk. 237 // Close the log to try to make sure everything is flushed to disk.
diff --git a/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp b/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp
index ce74534d..c619dbd6 100644
--- a/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp
+++ b/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp
@@ -89,39 +89,6 @@ public: // IBAFunctions
89 return hr; 89 return hr;
90 } 90 }
91 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
125public: //IBootstrapperApplication 92public: //IBootstrapperApplication
126 virtual STDMETHODIMP OnExecuteBegin( 93 virtual STDMETHODIMP OnExecuteBegin(
127 __in DWORD /*cExecutingPackages*/, 94 __in DWORD /*cExecutingPackages*/,
diff --git a/src/test/burn/TestData/Manual/BundleA/BundleA.wixproj b/src/test/burn/TestData/Manual/BundleA/BundleA.wixproj
index 907b85c9..225664b9 100644
--- a/src/test/burn/TestData/Manual/BundleA/BundleA.wixproj
+++ b/src/test/burn/TestData/Manual/BundleA/BundleA.wixproj
@@ -11,8 +11,12 @@
11 <ItemGroup> 11 <ItemGroup>
12 <ProjectReference Include="..\BafThmUtilTesting\BafThmUtilTesting.vcxproj" Properties="Platform=x86" ReferenceOutputAssembly="false" /> 12 <ProjectReference Include="..\BafThmUtilTesting\BafThmUtilTesting.vcxproj" Properties="Platform=x86" ReferenceOutputAssembly="false" />
13 <ProjectReference Include="..\PackageA\PackageA.wixproj" /> 13 <ProjectReference Include="..\PackageA\PackageA.wixproj" />
14 <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
14 </ItemGroup> 15 </ItemGroup>
15 <ItemGroup> 16 <ItemGroup>
16 <PackageReference Include="WixToolset.Bal.wixext" /> 17 <PackageReference Include="WixToolset.Bal.wixext" />
17 </ItemGroup> 18 </ItemGroup>
19 <Target Name="CopyManualInstructions" AfterTargets="AfterBuild">
20 <Copy SourceFiles="ManualTests.txt" DestinationFiles="$(OutputPath)ManualTests.txt" />
21 </Target>
18</Project> \ No newline at end of file 22</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/Manual/BundleA/BundleA.wxs b/src/test/burn/TestData/Manual/BundleA/BundleA.wxs
index 44abc645..20b68a91 100644
--- a/src/test/burn/TestData/Manual/BundleA/BundleA.wxs
+++ b/src/test/burn/TestData/Manual/BundleA/BundleA.wxs
@@ -11,7 +11,12 @@
11 <MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)"> 11 <MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)">
12 <MsiProperty Name="FORCERESTARTCA" Value="[FORCERESTARTCA]" /> 12 <MsiProperty Name="FORCERESTARTCA" Value="[FORCERESTARTCA]" />
13 </MsiPackage> 13 </MsiPackage>
14 <ExePackage Id="ExeA" Cache="remove" PerMachine="yes"
15 DetectCondition="" Permanent="yes" InstallArguments="/ec [EXEEXITCODE]">
16 <PayloadGroupRef Id="TestExePayloads" />
17 </ExePackage>
14 </PackageGroup> 18 </PackageGroup>
19 <Variable Name="EXEEXITCODE" bal:Overridable="yes" Value="0" />
15 <Variable Name="FORCERESTARTCA" bal:Overridable="yes" /> 20 <Variable Name="FORCERESTARTCA" bal:Overridable="yes" />
16 </Fragment> 21 </Fragment>
17</Wix> 22</Wix>
diff --git a/src/test/burn/TestData/Manual/BundleA/ManualTests.txt b/src/test/burn/TestData/Manual/BundleA/ManualTests.txt
new file mode 100644
index 00000000..d432f94a
--- /dev/null
+++ b/src/test/burn/TestData/Manual/BundleA/ManualTests.txt
@@ -0,0 +1,110 @@
1VariousWixstdbaAndBAFunctionsAndThmutilFunctionality
21. Run BundleA.exe unelevated.
32. Click Test.
43. Verify that the BafThmUtilTestingTheme window comes up and click Start.
54. There are several pages. Follow the instructions on each page before clicking Next.
65. Click ThemeClose to close the BafThmUtilTestingTheme window.
76. Click the Options button.
87. Click the Browse button.
98. Type in "C:\Program Files (x86)\mytest".
109. Click the Select Folder button.
1110. Verify the Install location textbox was populated with "C:\Program Files (x86)\mytest".
1211. Click OK.
1312. Verify the caption of the window is "~Manual - BundleA Setup" and the text is "~Manual - BundleA".
1413. Verify the Install button has the UAC shield and then click it (accept elevation).
1514. Click OK on the OnExecuteBegin message box.
1615. After it finishes, verify the window text is "Installation Successfully Completed".
1716. Click Close.
1817. Verify in the bundle log that the variable InstallFolder was set to "C:\Program Files (x86)\mytest".
1918. Run BundleA.exe again.
2019. Click Uninstall (accept elevation).
2120. Click OK on the OnExecuteBegin message box.
2221. After it finishes, verify the window text is "Uninstallation Successfully Completed".
2322. Click Close.
24
25CanBlockShutdownDuringApply
261. Run BundleA.exe unelevated.
272. Click Install (accept elevation).
283. Verify the OnExecuteBegin message box comes up with the message "Shutdown requests should be denied right now." and leave it up.
294. Use the Windows UI to try to restart the machine.
305. On current versions of Windows, full screen UI will display that the bundle is blocking restart. Click Cancel to stop the restart.
316. Click OK on the message box from step 3 to allow installation to continue.
327. Verify that the installation was successful and then close the bundle.
338. 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
35i400: Received system request to shut down the process: allowed: No, elevated: No, critical: No, logoff: No, close app: No
36i400: Received system request to shut down the process: allowed: No, elevated: Yes, critical: No, logoff: No, close app: No
37i401: Received result of system request to shut down the process: closing: No, elevated: No, critical: No, logoff: No, close app: No
38i401: 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
42CanLogRestartFailure
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
471. Run BundleA.exe elevated with the command line EXEEXITCODE=3010.
482. Click Install.
493. Click OK on the OnExecuteBegin message box.
504. Click Restart.
515. Look in the bundle log and verify that the restart request failure was logged with messages that look like:
52
53w005: Restarting computer...
54e000: Error 0x80070005: Failed to schedule restart.
55e023: The restart request failed, error: 0x80070005. The machine will need to be manually restarted.
56
57(5. Uninstall the bundle)
58
59CanGracefullyCutApplyShortInResponseToCriticalShutdown
60
61 Note: Requires different User Rights Assignment settings from CanLogRestartFailure - Administrators need to have the shutdown privilege.
62
631. Run BundleA.exe FORCERESTARTCA=1.
642. Click Install (accept elevation).
653. Click OK on the OnExecuteBegin message box.
664. Verify the machine automatically restarts with no chance to cancel it (because the MSI forces a restart).
675. Login to the machine.
686. Verify that the bundle automatically started running again.
697. Click OK on the OnExecuteBegin message box.
708. Verify that the installation was successful and then close the bundle.
719. 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
73i301: 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...
75i400: Received system request to shut down the process: allowed: No, elevated: No, critical: Yes, logoff: No, close app: No
76i400: Received system request to shut down the process: allowed: No, elevated: Yes, critical: Yes, logoff: No, close app: No
77i401: Received result of system request to shut down the process: closing: Yes, elevated: No, critical: Yes, logoff: No, close app: No
78i401: Received result of system request to shut down the process: closing: Yes, elevated: Yes, critical: Yes, logoff: No, close app: No
79...
80i319: Applied execute package: PackageA, result: 0x0, restart: Initiated
81...
82i420: Resuming automatic updates.
83i421: Resumed automatic updates.
84=======================================
85i001: 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
89CanRestartFromUnelevatedPerMachineBundleWithoutShutdownPrivilege
90
91 Note: Requires different User Rights Assignment settings from CanLogRestartFailure - Only Administrators should have the shutdown privilege. Users should not have it.
92
931. Run BundleA.exe unelevated with the command line EXEEXITCODE=3010.
942. Click Install.
953. Verify that the UAC prompt came up and accept elevation.
964. Click OK on the OnExecuteBegin message box.
975. Click Restart.
986. The machine should restart.
997. Login to the machine.
1008. Verify that the bundle did not automatically start running.
1019. 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)