diff options
20 files changed, 336 insertions, 76 deletions
diff --git a/src/burn/engine/EngineForApplication.h b/src/burn/engine/EngineForApplication.h index e5e8f6d7..d25a7e51 100644 --- a/src/burn/engine/EngineForApplication.h +++ b/src/burn/engine/EngineForApplication.h | |||
@@ -6,22 +6,6 @@ | |||
6 | extern "C" { | 6 | extern "C" { |
7 | #endif | 7 | #endif |
8 | 8 | ||
9 | // constants | ||
10 | |||
11 | enum WM_BURN | ||
12 | { | ||
13 | WM_BURN_FIRST = WM_APP + 0xFFF, // this enum value must always be first. | ||
14 | |||
15 | WM_BURN_DETECT, | ||
16 | WM_BURN_PLAN, | ||
17 | WM_BURN_ELEVATE, | ||
18 | WM_BURN_APPLY, | ||
19 | WM_BURN_LAUNCH_APPROVED_EXE, | ||
20 | WM_BURN_QUIT, | ||
21 | |||
22 | WM_BURN_LAST, // this enum value must always be last. | ||
23 | }; | ||
24 | |||
25 | // structs | 9 | // structs |
26 | 10 | ||
27 | typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT | 11 | typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT |
diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp index c8dce17b..93b9c002 100644 --- a/src/burn/engine/core.cpp +++ b/src/burn/engine/core.cpp | |||
@@ -593,6 +593,7 @@ LExit: | |||
593 | 593 | ||
594 | extern "C" HRESULT CoreElevate( | 594 | extern "C" HRESULT CoreElevate( |
595 | __in BURN_ENGINE_STATE* pEngineState, | 595 | __in BURN_ENGINE_STATE* pEngineState, |
596 | __in WM_BURN reason, | ||
596 | __in_opt HWND hwndParent | 597 | __in_opt HWND hwndParent |
597 | ) | 598 | ) |
598 | { | 599 | { |
@@ -608,7 +609,7 @@ extern "C" HRESULT CoreElevate( | |||
608 | ExitOnFailure(hr, "Failed to cache engine to working directory."); | 609 | ExitOnFailure(hr, "Failed to cache engine to working directory."); |
609 | } | 610 | } |
610 | 611 | ||
611 | hr = ElevationElevate(pEngineState, hwndParent); | 612 | hr = ElevationElevate(pEngineState, reason, hwndParent); |
612 | if (E_SUSPECTED_AV_INTERFERENCE == hr && 1 > cAVRetryAttempts) | 613 | if (E_SUSPECTED_AV_INTERFERENCE == hr && 1 > cAVRetryAttempts) |
613 | { | 614 | { |
614 | ++cAVRetryAttempts; | 615 | ++cAVRetryAttempts; |
@@ -720,7 +721,7 @@ extern "C" HRESULT CoreApply( | |||
720 | // Elevate. | 721 | // Elevate. |
721 | if (pEngineState->plan.fPerMachine) | 722 | if (pEngineState->plan.fPerMachine) |
722 | { | 723 | { |
723 | hr = CoreElevate(pEngineState, pEngineState->userExperience.hwndApply); | 724 | hr = CoreElevate(pEngineState, WM_BURN_APPLY, pEngineState->userExperience.hwndApply); |
724 | ExitOnFailure(hr, "Failed to elevate."); | 725 | ExitOnFailure(hr, "Failed to elevate."); |
725 | 726 | ||
726 | hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan); | 727 | hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan); |
@@ -872,7 +873,7 @@ extern "C" HRESULT CoreLaunchApprovedExe( | |||
872 | ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin."); | 873 | ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin."); |
873 | 874 | ||
874 | // Elevate. | 875 | // Elevate. |
875 | hr = CoreElevate(pEngineState, pLaunchApprovedExe->hwndParent); | 876 | hr = CoreElevate(pEngineState, WM_BURN_LAUNCH_APPROVED_EXE, pLaunchApprovedExe->hwndParent); |
876 | ExitOnFailure(hr, "Failed to elevate."); | 877 | ExitOnFailure(hr, "Failed to elevate."); |
877 | 878 | ||
878 | // Launch. | 879 | // Launch. |
diff --git a/src/burn/engine/core.h b/src/burn/engine/core.h index 812b40b1..3e636640 100644 --- a/src/burn/engine/core.h +++ b/src/burn/engine/core.h | |||
@@ -251,6 +251,7 @@ HRESULT CorePlan( | |||
251 | ); | 251 | ); |
252 | HRESULT CoreElevate( | 252 | HRESULT CoreElevate( |
253 | __in BURN_ENGINE_STATE* pEngineState, | 253 | __in BURN_ENGINE_STATE* pEngineState, |
254 | __in WM_BURN reason, | ||
254 | __in_opt HWND hwndParent | 255 | __in_opt HWND hwndParent |
255 | ); | 256 | ); |
256 | HRESULT CoreApply( | 257 | HRESULT CoreApply( |
diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp index 4d2e8544..7fd372b0 100644 --- a/src/burn/engine/elevation.cpp +++ b/src/burn/engine/elevation.cpp | |||
@@ -402,6 +402,7 @@ static HRESULT ElevatedOnExecuteActionComplete( | |||
402 | 402 | ||
403 | extern "C" HRESULT ElevationElevate( | 403 | extern "C" HRESULT ElevationElevate( |
404 | __in BURN_ENGINE_STATE* pEngineState, | 404 | __in BURN_ENGINE_STATE* pEngineState, |
405 | __in WM_BURN reason, | ||
405 | __in_opt HWND hwndParent | 406 | __in_opt HWND hwndParent |
406 | ) | 407 | ) |
407 | { | 408 | { |
@@ -450,7 +451,11 @@ extern "C" HRESULT ElevationElevate( | |||
450 | else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr) | 451 | else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr) |
451 | { | 452 | { |
452 | // The user clicked "Cancel" on the elevation prompt or the elevation prompt timed out, provide the notification with the option to retry. | 453 | // The user clicked "Cancel" on the elevation prompt or the elevation prompt timed out, provide the notification with the option to retry. |
453 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | 454 | if (WM_BURN_APPLY == reason) |
455 | { | ||
456 | hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT); | ||
457 | } | ||
458 | |||
454 | nResult = UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_ELEVATE, NULL, hr, NULL, MB_ICONERROR | MB_RETRYCANCEL, IDNOACTION); | 459 | nResult = UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_ELEVATE, NULL, hr, NULL, MB_ICONERROR | MB_RETRYCANCEL, IDNOACTION); |
455 | } | 460 | } |
456 | } while (IDRETRY == nResult); | 461 | } while (IDRETRY == nResult); |
diff --git a/src/burn/engine/elevation.h b/src/burn/engine/elevation.h index 0d15b470..810287a3 100644 --- a/src/burn/engine/elevation.h +++ b/src/burn/engine/elevation.h | |||
@@ -10,6 +10,7 @@ extern "C" { | |||
10 | // Parent (per-user process) side functions. | 10 | // Parent (per-user process) side functions. |
11 | HRESULT ElevationElevate( | 11 | HRESULT ElevationElevate( |
12 | __in BURN_ENGINE_STATE* pEngineState, | 12 | __in BURN_ENGINE_STATE* pEngineState, |
13 | __in WM_BURN reason, | ||
13 | __in_opt HWND hwndParent | 14 | __in_opt HWND hwndParent |
14 | ); | 15 | ); |
15 | HRESULT ElevationApplyInitialize( | 16 | HRESULT ElevationApplyInitialize( |
diff --git a/src/burn/engine/engine.cpp b/src/burn/engine/engine.cpp index 89082a88..48196655 100644 --- a/src/burn/engine/engine.cpp +++ b/src/burn/engine/engine.cpp | |||
@@ -893,7 +893,7 @@ static HRESULT ProcessMessage( | |||
893 | break; | 893 | break; |
894 | 894 | ||
895 | case WM_BURN_ELEVATE: | 895 | case WM_BURN_ELEVATE: |
896 | hr = CoreElevate(pEngineState, reinterpret_cast<HWND>(pmsg->lParam)); | 896 | hr = CoreElevate(pEngineState, WM_BURN_ELEVATE, reinterpret_cast<HWND>(pmsg->lParam)); |
897 | break; | 897 | break; |
898 | 898 | ||
899 | case WM_BURN_APPLY: | 899 | case WM_BURN_APPLY: |
@@ -1069,26 +1069,10 @@ static HRESULT Restart( | |||
1069 | ) | 1069 | ) |
1070 | { | 1070 | { |
1071 | HRESULT hr = S_OK; | 1071 | HRESULT hr = S_OK; |
1072 | HANDLE hProcessToken = NULL; | ||
1073 | TOKEN_PRIVILEGES priv = { }; | ||
1074 | DWORD dwRetries = 0; | 1072 | DWORD dwRetries = 0; |
1075 | 1073 | ||
1076 | if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken)) | 1074 | hr = ProcEnablePrivilege(::GetCurrentProcess(), SE_SHUTDOWN_NAME); |
1077 | { | 1075 | ExitOnFailure(hr, "Failed to enable shutdown privilege in process token."); |
1078 | ExitWithLastError(hr, "Failed to get process token."); | ||
1079 | } | ||
1080 | |||
1081 | priv.PrivilegeCount = 1; | ||
1082 | priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | ||
1083 | if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid)) | ||
1084 | { | ||
1085 | ExitWithLastError(hr, "Failed to get shutdown privilege LUID."); | ||
1086 | } | ||
1087 | |||
1088 | if (!::AdjustTokenPrivileges(hProcessToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0)) | ||
1089 | { | ||
1090 | ExitWithLastError(hr, "Failed to adjust token to add shutdown privileges."); | ||
1091 | } | ||
1092 | 1076 | ||
1093 | pEngineState->fRestarting = TRUE; | 1077 | pEngineState->fRestarting = TRUE; |
1094 | CoreUpdateRestartState(pEngineState, BURN_RESTART_STATE_REQUESTING); | 1078 | CoreUpdateRestartState(pEngineState, BURN_RESTART_STATE_REQUESTING); |
@@ -1147,7 +1131,6 @@ static HRESULT Restart( | |||
1147 | } | 1131 | } |
1148 | 1132 | ||
1149 | LExit: | 1133 | LExit: |
1150 | ReleaseHandle(hProcessToken); | ||
1151 | return hr; | 1134 | return hr; |
1152 | } | 1135 | } |
1153 | 1136 | ||
diff --git a/src/burn/engine/platform.h b/src/burn/engine/platform.h index 49d8f3e9..60184c23 100644 --- a/src/burn/engine/platform.h +++ b/src/burn/engine/platform.h | |||
@@ -8,6 +8,22 @@ | |||
8 | extern "C" { | 8 | extern "C" { |
9 | #endif | 9 | #endif |
10 | 10 | ||
11 | // constants | ||
12 | |||
13 | enum WM_BURN | ||
14 | { | ||
15 | WM_BURN_FIRST = WM_APP + 0xFFF, // this enum value must always be first. | ||
16 | |||
17 | WM_BURN_DETECT, | ||
18 | WM_BURN_PLAN, | ||
19 | WM_BURN_ELEVATE, | ||
20 | WM_BURN_APPLY, | ||
21 | WM_BURN_LAUNCH_APPROVED_EXE, | ||
22 | WM_BURN_QUIT, | ||
23 | |||
24 | WM_BURN_LAST, // this enum value must always be last. | ||
25 | }; | ||
26 | |||
11 | // forward declare | 27 | // forward declare |
12 | 28 | ||
13 | enum BURN_MODE; | 29 | enum BURN_MODE; |
diff --git a/src/burn/engine/variable.cpp b/src/burn/engine/variable.cpp index 81fa31b7..3947e29e 100644 --- a/src/burn/engine/variable.cpp +++ b/src/burn/engine/variable.cpp | |||
@@ -153,6 +153,10 @@ static HRESULT InitializeVariablePrivileged( | |||
153 | __in DWORD_PTR dwpData, | 153 | __in DWORD_PTR dwpData, |
154 | __inout BURN_VARIANT* pValue | 154 | __inout BURN_VARIANT* pValue |
155 | ); | 155 | ); |
156 | static HRESULT InitializeVariableProcessTokenPrivilege( | ||
157 | __in DWORD_PTR dwpData, | ||
158 | __inout BURN_VARIANT* pValue | ||
159 | ); | ||
156 | static HRESULT InitializeSystemLanguageID( | 160 | static HRESULT InitializeSystemLanguageID( |
157 | __in DWORD_PTR dwpData, | 161 | __in DWORD_PTR dwpData, |
158 | __inout BURN_VARIANT* pValue | 162 | __inout BURN_VARIANT* pValue |
@@ -284,6 +288,7 @@ extern "C" HRESULT VariableInitialize( | |||
284 | {L"WindowsFolder", InitializeVariableCsidlFolder, CSIDL_WINDOWS}, | 288 | {L"WindowsFolder", InitializeVariableCsidlFolder, CSIDL_WINDOWS}, |
285 | {L"WindowsVolume", InitializeVariableWindowsVolumeFolder, 0}, | 289 | {L"WindowsVolume", InitializeVariableWindowsVolumeFolder, 0}, |
286 | {BURN_BUNDLE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE}, | 290 | {BURN_BUNDLE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE}, |
291 | {L"WixCanRestart", InitializeVariableProcessTokenPrivilege, (DWORD_PTR)SE_SHUTDOWN_NAME}, | ||
287 | {BURN_BUNDLE_COMMAND_LINE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE}, | 292 | {BURN_BUNDLE_COMMAND_LINE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE}, |
288 | {BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, InitializeVariableString, NULL, FALSE, TRUE}, | 293 | {BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, InitializeVariableString, NULL, FALSE, TRUE}, |
289 | {BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, InitializeVariableString, NULL, FALSE, TRUE}, | 294 | {BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, InitializeVariableString, NULL, FALSE, TRUE}, |
@@ -2165,6 +2170,26 @@ LExit: | |||
2165 | return hr; | 2170 | return hr; |
2166 | } | 2171 | } |
2167 | 2172 | ||
2173 | static HRESULT InitializeVariableProcessTokenPrivilege( | ||
2174 | __in DWORD_PTR dwpData, | ||
2175 | __inout BURN_VARIANT* pValue | ||
2176 | ) | ||
2177 | { | ||
2178 | HRESULT hr = S_OK; | ||
2179 | BOOL fHasPrivilege = FALSE; | ||
2180 | LPCWSTR wzPrivilegeName = (LPCWSTR)dwpData; | ||
2181 | |||
2182 | hr = ProcHasPrivilege(::GetCurrentProcess(), wzPrivilegeName, &fHasPrivilege); | ||
2183 | ExitOnFailure(hr, "Failed to check if process token has privilege: %ls.", wzPrivilegeName); | ||
2184 | |||
2185 | // set value | ||
2186 | hr = BVariantSetNumeric(pValue, fHasPrivilege); | ||
2187 | ExitOnFailure(hr, "Failed to set variant value."); | ||
2188 | |||
2189 | LExit: | ||
2190 | return hr; | ||
2191 | } | ||
2192 | |||
2168 | static HRESULT InitializeSystemLanguageID( | 2193 | static HRESULT InitializeSystemLanguageID( |
2169 | __in DWORD_PTR dwpData, | 2194 | __in DWORD_PTR dwpData, |
2170 | __inout BURN_VARIANT* pValue | 2195 | __inout BURN_VARIANT* pValue |
diff --git a/src/burn/test/BurnUnitTest/ElevationTest.cpp b/src/burn/test/BurnUnitTest/ElevationTest.cpp index 8d4cc7ff..713d9e07 100644 --- a/src/burn/test/BurnUnitTest/ElevationTest.cpp +++ b/src/burn/test/BurnUnitTest/ElevationTest.cpp | |||
@@ -68,7 +68,7 @@ namespace Bootstrapper | |||
68 | // | 68 | // |
69 | // per-user side setup | 69 | // per-user side setup |
70 | // | 70 | // |
71 | hr = ElevationElevate(&engineState, NULL); | 71 | hr = ElevationElevate(&engineState, WM_BURN_ELEVATE, NULL); |
72 | TestThrowOnFailure(hr, L"Failed to elevate."); | 72 | TestThrowOnFailure(hr, L"Failed to elevate."); |
73 | 73 | ||
74 | // post execute message | 74 | // post execute message |
diff --git a/src/burn/test/BurnUnitTest/ExitCodeTest.cpp b/src/burn/test/BurnUnitTest/ExitCodeTest.cpp index d7d91d06..465ee04b 100644 --- a/src/burn/test/BurnUnitTest/ExitCodeTest.cpp +++ b/src/burn/test/BurnUnitTest/ExitCodeTest.cpp | |||
@@ -118,7 +118,7 @@ static void LoadEngineState( | |||
118 | // | 118 | // |
119 | LoadEngineState(&engineState); | 119 | LoadEngineState(&engineState); |
120 | 120 | ||
121 | hr = ElevationElevate(&engineState, NULL); | 121 | hr = ElevationElevate(&engineState, WM_BURN_ELEVATE, NULL); |
122 | TestThrowOnFailure(hr, L"Failed to elevate."); | 122 | TestThrowOnFailure(hr, L"Failed to elevate."); |
123 | 123 | ||
124 | for (DWORD i = 0; i < countof(rgExitCodeItems); ++i) | 124 | for (DWORD i = 0; i < countof(rgExitCodeItems); ++i) |
diff --git a/src/burn/test/BurnUnitTest/VariableTest.cpp b/src/burn/test/BurnUnitTest/VariableTest.cpp index 8ee6e179..b373ae8e 100644 --- a/src/burn/test/BurnUnitTest/VariableTest.cpp +++ b/src/burn/test/BurnUnitTest/VariableTest.cpp | |||
@@ -564,6 +564,7 @@ namespace Bootstrapper | |||
564 | VariableGetNumericHelper(&variables, L"TerminalServer"); | 564 | VariableGetNumericHelper(&variables, L"TerminalServer"); |
565 | VariableGetNumericHelper(&variables, L"UserUILanguageID"); | 565 | VariableGetNumericHelper(&variables, L"UserUILanguageID"); |
566 | VariableGetNumericHelper(&variables, L"UserLanguageID"); | 566 | VariableGetNumericHelper(&variables, L"UserLanguageID"); |
567 | VariableGetNumericHelper(&variables, L"WixCanRestart"); | ||
567 | 568 | ||
568 | // known folders | 569 | // known folders |
569 | Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::ApplicationData) + "\\", VariableGetStringHelper(&variables, L"AppDataFolder")); | 570 | Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::ApplicationData) + "\\", VariableGetStringHelper(&variables, L"AppDataFolder")); |
diff --git a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp index 8c4b0b35..50de2c7f 100644 --- a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp +++ b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "BalBaseBootstrapperApplicationProc.h" | 4 | #include "BalBaseBootstrapperApplicationProc.h" |
5 | #include "BalBaseBootstrapperApplication.h" | 5 | #include "BalBaseBootstrapperApplication.h" |
6 | 6 | ||
7 | static const LPCWSTR WIXBUNDLE_VARIABLE_CANRESTART = L"WixCanRestart"; | ||
7 | static const LPCWSTR WIXBUNDLE_VARIABLE_ELEVATED = L"WixBundleElevated"; | 8 | static const LPCWSTR WIXBUNDLE_VARIABLE_ELEVATED = L"WixBundleElevated"; |
8 | 9 | ||
9 | static const LPCWSTR WIXSTDBA_WINDOW_CLASS = L"WixStdBA"; | 10 | static const LPCWSTR WIXSTDBA_WINDOW_CLASS = L"WixStdBA"; |
@@ -1289,6 +1290,28 @@ public: // IBootstrapperApplication | |||
1289 | return S_OK; | 1290 | return S_OK; |
1290 | } | 1291 | } |
1291 | 1292 | ||
1293 | |||
1294 | virtual STDMETHODIMP OnElevateComplete( | ||
1295 | __in HRESULT hrStatus | ||
1296 | ) | ||
1297 | { | ||
1298 | if (m_fElevatingForRestart) | ||
1299 | { | ||
1300 | m_fElevatingForRestart = FALSE; | ||
1301 | |||
1302 | if (SUCCEEDED(hrStatus)) | ||
1303 | { | ||
1304 | m_fAllowRestart = TRUE; | ||
1305 | |||
1306 | ::SendMessageW(m_hWnd, WM_CLOSE, 0, 0); | ||
1307 | } | ||
1308 | // else if failed then OnError showed the user an error message box | ||
1309 | } | ||
1310 | |||
1311 | return __super::OnElevateComplete(hrStatus); | ||
1312 | } | ||
1313 | |||
1314 | |||
1292 | virtual STDMETHODIMP_(void) BAProcFallback( | 1315 | virtual STDMETHODIMP_(void) BAProcFallback( |
1293 | __in BOOTSTRAPPER_APPLICATION_MESSAGE message, | 1316 | __in BOOTSTRAPPER_APPLICATION_MESSAGE message, |
1294 | __in const LPVOID pvArgs, | 1317 | __in const LPVOID pvArgs, |
@@ -3734,14 +3757,16 @@ private: | |||
3734 | 3757 | ||
3735 | if (dwOldPageId != dwNewPageId) | 3758 | if (dwOldPageId != dwNewPageId) |
3736 | { | 3759 | { |
3760 | LONGLONG llCanRestart = 0; | ||
3761 | LONGLONG llElevated = 0; | ||
3762 | |||
3763 | BalGetNumericVariable(WIXBUNDLE_VARIABLE_CANRESTART, &llCanRestart); | ||
3764 | BalGetNumericVariable(WIXBUNDLE_VARIABLE_ELEVATED, &llElevated); | ||
3765 | m_fRestartRequiresElevation = !llCanRestart && !llElevated; | ||
3766 | |||
3737 | // Enable disable controls per-page. | 3767 | // Enable disable controls per-page. |
3738 | if (m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL] == dwNewPageId) // on the "Install" page, ensure the install button is enabled/disabled correctly. | 3768 | if (m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL] == dwNewPageId) // on the "Install" page, ensure the install button is enabled/disabled correctly. |
3739 | { | 3769 | { |
3740 | LONGLONG llElevated = 0; | ||
3741 | if (m_Bundle.fPerMachine) | ||
3742 | { | ||
3743 | BalGetNumericVariable(WIXBUNDLE_VARIABLE_ELEVATED, &llElevated); | ||
3744 | } | ||
3745 | ThemeControlElevates(m_pControlInstallButton, (m_Bundle.fPerMachine && !llElevated)); | 3770 | ThemeControlElevates(m_pControlInstallButton, (m_Bundle.fPerMachine && !llElevated)); |
3746 | 3771 | ||
3747 | // If the EULA control exists, show it only if a license URL is provided as well. | 3772 | // If the EULA control exists, show it only if a license URL is provided as well. |
@@ -3757,12 +3782,18 @@ private: | |||
3757 | } | 3782 | } |
3758 | else if (m_rgdwPageIds[WIXSTDBA_PAGE_MODIFY] == dwNewPageId) | 3783 | else if (m_rgdwPageIds[WIXSTDBA_PAGE_MODIFY] == dwNewPageId) |
3759 | { | 3784 | { |
3785 | ThemeControlElevates(m_pControlRepairButton, (m_Bundle.fPerMachine && !llElevated)); | ||
3786 | ThemeControlElevates(m_pControlUninstallButton, (m_Bundle.fPerMachine && !llElevated)); | ||
3787 | |||
3760 | ThemeControlEnable(m_pControlRepairButton, !m_fSuppressRepair); | 3788 | ThemeControlEnable(m_pControlRepairButton, !m_fSuppressRepair); |
3761 | } | 3789 | } |
3762 | else if (m_rgdwPageIds[WIXSTDBA_PAGE_SUCCESS] == dwNewPageId) // on the "Success" page, check if the restart or launch button should be enabled. | 3790 | else if (m_rgdwPageIds[WIXSTDBA_PAGE_SUCCESS] == dwNewPageId) // on the "Success" page, check if the restart or launch button should be enabled. |
3763 | { | 3791 | { |
3764 | BOOL fEnableRestartButton = FALSE; | 3792 | BOOL fEnableRestartButton = FALSE; |
3765 | BOOL fLaunchTargetExists = FALSE; | 3793 | BOOL fLaunchTargetExists = FALSE; |
3794 | |||
3795 | ThemeControlElevates(m_pControlSuccessRestartButton, m_fRestartRequiresElevation); | ||
3796 | |||
3766 | if (m_fShouldRestart) | 3797 | if (m_fShouldRestart) |
3767 | { | 3798 | { |
3768 | if (BAL_INFO_RESTART_PROMPT == m_BalInfoCommand.restart) | 3799 | if (BAL_INFO_RESTART_PROMPT == m_BalInfoCommand.restart) |
@@ -3784,6 +3815,8 @@ private: | |||
3784 | BOOL fShowErrorMessage = FALSE; | 3815 | BOOL fShowErrorMessage = FALSE; |
3785 | BOOL fEnableRestartButton = FALSE; | 3816 | BOOL fEnableRestartButton = FALSE; |
3786 | 3817 | ||
3818 | ThemeControlElevates(m_pControlFailureRestartButton, m_fRestartRequiresElevation); | ||
3819 | |||
3787 | if (FAILED(m_hrFinal)) | 3820 | if (FAILED(m_hrFinal)) |
3788 | { | 3821 | { |
3789 | // If we know the failure message, use that. | 3822 | // If we know the failure message, use that. |
@@ -4156,8 +4189,20 @@ private: | |||
4156 | { | 4189 | { |
4157 | AssertSz(m_fRestartRequired, "Restart must be requested to be able to click on the restart button."); | 4190 | AssertSz(m_fRestartRequired, "Restart must be requested to be able to click on the restart button."); |
4158 | 4191 | ||
4159 | m_fAllowRestart = TRUE; | 4192 | if (m_fRestartRequiresElevation) |
4160 | ::SendMessageW(m_hWnd, WM_CLOSE, 0, 0); | 4193 | { |
4194 | m_fElevatingForRestart = TRUE; | ||
4195 | ThemeControlEnable(m_pControlFailureRestartButton, FALSE); | ||
4196 | ThemeControlEnable(m_pControlSuccessRestartButton, FALSE); | ||
4197 | |||
4198 | m_pEngine->Elevate(m_hWnd); | ||
4199 | } | ||
4200 | else | ||
4201 | { | ||
4202 | m_fAllowRestart = TRUE; | ||
4203 | |||
4204 | ::SendMessageW(m_hWnd, WM_CLOSE, 0, 0); | ||
4205 | } | ||
4161 | } | 4206 | } |
4162 | 4207 | ||
4163 | 4208 | ||
@@ -4649,6 +4694,8 @@ public: | |||
4649 | m_fRestartRequired = FALSE; | 4694 | m_fRestartRequired = FALSE; |
4650 | m_fShouldRestart = FALSE; | 4695 | m_fShouldRestart = FALSE; |
4651 | m_fAllowRestart = FALSE; | 4696 | m_fAllowRestart = FALSE; |
4697 | m_fRestartRequiresElevation = FALSE; | ||
4698 | m_fElevatingForRestart = FALSE; | ||
4652 | 4699 | ||
4653 | m_sczLicenseFile = NULL; | 4700 | m_sczLicenseFile = NULL; |
4654 | m_sczLicenseUrl = NULL; | 4701 | m_sczLicenseUrl = NULL; |
@@ -4956,6 +5003,8 @@ private: | |||
4956 | BOOL m_fRestartRequired; | 5003 | BOOL m_fRestartRequired; |
4957 | BOOL m_fShouldRestart; | 5004 | BOOL m_fShouldRestart; |
4958 | BOOL m_fAllowRestart; | 5005 | BOOL m_fAllowRestart; |
5006 | BOOL m_fRestartRequiresElevation; | ||
5007 | BOOL m_fElevatingForRestart; | ||
4959 | 5008 | ||
4960 | LPWSTR m_sczLicenseFile; | 5009 | LPWSTR m_sczLicenseFile; |
4961 | LPWSTR m_sczLicenseUrl; | 5010 | LPWSTR m_sczLicenseUrl; |
diff --git a/src/libs/dutil/WixToolset.DUtil/inc/procutil.h b/src/libs/dutil/WixToolset.DUtil/inc/procutil.h index d61d91b5..6a641a5b 100644 --- a/src/libs/dutil/WixToolset.DUtil/inc/procutil.h +++ b/src/libs/dutil/WixToolset.DUtil/inc/procutil.h | |||
@@ -23,9 +23,21 @@ HRESULT DAPI ProcSystem( | |||
23 | __out BOOL* pfSystem | 23 | __out BOOL* pfSystem |
24 | ); | 24 | ); |
25 | 25 | ||
26 | HRESULT DAPI ProcTokenUser( | 26 | HRESULT DAPI ProcGetTokenInformation( |
27 | __in HANDLE hProcess, | 27 | __in HANDLE hProcess, |
28 | __out TOKEN_USER** ppTokenUser | 28 | __in TOKEN_INFORMATION_CLASS tokenInformationClass, |
29 | __out LPVOID* ppvTokenInformation | ||
30 | ); | ||
31 | |||
32 | HRESULT DAPI ProcHasPrivilege( | ||
33 | __in HANDLE hProcess, | ||
34 | __in LPCWSTR wzPrivilegeName, | ||
35 | __out BOOL* pfHasPrivilege | ||
36 | ); | ||
37 | |||
38 | HRESULT DAPI ProcEnablePrivilege( | ||
39 | __in HANDLE hProcess, | ||
40 | __in LPCWSTR wzPrivilegeName | ||
29 | ); | 41 | ); |
30 | 42 | ||
31 | HRESULT DAPI ProcWow64( | 43 | HRESULT DAPI ProcWow64( |
diff --git a/src/libs/dutil/WixToolset.DUtil/procutil.cpp b/src/libs/dutil/WixToolset.DUtil/procutil.cpp index 29f575ae..376aec6d 100644 --- a/src/libs/dutil/WixToolset.DUtil/procutil.cpp +++ b/src/libs/dutil/WixToolset.DUtil/procutil.cpp | |||
@@ -85,7 +85,7 @@ extern "C" HRESULT DAPI ProcSystem( | |||
85 | HRESULT hr = S_OK; | 85 | HRESULT hr = S_OK; |
86 | TOKEN_USER* pTokenUser = NULL; | 86 | TOKEN_USER* pTokenUser = NULL; |
87 | 87 | ||
88 | hr = ProcTokenUser(hProcess, &pTokenUser); | 88 | hr = ProcGetTokenInformation(hProcess, TokenUser, reinterpret_cast<LPVOID*>(&pTokenUser)); |
89 | ProcExitOnFailure(hr, "Failed to get TokenUser from process token."); | 89 | ProcExitOnFailure(hr, "Failed to get TokenUser from process token."); |
90 | 90 | ||
91 | *pfSystem = ::IsWellKnownSid(pTokenUser->User.Sid, WinLocalSystemSid); | 91 | *pfSystem = ::IsWellKnownSid(pTokenUser->User.Sid, WinLocalSystemSid); |
@@ -96,15 +96,16 @@ LExit: | |||
96 | return hr; | 96 | return hr; |
97 | } | 97 | } |
98 | 98 | ||
99 | extern "C" HRESULT DAPI ProcTokenUser( | 99 | extern "C" HRESULT DAPI ProcGetTokenInformation( |
100 | __in HANDLE hProcess, | 100 | __in HANDLE hProcess, |
101 | __out TOKEN_USER** ppTokenUser | 101 | __in TOKEN_INFORMATION_CLASS tokenInformationClass, |
102 | __out LPVOID* ppvTokenInformation | ||
102 | ) | 103 | ) |
103 | { | 104 | { |
104 | HRESULT hr = S_OK; | 105 | HRESULT hr = S_OK; |
105 | DWORD er = ERROR_SUCCESS; | 106 | DWORD er = ERROR_SUCCESS; |
106 | HANDLE hToken = NULL; | 107 | HANDLE hToken = NULL; |
107 | TOKEN_USER* pTokenUser = NULL; | 108 | LPVOID pvTokenInformation = NULL; |
108 | DWORD cbToken = 0; | 109 | DWORD cbToken = 0; |
109 | 110 | ||
110 | if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) | 111 | if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) |
@@ -112,33 +113,104 @@ extern "C" HRESULT DAPI ProcTokenUser( | |||
112 | ProcExitWithLastError(hr, "Failed to open process token."); | 113 | ProcExitWithLastError(hr, "Failed to open process token."); |
113 | } | 114 | } |
114 | 115 | ||
115 | if (::GetTokenInformation(hToken, TokenUser, pTokenUser, 0, &cbToken)) | 116 | if (!::GetTokenInformation(hToken, tokenInformationClass, pvTokenInformation, 0, &cbToken)) |
116 | { | ||
117 | er = ERROR_SUCCESS; | ||
118 | } | ||
119 | else | ||
120 | { | 117 | { |
121 | er = ::GetLastError(); | 118 | er = ::GetLastError(); |
122 | } | 119 | } |
123 | 120 | ||
124 | if (er != ERROR_INSUFFICIENT_BUFFER) | 121 | if (er != ERROR_INSUFFICIENT_BUFFER) |
125 | { | 122 | { |
126 | ProcExitOnWin32Error(er, hr, "Failed to get user from process token size."); | 123 | ProcExitOnWin32Error(er, hr, "Failed to get information from process token size."); |
127 | } | 124 | } |
128 | 125 | ||
129 | pTokenUser = reinterpret_cast<TOKEN_USER*>(MemAlloc(cbToken, TRUE)); | 126 | pvTokenInformation = MemAlloc(cbToken, TRUE); |
130 | ProcExitOnNull(pTokenUser, hr, E_OUTOFMEMORY, "Failed to allocate token information."); | 127 | ProcExitOnNull(pvTokenInformation, hr, E_OUTOFMEMORY, "Failed to allocate token information."); |
131 | 128 | ||
132 | if (!::GetTokenInformation(hToken, TokenUser, pTokenUser, cbToken, &cbToken)) | 129 | if (!::GetTokenInformation(hToken, tokenInformationClass, pvTokenInformation, cbToken, &cbToken)) |
133 | { | 130 | { |
134 | ProcExitWithLastError(hr, "Failed to get user from process token."); | 131 | ProcExitWithLastError(hr, "Failed to get information from process token."); |
135 | } | 132 | } |
136 | 133 | ||
137 | *ppTokenUser = pTokenUser; | 134 | *ppvTokenInformation = pvTokenInformation; |
138 | pTokenUser = NULL; | 135 | pvTokenInformation = NULL; |
136 | |||
137 | LExit: | ||
138 | ReleaseMem(pvTokenInformation); | ||
139 | ReleaseHandle(hToken); | ||
140 | |||
141 | return hr; | ||
142 | } | ||
143 | |||
144 | extern "C" HRESULT DAPI ProcHasPrivilege( | ||
145 | __in HANDLE hProcess, | ||
146 | __in LPCWSTR wzPrivilegeName, | ||
147 | __out BOOL* pfHasPrivilege | ||
148 | ) | ||
149 | { | ||
150 | HRESULT hr = S_OK; | ||
151 | TOKEN_PRIVILEGES* pTokenPrivileges = NULL; | ||
152 | LUID luidPrivilege = { }; | ||
153 | |||
154 | *pfHasPrivilege = FALSE; | ||
155 | |||
156 | if (!::LookupPrivilegeValueW(NULL, wzPrivilegeName, &luidPrivilege)) | ||
157 | { | ||
158 | ProcExitWithLastError(hr, "Failed to get privilege LUID: %ls", wzPrivilegeName); | ||
159 | } | ||
160 | |||
161 | hr = ProcGetTokenInformation(hProcess, TokenPrivileges, reinterpret_cast<LPVOID*>(&pTokenPrivileges)); | ||
162 | ProcExitOnFailure(hr, "Failed to get token privilege information."); | ||
163 | |||
164 | for (DWORD i = 0; i < pTokenPrivileges->PrivilegeCount; ++i) | ||
165 | { | ||
166 | LUID* pTokenLuid = &pTokenPrivileges->Privileges[i].Luid; | ||
167 | |||
168 | if (luidPrivilege.LowPart == pTokenLuid->LowPart && luidPrivilege.HighPart == pTokenLuid->HighPart) | ||
169 | { | ||
170 | *pfHasPrivilege = TRUE; | ||
171 | break; | ||
172 | } | ||
173 | } | ||
174 | |||
175 | LExit: | ||
176 | ReleaseMem(pTokenPrivileges); | ||
177 | |||
178 | return hr; | ||
179 | } | ||
180 | |||
181 | extern "C" HRESULT DAPI ProcEnablePrivilege( | ||
182 | __in HANDLE hProcess, | ||
183 | __in LPCWSTR wzPrivilegeName | ||
184 | ) | ||
185 | { | ||
186 | HRESULT hr = S_OK; | ||
187 | HANDLE hToken = NULL; | ||
188 | TOKEN_PRIVILEGES priv = { }; | ||
189 | |||
190 | priv.PrivilegeCount = 1; | ||
191 | priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; | ||
192 | |||
193 | if (!::LookupPrivilegeValueW(NULL, wzPrivilegeName, &priv.Privileges[0].Luid)) | ||
194 | { | ||
195 | ProcExitWithLastError(hr, "Failed to get privilege LUID: %ls", wzPrivilegeName); | ||
196 | } | ||
197 | |||
198 | if (!::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken)) | ||
199 | { | ||
200 | ProcExitWithLastError(hr, "Failed to get process token to adjust privileges."); | ||
201 | } | ||
202 | |||
203 | if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0)) | ||
204 | { | ||
205 | ProcExitWithLastError(hr, "Failed to adjust token to add privilege: %ls", wzPrivilegeName); | ||
206 | } | ||
207 | |||
208 | if (ERROR_NOT_ALL_ASSIGNED == ::GetLastError()) | ||
209 | { | ||
210 | hr = S_FALSE; | ||
211 | } | ||
139 | 212 | ||
140 | LExit: | 213 | LExit: |
141 | ReleaseMem(pTokenUser); | ||
142 | ReleaseHandle(hToken); | 214 | ReleaseHandle(hToken); |
143 | 215 | ||
144 | return hr; | 216 | return hr; |
diff --git a/src/libs/dutil/test/DUtilUnitTest/ProcUtilTest.cpp b/src/libs/dutil/test/DUtilUnitTest/ProcUtilTest.cpp index 297d90f4..8672ed38 100644 --- a/src/libs/dutil/test/DUtilUnitTest/ProcUtilTest.cpp +++ b/src/libs/dutil/test/DUtilUnitTest/ProcUtilTest.cpp | |||
@@ -6,6 +6,7 @@ using namespace System; | |||
6 | using namespace System::Security::Principal; | 6 | using namespace System::Security::Principal; |
7 | using namespace Xunit; | 7 | using namespace Xunit; |
8 | using namespace WixBuildTools::TestSupport; | 8 | using namespace WixBuildTools::TestSupport; |
9 | using namespace WixBuildTools::TestSupport::XunitExtensions; | ||
9 | 10 | ||
10 | namespace DutilTests | 11 | namespace DutilTests |
11 | { | 12 | { |
@@ -13,7 +14,7 @@ namespace DutilTests | |||
13 | { | 14 | { |
14 | public: | 15 | public: |
15 | [Fact] | 16 | [Fact] |
16 | void ProcTokenUserTest() | 17 | void ProcGetTokenInformationTest() |
17 | { | 18 | { |
18 | HRESULT hr = S_OK; | 19 | HRESULT hr = S_OK; |
19 | TOKEN_USER* pTokenUser = NULL; | 20 | TOKEN_USER* pTokenUser = NULL; |
@@ -21,7 +22,7 @@ namespace DutilTests | |||
21 | 22 | ||
22 | try | 23 | try |
23 | { | 24 | { |
24 | hr = ProcTokenUser(::GetCurrentProcess(), &pTokenUser); | 25 | hr = ProcGetTokenInformation(::GetCurrentProcess(), TokenUser, reinterpret_cast<LPVOID*>(&pTokenUser)); |
25 | NativeAssert::Succeeded(hr, "Failed to get TokenUser for current process."); | 26 | NativeAssert::Succeeded(hr, "Failed to get TokenUser for current process."); |
26 | 27 | ||
27 | if (!::ConvertSidToStringSidW(pTokenUser->User.Sid, &sczSid)) | 28 | if (!::ConvertSidToStringSidW(pTokenUser->User.Sid, &sczSid)) |
@@ -38,5 +39,28 @@ namespace DutilTests | |||
38 | ReleaseStr(sczSid); | 39 | ReleaseStr(sczSid); |
39 | } | 40 | } |
40 | } | 41 | } |
42 | |||
43 | [SkippableFact] | ||
44 | void ProcHasPrivilegeTest() | ||
45 | { | ||
46 | HRESULT hr = S_OK; | ||
47 | BOOL fHasPrivilege = FALSE; | ||
48 | |||
49 | hr = ProcHasPrivilege(::GetCurrentProcess(), SE_CREATE_TOKEN_NAME, &fHasPrivilege); | ||
50 | NativeAssert::Succeeded(hr, "Failed to check privilege for current process."); | ||
51 | |||
52 | if (fHasPrivilege) | ||
53 | { | ||
54 | WixAssert::Skip("Didn't expect process to have SE_CREATE_TOKEN_NAME privilege"); | ||
55 | } | ||
56 | |||
57 | hr = ProcHasPrivilege(::GetCurrentProcess(), SE_INC_WORKING_SET_NAME, &fHasPrivilege); | ||
58 | NativeAssert::Succeeded(hr, "Failed to check privilege for current process."); | ||
59 | |||
60 | if (!fHasPrivilege) | ||
61 | { | ||
62 | WixAssert::Skip("Expected process to have SE_INC_WORKING_SET_NAME privilege"); | ||
63 | } | ||
64 | } | ||
41 | }; | 65 | }; |
42 | } | 66 | } |
diff --git a/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp b/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp index c619dbd6..a2b8e041 100644 --- a/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp +++ b/src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp | |||
@@ -477,7 +477,7 @@ static HRESULT LogUserSid() | |||
477 | TOKEN_USER* pTokenUser = NULL; | 477 | TOKEN_USER* pTokenUser = NULL; |
478 | LPWSTR sczSid = NULL; | 478 | LPWSTR sczSid = NULL; |
479 | 479 | ||
480 | hr = ProcTokenUser(::GetCurrentProcess(), &pTokenUser); | 480 | hr = ProcGetTokenInformation(::GetCurrentProcess(), TokenUser, reinterpret_cast<LPVOID*>(&pTokenUser)); |
481 | BalExitOnFailure(hr, "Failed to get user from process token."); | 481 | BalExitOnFailure(hr, "Failed to get user from process token."); |
482 | 482 | ||
483 | if (!::ConvertSidToStringSidW(pTokenUser->User.Sid, &sczSid)) | 483 | if (!::ConvertSidToStringSidW(pTokenUser->User.Sid, &sczSid)) |
diff --git a/src/test/burn/TestData/Manual/BundleA/ManualTests.txt b/src/test/burn/TestData/Manual/BundleA/ManualTests.txt index d432f94a..b8c6e74d 100644 --- a/src/test/burn/TestData/Manual/BundleA/ManualTests.txt +++ b/src/test/burn/TestData/Manual/BundleA/ManualTests.txt | |||
@@ -94,11 +94,12 @@ CanRestartFromUnelevatedPerMachineBundleWithoutShutdownPrivilege | |||
94 | 2. Click Install. | 94 | 2. Click Install. |
95 | 3. Verify that the UAC prompt came up and accept elevation. | 95 | 3. Verify that the UAC prompt came up and accept elevation. |
96 | 4. Click OK on the OnExecuteBegin message box. | 96 | 4. Click OK on the OnExecuteBegin message box. |
97 | 5. Click Restart. | 97 | 5. Verify the Restart button doesn't have the UAC shield and then click it. |
98 | 6. The machine should restart. | 98 | 6. Verify that there was no UAC prompt. |
99 | 7. Login to the machine. | 99 | 7. The machine should restart. |
100 | 8. Verify that the bundle did not automatically start running. | 100 | 8. Login to the machine. |
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): | 101 | 9. Verify that the bundle did not automatically start running. |
102 | 10. 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 | ||
103 | [0DDC:0448]w005: Restarting computer... | 104 | [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]i400: Received system request to shut down the process: allowed: Yes, elevated: No, critical: No, logoff: No, close app: No |
@@ -107,4 +108,47 @@ CanRestartFromUnelevatedPerMachineBundleWithoutShutdownPrivilege | |||
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 | [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 | ======================================= |
109 | 110 | ||
111 | (11. Uninstall the bundle) | ||
112 | |||
113 | CanRestartFromUnelevatedPerUserBundleWithoutShutdownPrivilege | ||
114 | |||
115 | Note: Requires different User Rights Assignment settings from CanLogRestartFailure - Only Administrators should have the shutdown privilege. Users should not have it. | ||
116 | |||
117 | 1. Run BundleC.exe unelevated with the command line EXEEXITCODE=3010. | ||
118 | 2. Click Install. | ||
119 | 3. Verify the Restart button has the UAC shield and then click it. | ||
120 | 4. Verify that the UAC prompt came up and accept elevation. | ||
121 | 5. The machine should restart. | ||
122 | 6. Login to the machine. | ||
123 | 7. Verify that the bundle did not automatically start running. | ||
124 | 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 (the process id for w005 must match the elevated i400 and i401): | ||
125 | |||
126 | [0DDC:0448]w005: Restarting computer... | ||
127 | [1228:18CC]i400: Received system request to shut down the process: allowed: Yes, elevated: No, critical: No, logoff: No, close app: No | ||
128 | [1228:18CC]i401: Received result of system request to shut down the process: closing: Yes, elevated: No, critical: No, logoff: No, close app: No | ||
129 | [0DDC:0954]i400: Received system request to shut down the process: allowed: Yes, elevated: Yes, critical: No, logoff: No, close app: No | ||
130 | [0DDC:0954]i401: Received result of system request to shut down the process: closing: Yes, elevated: Yes, critical: No, logoff: No, close app: No | ||
131 | ======================================= | ||
132 | |||
133 | (9. Uninstall the bundle) | ||
134 | |||
135 | CanRestartFromUnelevatedPerUserBundleWithShutdownPrivilege | ||
136 | |||
137 | Note: Requires different User Rights Assignment settings from CanLogRestartFailure - Administrators and Users should have the shutdown privilege. | ||
138 | |||
139 | 1. Run BundleC.exe unelevated with the command line EXEEXITCODE=3010. | ||
140 | 2. Click Install. | ||
141 | 3. Verify that there was no UAC prompt. | ||
142 | 4. Verify the Restart button doesn't have the UAC shield and then click it. | ||
143 | 5. Verify that there was no UAC prompt. | ||
144 | 6. The machine should restart. | ||
145 | 7. Login to the machine. | ||
146 | 8. Verify that the bundle did not automatically start running. | ||
147 | 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 (there should be no elevated i400 and i401 and the process id for w005 must match): | ||
148 | |||
149 | [1228:0448]w005: Restarting computer... | ||
150 | [1228:18CC]i400: Received system request to shut down the process: allowed: Yes, elevated: No, critical: No, logoff: No, close app: No | ||
151 | [1228:18CC]i401: Received result of system request to shut down the process: closing: Yes, elevated: No, critical: No, logoff: No, close app: No | ||
152 | ======================================= | ||
153 | |||
110 | (10. Uninstall the bundle) | 154 | (10. Uninstall the bundle) |
diff --git a/src/test/burn/TestData/Manual/BundleC/BundleC.wixproj b/src/test/burn/TestData/Manual/BundleC/BundleC.wixproj new file mode 100644 index 00000000..eabd529c --- /dev/null +++ b/src/test/burn/TestData/Manual/BundleC/BundleC.wixproj | |||
@@ -0,0 +1,18 @@ | |||
1 | <!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> | ||
2 | <Project Sdk="WixToolset.Sdk"> | ||
3 | <PropertyGroup> | ||
4 | <OutputType>Bundle</OutputType> | ||
5 | <BA>hyperlinkLicense</BA> | ||
6 | <UpgradeCode>{5CAE82BB-A2F3-4994-9BA8-4ACEAFAE7738}</UpgradeCode> | ||
7 | </PropertyGroup> | ||
8 | <ItemGroup> | ||
9 | <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" /> | ||
10 | </ItemGroup> | ||
11 | <ItemGroup> | ||
12 | <ProjectReference Include="..\PackagePerUser\PackagePerUser.wixproj" /> | ||
13 | <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" /> | ||
14 | </ItemGroup> | ||
15 | <ItemGroup> | ||
16 | <PackageReference Include="WixToolset.Bal.wixext" /> | ||
17 | </ItemGroup> | ||
18 | </Project> \ No newline at end of file | ||
diff --git a/src/test/burn/TestData/Manual/BundleC/BundleC.wxs b/src/test/burn/TestData/Manual/BundleC/BundleC.wxs new file mode 100644 index 00000000..aadcd8b2 --- /dev/null +++ b/src/test/burn/TestData/Manual/BundleC/BundleC.wxs | |||
@@ -0,0 +1,15 @@ | |||
1 | <!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> | ||
2 | |||
3 | |||
4 | <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal"> | ||
5 | <Fragment> | ||
6 | <PackageGroup Id="BundlePackages"> | ||
7 | <MsiPackage Id="PackagePerUser" SourceFile="$(var.PackagePerUser.TargetPath)" /> | ||
8 | <ExePackage Id="ExeA" Cache="remove" PerMachine="no" | ||
9 | DetectCondition="" Permanent="yes" InstallArguments="/ec [EXEEXITCODE]"> | ||
10 | <PayloadGroupRef Id="TestExePayloads" /> | ||
11 | </ExePackage> | ||
12 | </PackageGroup> | ||
13 | <Variable Name="EXEEXITCODE" bal:Overridable="yes" Value="0" /> | ||
14 | </Fragment> | ||
15 | </Wix> | ||
diff --git a/src/test/burn/TestData/Manual/PackagePerUser/PackagePerUser.wixproj b/src/test/burn/TestData/Manual/PackagePerUser/PackagePerUser.wixproj new file mode 100644 index 00000000..0efa4d63 --- /dev/null +++ b/src/test/burn/TestData/Manual/PackagePerUser/PackagePerUser.wixproj | |||
@@ -0,0 +1,9 @@ | |||
1 | <!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. --> | ||
2 | <Project Sdk="WixToolset.Sdk"> | ||
3 | <PropertyGroup> | ||
4 | <UpgradeCode>{F7FBAC90-07A6-4DEB-A9C5-E267EDDA28EF}</UpgradeCode> | ||
5 | </PropertyGroup> | ||
6 | <ItemGroup> | ||
7 | <Compile Include="..\..\Templates\PackagePerUser.wxs" Link="PackagePerUser.wxs" /> | ||
8 | </ItemGroup> | ||
9 | </Project> \ No newline at end of file | ||