aboutsummaryrefslogtreecommitdiff
path: root/src/ca/test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ca/test.cpp')
-rw-r--r--src/ca/test.cpp269
1 files changed, 269 insertions, 0 deletions
diff --git a/src/ca/test.cpp b/src/ca/test.cpp
new file mode 100644
index 00000000..c4d215f0
--- /dev/null
+++ b/src/ca/test.cpp
@@ -0,0 +1,269 @@
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#include "precomp.h"
4
5#define WIXCA_UITHREAD_CLASS_WINDOW L"WixCaMessageWindow"
6
7extern HMODULE g_hInstCADLL;
8
9
10// structs
11
12struct UITHREAD_CONTEXT
13{
14 HANDLE hInitializedEvent;
15 HINSTANCE hInstance;
16 HWND hWnd;
17};
18
19
20// internal function declarations
21
22static HRESULT CreateMessageWindow(
23 __out HWND* phWnd
24 );
25
26static void CloseMessageWindow(
27 __in HWND hWnd
28 );
29
30static DWORD WINAPI ThreadProc(
31 __in LPVOID pvContext
32 );
33
34static LRESULT CALLBACK WndProc(
35 __in HWND hWnd,
36 __in UINT uMsg,
37 __in WPARAM wParam,
38 __in LPARAM lParam
39 );
40
41
42/******************************************************************
43WixFailWhenDeferred - entry point for WixFailWhenDeferred
44 custom action which always fails when running as a deferred
45 custom action (otherwise it blindly succeeds). It's useful when
46 testing the rollback of deferred custom actions: Schedule it
47 immediately after the rollback/deferred CA pair you're testing
48 and it will fail, causing your rollback CA to get invoked.
49********************************************************************/
50extern "C" UINT __stdcall WixFailWhenDeferred(
51 __in MSIHANDLE hInstall
52 )
53{
54 return ::MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS;
55}
56
57/******************************************************************
58WixWaitForEvent - entry point for WixWaitForEvent custom action
59 which waits for either the WixWaitForEventFail or
60 WixWaitForEventSucceed named auto reset events. Signaling the
61 WixWaitForEventFail event will return ERROR_INSTALL_FAILURE or
62 signaling the WixWaitForEventSucceed event will return
63 ERROR_SUCCESS. Both events are declared in the Global\ namespace.
64********************************************************************/
65extern "C" UINT __stdcall WixWaitForEvent(
66 __in MSIHANDLE hInstall
67 )
68{
69 HRESULT hr = S_OK;
70 UINT er = ERROR_SUCCESS;
71 HWND hMessageWindow = NULL;
72 LPCWSTR wzSDDL = L"D:(A;;GA;;;WD)";
73 OS_VERSION version = OS_VERSION_UNKNOWN;
74 DWORD dwServicePack = 0;
75 PSECURITY_DESCRIPTOR pSD = NULL;
76 SECURITY_ATTRIBUTES sa = { };
77 HANDLE rghEvents[2];
78
79 hr = WcaInitialize(hInstall, "WixWaitForEvent");
80 ExitOnFailure(hr, "Failed to initialize.");
81
82 // Create a window to prevent shutdown requests.
83 hr = CreateMessageWindow(&hMessageWindow);
84 ExitOnFailure(hr, "Failed to create message window.");
85
86 // If running on Vista/2008 or newer use integrity enhancements.
87 OsGetVersion(&version, &dwServicePack);
88 if (OS_VERSION_VISTA <= version)
89 {
90 // Add SACL to allow Everyone to signal from a medium integrity level.
91 wzSDDL = L"D:(A;;GA;;;WD)S:(ML;;NW;;;ME)";
92 }
93
94 // Create the security descriptor and attributes for the events.
95 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSDDL, SDDL_REVISION_1, &pSD, NULL))
96 {
97 ExitWithLastError(hr, "Failed to create the security descriptor for the events.");
98 }
99
100 sa.nLength = sizeof(sa);
101 sa.lpSecurityDescriptor = pSD;
102 sa.bInheritHandle = FALSE;
103
104 rghEvents[0] = ::CreateEventW(&sa, FALSE, FALSE, L"Global\\WixWaitForEventFail");
105 ExitOnNullWithLastError(rghEvents[0], hr, "Failed to create the Global\\WixWaitForEventFail event.");
106
107 rghEvents[1] = ::CreateEventW(&sa, FALSE, FALSE, L"Global\\WixWaitForEventSucceed");
108 ExitOnNullWithLastError(rghEvents[1], hr, "Failed to create the Global\\WixWaitForEventSucceed event.");
109
110 // Wait for either of the events to be signaled and handle accordingly.
111 er = ::WaitForMultipleObjects(countof(rghEvents), rghEvents, FALSE, INFINITE);
112 switch (er)
113 {
114 case WAIT_OBJECT_0 + 0:
115 er = ERROR_INSTALL_FAILURE;
116 break;
117 case WAIT_OBJECT_0 + 1:
118 er = ERROR_SUCCESS;
119 break;
120 default:
121 ExitOnWin32Error(er, hr, "Unexpected failure.");
122 }
123
124LExit:
125 ReleaseHandle(rghEvents[1]);
126 ReleaseHandle(rghEvents[0]);
127
128 if (pSD)
129 {
130 ::LocalFree(pSD);
131 }
132
133 if (hMessageWindow)
134 {
135 CloseMessageWindow(hMessageWindow);
136 }
137
138 if (FAILED(hr))
139 {
140 er = ERROR_INSTALL_FAILURE;
141 }
142
143 return WcaFinalize(er);
144}
145
146
147// internal function definitions
148
149static HRESULT CreateMessageWindow(
150 __out HWND* phWnd
151 )
152{
153 HRESULT hr = S_OK;
154 HANDLE rgWaitHandles[2] = { };
155 UITHREAD_CONTEXT context = { };
156
157 // Create event to signal after the UI thread / window is initialized.
158 rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
159 ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event.");
160
161 // Pass necessary information to create the window.
162 context.hInitializedEvent = rgWaitHandles[0];
163 context.hInstance = (HINSTANCE)g_hInstCADLL;
164
165 // Create our separate UI thread.
166 rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL);
167 ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread.");
168
169 // Wait for either the thread to be initialized or the window to exit / fail prematurely.
170 ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE);
171
172 // Pass the window back to the caller.
173 *phWnd = context.hWnd;
174
175LExit:
176 ReleaseHandle(rgWaitHandles[1]);
177 ReleaseHandle(rgWaitHandles[0]);
178
179 return hr;
180}
181
182static void CloseMessageWindow(
183 __in HWND hWnd
184 )
185{
186 if (::IsWindow(hWnd))
187 {
188 ::PostMessageW(hWnd, WM_CLOSE, 0, 0);
189 }
190}
191
192static DWORD WINAPI ThreadProc(
193 __in LPVOID pvContext
194 )
195{
196 HRESULT hr = S_OK;
197 UITHREAD_CONTEXT* pContext = static_cast<UITHREAD_CONTEXT*>(pvContext);
198
199 WNDCLASSW wc = { };
200 BOOL fRegistered = TRUE;
201 HWND hWnd = NULL;
202
203 BOOL fRet = FALSE;
204 MSG msg = { };
205
206 wc.lpfnWndProc = WndProc;
207 wc.hInstance = pContext->hInstance;
208 wc.lpszClassName = WIXCA_UITHREAD_CLASS_WINDOW;
209
210 if (!::RegisterClassW(&wc))
211 {
212 ExitWithLastError(hr, "Failed to register window.");
213 }
214
215 fRegistered = TRUE;
216
217 // Create the window to handle reboots without activating it.
218 hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, NULL, WS_POPUP | WS_VISIBLE, CW_USEDEFAULT, SW_SHOWNA, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, NULL);
219 ExitOnNullWithLastError(hWnd, hr, "Failed to create window.");
220
221 // Persist the window handle and let the caller know we've initialized.
222 pContext->hWnd = hWnd;
223 ::SetEvent(pContext->hInitializedEvent);
224
225 // Pump messages until the window is closed.
226 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
227 {
228 if (-1 == fRet)
229 {
230 hr = E_UNEXPECTED;
231 ExitOnFailure(hr, "Unexpected return value from message pump.");
232 }
233 else if (!::IsDialogMessageW(msg.hwnd, &msg))
234 {
235 ::TranslateMessage(&msg);
236 ::DispatchMessageW(&msg);
237 }
238 }
239
240LExit:
241 if (fRegistered)
242 {
243 ::UnregisterClassW(WIXCA_UITHREAD_CLASS_WINDOW, pContext->hInstance);
244 }
245
246 return hr;
247}
248
249static LRESULT CALLBACK WndProc(
250 __in HWND hWnd,
251 __in UINT uMsg,
252 __in WPARAM wParam,
253 __in LPARAM lParam
254 )
255{
256 switch (uMsg)
257 {
258 case WM_QUERYENDSESSION:
259 // Prevent the process from being shut down.
260 WcaLog(LOGMSG_VERBOSE, "Disallowed system request to shut down the custom action server.");
261 return FALSE;
262
263 case WM_DESTROY:
264 ::PostQuitMessage(0);
265 return 0;
266 }
267
268 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
269}