diff options
Diffstat (limited to 'src/dutil/procutil.cpp')
-rw-r--r-- | src/dutil/procutil.cpp | 488 |
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 | ||
7 | static HRESULT CreatePipes( | ||
8 | __out HANDLE *phOutRead, | ||
9 | __out HANDLE *phOutWrite, | ||
10 | __out HANDLE *phErrWrite, | ||
11 | __out HANDLE *phInRead, | ||
12 | __out HANDLE *phInWrite | ||
13 | ); | ||
14 | |||
15 | static BOOL CALLBACK CloseWindowEnumCallback( | ||
16 | __in HWND hWnd, | ||
17 | __in LPARAM lParam | ||
18 | ); | ||
19 | |||
20 | |||
21 | extern "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 | |||
57 | LExit: | ||
58 | ReleaseHandle(hToken); | ||
59 | |||
60 | return hr; | ||
61 | } | ||
62 | |||
63 | extern "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 | |||
84 | LExit: | ||
85 | return hr; | ||
86 | } | ||
87 | |||
88 | extern "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 | |||
110 | LExit: | ||
111 | return hr; | ||
112 | } | ||
113 | |||
114 | extern "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 | |||
134 | LExit: | ||
135 | return hr; | ||
136 | } | ||
137 | |||
138 | |||
139 | extern "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 | |||
164 | LExit: | ||
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 | *******************************************************************/ | ||
177 | extern "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 | |||
251 | LExit: | ||
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 | *******************************************************************/ | ||
276 | extern "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 | |||
303 | LExit: | ||
304 | return hr; | ||
305 | } | ||
306 | |||
307 | /******************************************************************** | ||
308 | ProcWaitForIds() - waits for multiple processes to complete. | ||
309 | |||
310 | *******************************************************************/ | ||
311 | extern "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 | |||
345 | LExit: | ||
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 | *******************************************************************/ | ||
366 | extern "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 | |||
381 | LExit: | ||
382 | return hr; | ||
383 | } | ||
384 | |||
385 | |||
386 | static 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 | |||
456 | LExit: | ||
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 | *******************************************************************/ | ||
473 | static 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 | } | ||