diff options
Diffstat (limited to 'src/ca/test.cpp')
-rw-r--r-- | src/ca/test.cpp | 269 |
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 | |||
7 | extern HMODULE g_hInstCADLL; | ||
8 | |||
9 | |||
10 | // structs | ||
11 | |||
12 | struct UITHREAD_CONTEXT | ||
13 | { | ||
14 | HANDLE hInitializedEvent; | ||
15 | HINSTANCE hInstance; | ||
16 | HWND hWnd; | ||
17 | }; | ||
18 | |||
19 | |||
20 | // internal function declarations | ||
21 | |||
22 | static HRESULT CreateMessageWindow( | ||
23 | __out HWND* phWnd | ||
24 | ); | ||
25 | |||
26 | static void CloseMessageWindow( | ||
27 | __in HWND hWnd | ||
28 | ); | ||
29 | |||
30 | static DWORD WINAPI ThreadProc( | ||
31 | __in LPVOID pvContext | ||
32 | ); | ||
33 | |||
34 | static LRESULT CALLBACK WndProc( | ||
35 | __in HWND hWnd, | ||
36 | __in UINT uMsg, | ||
37 | __in WPARAM wParam, | ||
38 | __in LPARAM lParam | ||
39 | ); | ||
40 | |||
41 | |||
42 | /****************************************************************** | ||
43 | WixFailWhenDeferred - 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 | ********************************************************************/ | ||
50 | extern "C" UINT __stdcall WixFailWhenDeferred( | ||
51 | __in MSIHANDLE hInstall | ||
52 | ) | ||
53 | { | ||
54 | return ::MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS; | ||
55 | } | ||
56 | |||
57 | /****************************************************************** | ||
58 | WixWaitForEvent - 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 | ********************************************************************/ | ||
65 | extern "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 | |||
124 | LExit: | ||
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 | |||
149 | static 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 | |||
175 | LExit: | ||
176 | ReleaseHandle(rgWaitHandles[1]); | ||
177 | ReleaseHandle(rgWaitHandles[0]); | ||
178 | |||
179 | return hr; | ||
180 | } | ||
181 | |||
182 | static void CloseMessageWindow( | ||
183 | __in HWND hWnd | ||
184 | ) | ||
185 | { | ||
186 | if (::IsWindow(hWnd)) | ||
187 | { | ||
188 | ::PostMessageW(hWnd, WM_CLOSE, 0, 0); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | static 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 | |||
240 | LExit: | ||
241 | if (fRegistered) | ||
242 | { | ||
243 | ::UnregisterClassW(WIXCA_UITHREAD_CLASS_WINDOW, pContext->hInstance); | ||
244 | } | ||
245 | |||
246 | return hr; | ||
247 | } | ||
248 | |||
249 | static 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 | } | ||