From ff659159e041bf6c083e6b7fcb9b726065a9dd73 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Mon, 3 May 2021 09:55:22 -0700 Subject: Move Util.wixext into ext --- src/ext/Util/ca/test.cpp | 269 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 src/ext/Util/ca/test.cpp (limited to 'src/ext/Util/ca/test.cpp') diff --git a/src/ext/Util/ca/test.cpp b/src/ext/Util/ca/test.cpp new file mode 100644 index 00000000..c4d215f0 --- /dev/null +++ b/src/ext/Util/ca/test.cpp @@ -0,0 +1,269 @@ +// 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. + +#include "precomp.h" + +#define WIXCA_UITHREAD_CLASS_WINDOW L"WixCaMessageWindow" + +extern HMODULE g_hInstCADLL; + + +// structs + +struct UITHREAD_CONTEXT +{ + HANDLE hInitializedEvent; + HINSTANCE hInstance; + HWND hWnd; +}; + + +// internal function declarations + +static HRESULT CreateMessageWindow( + __out HWND* phWnd + ); + +static void CloseMessageWindow( + __in HWND hWnd + ); + +static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ); + +static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ); + + +/****************************************************************** +WixFailWhenDeferred - entry point for WixFailWhenDeferred + custom action which always fails when running as a deferred + custom action (otherwise it blindly succeeds). It's useful when + testing the rollback of deferred custom actions: Schedule it + immediately after the rollback/deferred CA pair you're testing + and it will fail, causing your rollback CA to get invoked. +********************************************************************/ +extern "C" UINT __stdcall WixFailWhenDeferred( + __in MSIHANDLE hInstall + ) +{ + return ::MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS; +} + +/****************************************************************** +WixWaitForEvent - entry point for WixWaitForEvent custom action + which waits for either the WixWaitForEventFail or + WixWaitForEventSucceed named auto reset events. Signaling the + WixWaitForEventFail event will return ERROR_INSTALL_FAILURE or + signaling the WixWaitForEventSucceed event will return + ERROR_SUCCESS. Both events are declared in the Global\ namespace. +********************************************************************/ +extern "C" UINT __stdcall WixWaitForEvent( + __in MSIHANDLE hInstall + ) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + HWND hMessageWindow = NULL; + LPCWSTR wzSDDL = L"D:(A;;GA;;;WD)"; + OS_VERSION version = OS_VERSION_UNKNOWN; + DWORD dwServicePack = 0; + PSECURITY_DESCRIPTOR pSD = NULL; + SECURITY_ATTRIBUTES sa = { }; + HANDLE rghEvents[2]; + + hr = WcaInitialize(hInstall, "WixWaitForEvent"); + ExitOnFailure(hr, "Failed to initialize."); + + // Create a window to prevent shutdown requests. + hr = CreateMessageWindow(&hMessageWindow); + ExitOnFailure(hr, "Failed to create message window."); + + // If running on Vista/2008 or newer use integrity enhancements. + OsGetVersion(&version, &dwServicePack); + if (OS_VERSION_VISTA <= version) + { + // Add SACL to allow Everyone to signal from a medium integrity level. + wzSDDL = L"D:(A;;GA;;;WD)S:(ML;;NW;;;ME)"; + } + + // Create the security descriptor and attributes for the events. + if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSDDL, SDDL_REVISION_1, &pSD, NULL)) + { + ExitWithLastError(hr, "Failed to create the security descriptor for the events."); + } + + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = pSD; + sa.bInheritHandle = FALSE; + + rghEvents[0] = ::CreateEventW(&sa, FALSE, FALSE, L"Global\\WixWaitForEventFail"); + ExitOnNullWithLastError(rghEvents[0], hr, "Failed to create the Global\\WixWaitForEventFail event."); + + rghEvents[1] = ::CreateEventW(&sa, FALSE, FALSE, L"Global\\WixWaitForEventSucceed"); + ExitOnNullWithLastError(rghEvents[1], hr, "Failed to create the Global\\WixWaitForEventSucceed event."); + + // Wait for either of the events to be signaled and handle accordingly. + er = ::WaitForMultipleObjects(countof(rghEvents), rghEvents, FALSE, INFINITE); + switch (er) + { + case WAIT_OBJECT_0 + 0: + er = ERROR_INSTALL_FAILURE; + break; + case WAIT_OBJECT_0 + 1: + er = ERROR_SUCCESS; + break; + default: + ExitOnWin32Error(er, hr, "Unexpected failure."); + } + +LExit: + ReleaseHandle(rghEvents[1]); + ReleaseHandle(rghEvents[0]); + + if (pSD) + { + ::LocalFree(pSD); + } + + if (hMessageWindow) + { + CloseMessageWindow(hMessageWindow); + } + + if (FAILED(hr)) + { + er = ERROR_INSTALL_FAILURE; + } + + return WcaFinalize(er); +} + + +// internal function definitions + +static HRESULT CreateMessageWindow( + __out HWND* phWnd + ) +{ + HRESULT hr = S_OK; + HANDLE rgWaitHandles[2] = { }; + UITHREAD_CONTEXT context = { }; + + // Create event to signal after the UI thread / window is initialized. + rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL); + ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event."); + + // Pass necessary information to create the window. + context.hInitializedEvent = rgWaitHandles[0]; + context.hInstance = (HINSTANCE)g_hInstCADLL; + + // Create our separate UI thread. + rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL); + ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread."); + + // Wait for either the thread to be initialized or the window to exit / fail prematurely. + ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE); + + // Pass the window back to the caller. + *phWnd = context.hWnd; + +LExit: + ReleaseHandle(rgWaitHandles[1]); + ReleaseHandle(rgWaitHandles[0]); + + return hr; +} + +static void CloseMessageWindow( + __in HWND hWnd + ) +{ + if (::IsWindow(hWnd)) + { + ::PostMessageW(hWnd, WM_CLOSE, 0, 0); + } +} + +static DWORD WINAPI ThreadProc( + __in LPVOID pvContext + ) +{ + HRESULT hr = S_OK; + UITHREAD_CONTEXT* pContext = static_cast(pvContext); + + WNDCLASSW wc = { }; + BOOL fRegistered = TRUE; + HWND hWnd = NULL; + + BOOL fRet = FALSE; + MSG msg = { }; + + wc.lpfnWndProc = WndProc; + wc.hInstance = pContext->hInstance; + wc.lpszClassName = WIXCA_UITHREAD_CLASS_WINDOW; + + if (!::RegisterClassW(&wc)) + { + ExitWithLastError(hr, "Failed to register window."); + } + + fRegistered = TRUE; + + // Create the window to handle reboots without activating it. + hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, NULL, WS_POPUP | WS_VISIBLE, CW_USEDEFAULT, SW_SHOWNA, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, NULL); + ExitOnNullWithLastError(hWnd, hr, "Failed to create window."); + + // Persist the window handle and let the caller know we've initialized. + pContext->hWnd = hWnd; + ::SetEvent(pContext->hInitializedEvent); + + // Pump messages until the window is closed. + while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0))) + { + if (-1 == fRet) + { + hr = E_UNEXPECTED; + ExitOnFailure(hr, "Unexpected return value from message pump."); + } + else if (!::IsDialogMessageW(msg.hwnd, &msg)) + { + ::TranslateMessage(&msg); + ::DispatchMessageW(&msg); + } + } + +LExit: + if (fRegistered) + { + ::UnregisterClassW(WIXCA_UITHREAD_CLASS_WINDOW, pContext->hInstance); + } + + return hr; +} + +static LRESULT CALLBACK WndProc( + __in HWND hWnd, + __in UINT uMsg, + __in WPARAM wParam, + __in LPARAM lParam + ) +{ + switch (uMsg) + { + case WM_QUERYENDSESSION: + // Prevent the process from being shut down. + WcaLog(LOGMSG_VERBOSE, "Disallowed system request to shut down the custom action server."); + return FALSE; + + case WM_DESTROY: + ::PostQuitMessage(0); + return 0; + } + + return ::DefWindowProcW(hWnd, uMsg, wParam, lParam); +} -- cgit v1.2.3-55-g6feb