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