aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/procutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/procutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/procutil.cpp522
1 files changed, 522 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/procutil.cpp b/src/libs/dutil/WixToolset.DUtil/procutil.cpp
new file mode 100644
index 00000000..6bfe5017
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/procutil.cpp
@@ -0,0 +1,522 @@
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
6// Exit macros
7#define ProcExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
8#define ProcExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
9#define ProcExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
10#define ProcExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
11#define ProcExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
12#define ProcExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_PROCUTIL, x, s, __VA_ARGS__)
13#define ProcExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__)
14#define ProcExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__)
15#define ProcExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_PROCUTIL, p, x, e, s, __VA_ARGS__)
16#define ProcExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_PROCUTIL, p, x, s, __VA_ARGS__)
17#define ProcExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_PROCUTIL, e, x, s, __VA_ARGS__)
18#define ProcExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_PROCUTIL, g, x, s, __VA_ARGS__)
19
20
21// private functions
22static HRESULT CreatePipes(
23 __out HANDLE *phOutRead,
24 __out HANDLE *phOutWrite,
25 __out HANDLE *phErrWrite,
26 __out HANDLE *phInRead,
27 __out HANDLE *phInWrite
28 );
29
30static BOOL CALLBACK CloseWindowEnumCallback(
31 __in HWND hWnd,
32 __in LPARAM lParam
33 );
34
35
36extern "C" HRESULT DAPI ProcElevated(
37 __in HANDLE hProcess,
38 __out BOOL* pfElevated
39 )
40{
41 HRESULT hr = S_OK;
42 HANDLE hToken = NULL;
43 TOKEN_ELEVATION tokenElevated = { };
44 DWORD cbToken = 0;
45
46 if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
47 {
48 ProcExitWithLastError(hr, "Failed to open process token.");
49 }
50
51 if (::GetTokenInformation(hToken, TokenElevation, &tokenElevated, sizeof(TOKEN_ELEVATION), &cbToken))
52 {
53 *pfElevated = (0 != tokenElevated.TokenIsElevated);
54 }
55 else
56 {
57 DWORD er = ::GetLastError();
58 hr = HRESULT_FROM_WIN32(er);
59
60 // If it's invalid argument, this means the OS doesn't support TokenElevation, so we're not elevated.
61 if (E_INVALIDARG == hr)
62 {
63 *pfElevated = FALSE;
64 hr = S_OK;
65 }
66 else
67 {
68 ProcExitOnRootFailure(hr, "Failed to get elevation token from process.");
69 }
70 }
71
72LExit:
73 ReleaseHandle(hToken);
74
75 return hr;
76}
77
78extern "C" HRESULT DAPI ProcWow64(
79 __in HANDLE hProcess,
80 __out BOOL* pfWow64
81 )
82{
83 HRESULT hr = S_OK;
84 BOOL fIsWow64 = FALSE;
85
86 typedef BOOL(WINAPI* LPFN_ISWOW64PROCESS2)(HANDLE, USHORT *, USHORT *);
87 LPFN_ISWOW64PROCESS2 pfnIsWow64Process2 = (LPFN_ISWOW64PROCESS2)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process2");
88
89 if (pfnIsWow64Process2)
90 {
91 USHORT pProcessMachine = IMAGE_FILE_MACHINE_UNKNOWN;
92 if (!pfnIsWow64Process2(hProcess, &pProcessMachine, nullptr))
93 {
94 ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process2.");
95 }
96
97 if (pProcessMachine != IMAGE_FILE_MACHINE_UNKNOWN)
98 {
99 fIsWow64 = TRUE;
100 }
101 }
102 else
103 {
104 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
105 LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "IsWow64Process");
106
107 if (pfnIsWow64Process)
108 {
109 if (!pfnIsWow64Process(hProcess, &fIsWow64))
110 {
111 ProcExitWithLastError(hr, "Failed to check WOW64 process - IsWow64Process.");
112 }
113 }
114 }
115
116 *pfWow64 = fIsWow64;
117
118LExit:
119 return hr;
120}
121
122extern "C" HRESULT DAPI ProcDisableWowFileSystemRedirection(
123 __in PROC_FILESYSTEMREDIRECTION* pfsr
124 )
125{
126 AssertSz(!pfsr->fDisabled, "File system redirection was already disabled.");
127 HRESULT hr = S_OK;
128
129 typedef BOOL (WINAPI *LPFN_Wow64DisableWow64FsRedirection)(PVOID *);
130 LPFN_Wow64DisableWow64FsRedirection pfnWow64DisableWow64FsRedirection = (LPFN_Wow64DisableWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64DisableWow64FsRedirection");
131
132 if (!pfnWow64DisableWow64FsRedirection)
133 {
134 ExitFunction1(hr = E_NOTIMPL);
135 }
136
137 if (!pfnWow64DisableWow64FsRedirection(&pfsr->pvRevertState))
138 {
139 ProcExitWithLastError(hr, "Failed to disable file system redirection.");
140 }
141
142 pfsr->fDisabled = TRUE;
143
144LExit:
145 return hr;
146}
147
148extern "C" HRESULT DAPI ProcRevertWowFileSystemRedirection(
149 __in PROC_FILESYSTEMREDIRECTION* pfsr
150 )
151{
152 HRESULT hr = S_OK;
153
154 if (pfsr->fDisabled)
155 {
156 typedef BOOL (WINAPI *LPFN_Wow64RevertWow64FsRedirection)(PVOID);
157 LPFN_Wow64RevertWow64FsRedirection pfnWow64RevertWow64FsRedirection = (LPFN_Wow64RevertWow64FsRedirection)::GetProcAddress(::GetModuleHandleW(L"kernel32"), "Wow64RevertWow64FsRedirection");
158
159 if (!pfnWow64RevertWow64FsRedirection(pfsr->pvRevertState))
160 {
161 ProcExitWithLastError(hr, "Failed to revert file system redirection.");
162 }
163
164 pfsr->fDisabled = FALSE;
165 pfsr->pvRevertState = NULL;
166 }
167
168LExit:
169 return hr;
170}
171
172
173extern "C" HRESULT DAPI ProcExec(
174 __in_z LPCWSTR wzExecutablePath,
175 __in_z_opt LPCWSTR wzCommandLine,
176 __in int nCmdShow,
177 __out HANDLE *phProcess
178 )
179{
180 HRESULT hr = S_OK;
181 LPWSTR sczFullCommandLine = NULL;
182 STARTUPINFOW si = { };
183 PROCESS_INFORMATION pi = { };
184
185 hr = StrAllocFormatted(&sczFullCommandLine, L"\"%ls\" %ls", wzExecutablePath, wzCommandLine ? wzCommandLine : L"");
186 ProcExitOnFailure(hr, "Failed to allocate full command-line.");
187
188 si.cb = sizeof(si);
189 si.wShowWindow = static_cast<WORD>(nCmdShow);
190 if (!::CreateProcessW(wzExecutablePath, sczFullCommandLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi))
191 {
192 ProcExitWithLastError(hr, "Failed to create process: %ls", sczFullCommandLine);
193 }
194
195 *phProcess = pi.hProcess;
196 pi.hProcess = NULL;
197
198LExit:
199 ReleaseHandle(pi.hThread);
200 ReleaseHandle(pi.hProcess);
201 ReleaseStr(sczFullCommandLine);
202
203 return hr;
204}
205
206
207/********************************************************************
208 ProcExecute() - executes a command-line.
209
210*******************************************************************/
211extern "C" HRESULT DAPI ProcExecute(
212 __in_z LPWSTR wzCommand,
213 __out HANDLE *phProcess,
214 __out_opt HANDLE *phChildStdIn,
215 __out_opt HANDLE *phChildStdOutErr
216 )
217{
218 HRESULT hr = S_OK;
219
220 PROCESS_INFORMATION pi = { };
221 STARTUPINFOW si = { };
222
223 HANDLE hOutRead = INVALID_HANDLE_VALUE;
224 HANDLE hOutWrite = INVALID_HANDLE_VALUE;
225 HANDLE hErrWrite = INVALID_HANDLE_VALUE;
226 HANDLE hInRead = INVALID_HANDLE_VALUE;
227 HANDLE hInWrite = INVALID_HANDLE_VALUE;
228
229 // Create redirect pipes.
230 hr = CreatePipes(&hOutRead, &hOutWrite, &hErrWrite, &hInRead, &hInWrite);
231 ProcExitOnFailure(hr, "failed to create output pipes");
232
233 // Set up startup structure.
234 si.cb = sizeof(STARTUPINFOW);
235 si.dwFlags = STARTF_USESTDHANDLES;
236 si.hStdInput = hInRead;
237 si.hStdOutput = hOutWrite;
238 si.hStdError = hErrWrite;
239
240#pragma prefast(push)
241#pragma prefast(disable:25028)
242 if (::CreateProcessW(NULL,
243 wzCommand, // command line
244 NULL, // security info
245 NULL, // thread info
246 TRUE, // inherit handles
247 ::GetPriorityClass(::GetCurrentProcess()) | CREATE_NO_WINDOW, // creation flags
248 NULL, // environment
249 NULL, // cur dir
250 &si,
251 &pi))
252#pragma prefast(pop)
253 {
254 // Close child process output/input handles so child doesn't hang
255 // while waiting for input from parent process.
256 ::CloseHandle(hOutWrite);
257 hOutWrite = INVALID_HANDLE_VALUE;
258
259 ::CloseHandle(hErrWrite);
260 hErrWrite = INVALID_HANDLE_VALUE;
261
262 ::CloseHandle(hInRead);
263 hInRead = INVALID_HANDLE_VALUE;
264 }
265 else
266 {
267 ProcExitWithLastError(hr, "Process failed to execute.");
268 }
269
270 *phProcess = pi.hProcess;
271 pi.hProcess = 0;
272
273 if (phChildStdIn)
274 {
275 *phChildStdIn = hInWrite;
276 hInWrite = INVALID_HANDLE_VALUE;
277 }
278
279 if (phChildStdOutErr)
280 {
281 *phChildStdOutErr = hOutRead;
282 hOutRead = INVALID_HANDLE_VALUE;
283 }
284
285LExit:
286 if (pi.hThread)
287 {
288 ::CloseHandle(pi.hThread);
289 }
290
291 if (pi.hProcess)
292 {
293 ::CloseHandle(pi.hProcess);
294 }
295
296 ReleaseFileHandle(hOutRead);
297 ReleaseFileHandle(hOutWrite);
298 ReleaseFileHandle(hErrWrite);
299 ReleaseFileHandle(hInRead);
300 ReleaseFileHandle(hInWrite);
301
302 return hr;
303}
304
305
306/********************************************************************
307 ProcWaitForCompletion() - waits for process to complete and gets return code.
308
309*******************************************************************/
310extern "C" HRESULT DAPI ProcWaitForCompletion(
311 __in HANDLE hProcess,
312 __in DWORD dwTimeout,
313 __out DWORD *pReturnCode
314 )
315{
316 HRESULT hr = S_OK;
317 DWORD er = ERROR_SUCCESS;
318
319 // Wait for everything to finish
320 er = ::WaitForSingleObject(hProcess, dwTimeout);
321 if (WAIT_FAILED == er)
322 {
323 ProcExitWithLastError(hr, "Failed to wait for process to complete.");
324 }
325 else if (WAIT_TIMEOUT == er)
326 {
327 ExitFunction1(hr = HRESULT_FROM_WIN32(er));
328 }
329
330 if (!::GetExitCodeProcess(hProcess, &er))
331 {
332 ProcExitWithLastError(hr, "Failed to get process return code.");
333 }
334
335 *pReturnCode = er;
336
337LExit:
338 return hr;
339}
340
341/********************************************************************
342 ProcWaitForIds() - waits for multiple processes to complete.
343
344*******************************************************************/
345extern "C" HRESULT DAPI ProcWaitForIds(
346 __in_ecount(cProcessIds) const DWORD rgdwProcessIds[],
347 __in DWORD cProcessIds,
348 __in DWORD dwMilliseconds
349 )
350{
351 HRESULT hr = S_OK;
352 DWORD er = ERROR_SUCCESS;
353 HANDLE hProcess = NULL;
354 HANDLE * rghProcesses = NULL;
355 DWORD cProcesses = 0;
356
357 rghProcesses = static_cast<HANDLE*>(MemAlloc(sizeof(DWORD) * cProcessIds, TRUE));
358 ProcExitOnNull(rgdwProcessIds, hr, E_OUTOFMEMORY, "Failed to allocate array for process ID Handles.");
359
360 for (DWORD i = 0; i < cProcessIds; ++i)
361 {
362 hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, rgdwProcessIds[i]);
363 if (hProcess != NULL)
364 {
365 rghProcesses[cProcesses++] = hProcess;
366 }
367 }
368
369 er = ::WaitForMultipleObjects(cProcesses, rghProcesses, TRUE, dwMilliseconds);
370 if (WAIT_FAILED == er)
371 {
372 ProcExitWithLastError(hr, "Failed to wait for process to complete.");
373 }
374 else if (WAIT_TIMEOUT == er)
375 {
376 ProcExitOnWin32Error(er, hr, "Timed out while waiting for process to complete.");
377 }
378
379LExit:
380 if (rghProcesses)
381 {
382 for (DWORD i = 0; i < cProcesses; ++i)
383 {
384 if (NULL != rghProcesses[i])
385 {
386 ::CloseHandle(rghProcesses[i]);
387 }
388 }
389
390 MemFree(rghProcesses);
391 }
392
393 return hr;
394}
395
396/********************************************************************
397 ProcCloseIds() - sends WM_CLOSE messages to all process ids.
398
399*******************************************************************/
400extern "C" HRESULT DAPI ProcCloseIds(
401 __in_ecount(cProcessIds) const DWORD* pdwProcessIds,
402 __in DWORD cProcessIds
403 )
404{
405 HRESULT hr = S_OK;
406
407 for (DWORD i = 0; i < cProcessIds; ++i)
408 {
409 if (!::EnumWindows(&CloseWindowEnumCallback, pdwProcessIds[i]))
410 {
411 ProcExitWithLastError(hr, "Failed to enumerate windows.");
412 }
413 }
414
415LExit:
416 return hr;
417}
418
419
420static HRESULT CreatePipes(
421 __out HANDLE *phOutRead,
422 __out HANDLE *phOutWrite,
423 __out HANDLE *phErrWrite,
424 __out HANDLE *phInRead,
425 __out HANDLE *phInWrite
426 )
427{
428 HRESULT hr = S_OK;
429 SECURITY_ATTRIBUTES sa;
430 HANDLE hOutTemp = INVALID_HANDLE_VALUE;
431 HANDLE hInTemp = INVALID_HANDLE_VALUE;
432
433 HANDLE hOutRead = INVALID_HANDLE_VALUE;
434 HANDLE hOutWrite = INVALID_HANDLE_VALUE;
435 HANDLE hErrWrite = INVALID_HANDLE_VALUE;
436 HANDLE hInRead = INVALID_HANDLE_VALUE;
437 HANDLE hInWrite = INVALID_HANDLE_VALUE;
438
439 // Fill out security structure so we can inherit handles
440 ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
441 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
442 sa.bInheritHandle = TRUE;
443 sa.lpSecurityDescriptor = NULL;
444
445 // Create pipes
446 if (!::CreatePipe(&hOutTemp, &hOutWrite, &sa, 0))
447 {
448 ProcExitWithLastError(hr, "failed to create output pipe");
449 }
450
451 if (!::CreatePipe(&hInRead, &hInTemp, &sa, 0))
452 {
453 ProcExitWithLastError(hr, "failed to create input pipe");
454 }
455
456 // Duplicate output pipe so standard error and standard output write to the same pipe.
457 if (!::DuplicateHandle(::GetCurrentProcess(), hOutWrite, ::GetCurrentProcess(), &hErrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS))
458 {
459 ProcExitWithLastError(hr, "failed to duplicate write handle");
460 }
461
462 // We need to create new "output read" and "input write" handles that are non inheritable. Otherwise CreateProcess will creates handles in
463 // the child process that can't be closed.
464 if (!::DuplicateHandle(::GetCurrentProcess(), hOutTemp, ::GetCurrentProcess(), &hOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS))
465 {
466 ProcExitWithLastError(hr, "failed to duplicate output pipe");
467 }
468
469 if (!::DuplicateHandle(::GetCurrentProcess(), hInTemp, ::GetCurrentProcess(), &hInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS))
470 {
471 ProcExitWithLastError(hr, "failed to duplicate input pipe");
472 }
473
474 // now that everything has succeeded, assign to the outputs
475 *phOutRead = hOutRead;
476 hOutRead = INVALID_HANDLE_VALUE;
477
478 *phOutWrite = hOutWrite;
479 hOutWrite = INVALID_HANDLE_VALUE;
480
481 *phErrWrite = hErrWrite;
482 hErrWrite = INVALID_HANDLE_VALUE;
483
484 *phInRead = hInRead;
485 hInRead = INVALID_HANDLE_VALUE;
486
487 *phInWrite = hInWrite;
488 hInWrite = INVALID_HANDLE_VALUE;
489
490LExit:
491 ReleaseFileHandle(hOutRead);
492 ReleaseFileHandle(hOutWrite);
493 ReleaseFileHandle(hErrWrite);
494 ReleaseFileHandle(hInRead);
495 ReleaseFileHandle(hInWrite);
496 ReleaseFileHandle(hOutTemp);
497 ReleaseFileHandle(hInTemp);
498
499 return hr;
500}
501
502
503/********************************************************************
504 CloseWindowEnumCallback() - outputs trace and log info
505
506*******************************************************************/
507static BOOL CALLBACK CloseWindowEnumCallback(
508 __in HWND hWnd,
509 __in LPARAM lParam
510 )
511{
512 DWORD dwPid = static_cast<DWORD>(lParam);
513 DWORD dwProcessId = 0;
514
515 ::GetWindowThreadProcessId(hWnd, &dwProcessId);
516 if (dwPid == dwProcessId)
517 {
518 ::SendMessageW(hWnd, WM_CLOSE, 0, 0);
519 }
520
521 return TRUE;
522}