VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/process-win.cpp@ 37043

Last change on this file since 37043 was 34708, checked in by vboxsync, 14 years ago

IPRT/RTProc: Added RTPROC_FLAGS_HIDDEN for starting hidden processes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 48.3 KB
Line 
1/* $Id: process-win.cpp 34708 2010-12-03 17:30:45Z vboxsync $ */
2/** @file
3 * IPRT - Process, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#define LOG_GROUP RTLOGGROUP_PROCESS
32
33#include <Userenv.h>
34#include <Windows.h>
35#include <tlhelp32.h>
36#include <process.h>
37#include <errno.h>
38#include <Strsafe.h>
39
40#include <iprt/process.h>
41#include "internal/iprt.h"
42
43#include <iprt/assert.h>
44#include <iprt/critsect.h>
45#include <iprt/file.h>
46#include <iprt/err.h>
47#include <iprt/env.h>
48#include <iprt/getopt.h>
49#include <iprt/initterm.h>
50#include <iprt/ldr.h>
51#include <iprt/mem.h>
52#include <iprt/once.h>
53#include <iprt/pipe.h>
54#include <iprt/string.h>
55#include <iprt/socket.h>
56
57
58/*******************************************************************************
59* Structures and Typedefs *
60*******************************************************************************/
61typedef WINADVAPI BOOL WINAPI FNCREATEPROCESSWITHLOGON(LPCWSTR,
62 LPCWSTR,
63 LPCWSTR,
64 DWORD,
65 LPCWSTR,
66 LPWSTR,
67 DWORD,
68 LPVOID,
69 LPCWSTR,
70 LPSTARTUPINFOW,
71 LPPROCESS_INFORMATION);
72typedef FNCREATEPROCESSWITHLOGON *PFNCREATEPROCESSWITHLOGON;
73
74typedef DWORD WINAPI FNWTSGETACTIVECONSOLESESSIONID();
75typedef FNWTSGETACTIVECONSOLESESSIONID *PFNWTSGETACTIVECONSOLESESSIONID;
76
77typedef HANDLE WINAPI FNCREATETOOLHELP32SNAPSHOT(DWORD, DWORD);
78typedef FNCREATETOOLHELP32SNAPSHOT *PFNCREATETOOLHELP32SNAPSHOT;
79
80typedef BOOL WINAPI FNPROCESS32FIRST(HANDLE, LPPROCESSENTRY32);
81typedef FNPROCESS32FIRST *PFNPROCESS32FIRST;
82
83typedef BOOL WINAPI FNPROCESS32NEXT(HANDLE, LPPROCESSENTRY32);
84typedef FNPROCESS32NEXT *PFNPROCESS32NEXT;
85
86typedef BOOL WINAPI FNENUMPROCESSES(DWORD*, DWORD, DWORD*);
87typedef FNENUMPROCESSES *PFNENUMPROCESSES;
88
89typedef DWORD FNGETMODULEBASENAME(HANDLE, HMODULE, LPTSTR, DWORD);
90typedef FNGETMODULEBASENAME *PFNGETMODULEBASENAME;
91
92typedef BOOL WINAPI FNCREATEENVIRONMENTBLOCK(LPVOID *, HANDLE, BOOL);
93typedef FNCREATEENVIRONMENTBLOCK *PFNCREATEENVIRONMENTBLOCK;
94
95typedef BOOL WINAPI FNPFNDESTROYENVIRONMENTBLOCK(LPVOID);
96typedef FNPFNDESTROYENVIRONMENTBLOCK *PFNPFNDESTROYENVIRONMENTBLOCK;
97
98typedef BOOL WINAPI FNLOADUSERPROFILEW(HANDLE, LPPROFILEINFOW);
99typedef FNLOADUSERPROFILEW *PFNLOADUSERPROFILEW;
100
101typedef BOOL WINAPI FNUNLOADUSERPROFILE(HANDLE, HANDLE);
102typedef FNUNLOADUSERPROFILE *PFNUNLOADUSERPROFILE;
103
104
105/*******************************************************************************
106* Global Variables *
107*******************************************************************************/
108/** Init once structure. */
109static RTONCE g_rtProcWinInitOnce = RTONCE_INITIALIZER;
110/** Critical section protecting the process array. */
111static RTCRITSECT g_CritSect;
112/** The number of processes in the array. */
113static uint32_t g_cProcesses;
114/** The current allocation size. */
115static uint32_t g_cProcessesAlloc;
116/** Array containing the live or non-reaped child processes. */
117static struct RTPROCWINENTRY
118{
119 /** The process ID. */
120 ULONG_PTR pid;
121 /** The process handle. */
122 HANDLE hProcess;
123} *g_paProcesses;
124
125
126/**
127 * Clean up the globals.
128 *
129 * @param enmReason Ignored.
130 * @param iStatus Ignored.
131 * @param pvUser Ignored.
132 */
133static DECLCALLBACK(void) rtProcWinTerm(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
134{
135 NOREF(pvUser); NOREF(iStatus); NOREF(enmReason);
136
137 RTCritSectDelete(&g_CritSect);
138
139 size_t i = g_cProcesses;
140 while (i-- > 0)
141 {
142 CloseHandle(g_paProcesses[i].hProcess);
143 g_paProcesses[i].hProcess = NULL;
144 }
145 RTMemFree(g_paProcesses);
146
147 g_paProcesses = NULL;
148 g_cProcesses = 0;
149 g_cProcessesAlloc = 0;
150}
151
152
153/**
154 * Initialize the globals.
155 *
156 * @returns IPRT status code.
157 * @param pvUser1 Ignored.
158 * @param pvUser2 Ignored.
159 */
160static DECLCALLBACK(int32_t) rtProcWinInitOnce(void *pvUser1, void *pvUser2)
161{
162 NOREF(pvUser1); NOREF(pvUser2);
163
164 g_cProcesses = 0;
165 g_cProcessesAlloc = 0;
166 g_paProcesses = NULL;
167 int rc = RTCritSectInit(&g_CritSect);
168 if (RT_SUCCESS(rc))
169 {
170 /** @todo init once, terminate once - this is a generic thing which should
171 * have some kind of static and simpler setup! */
172 rc = RTTermRegisterCallback(rtProcWinTerm, NULL);
173 if (RT_SUCCESS(rc))
174 return rc;
175 RTCritSectDelete(&g_CritSect);
176 }
177 return rc;
178}
179
180
181/**
182 * Gets the process handle for a process from g_paProcesses.
183 *
184 * @returns Process handle if found, NULL if not.
185 * @param pid The process to remove (pid).
186 */
187static HANDLE rtProcWinFindPid(RTPROCESS pid)
188{
189 HANDLE hProcess = NULL;
190
191 RTCritSectEnter(&g_CritSect);
192 uint32_t i = g_cProcesses;
193 while (i-- > 0)
194 if (g_paProcesses[i].pid == pid)
195 {
196 hProcess = g_paProcesses[i].hProcess;
197 break;
198 }
199 RTCritSectLeave(&g_CritSect);
200
201 return hProcess;
202}
203
204
205/**
206 * Removes a process from g_paProcesses.
207 *
208 * @param pid The process to remove (pid).
209 */
210static void rtProcWinRemovePid(RTPROCESS pid)
211{
212 RTCritSectEnter(&g_CritSect);
213 uint32_t i = g_cProcesses;
214 while (i-- > 0)
215 if (g_paProcesses[i].pid == pid)
216 {
217 g_cProcesses--;
218 uint32_t cToMove = g_cProcesses - i;
219 if (cToMove)
220 memmove(&g_paProcesses[i], &g_paProcesses[i + 1], cToMove * sizeof(g_paProcesses[0]));
221 break;
222 }
223 RTCritSectLeave(&g_CritSect);
224}
225
226
227/**
228 * Adds a process to g_paProcesses.
229 *
230 * @returns IPRT status code.
231 * @param pid The process id.
232 * @param hProcess The process handle.
233 */
234static int rtProcWinAddPid(RTPROCESS pid, HANDLE hProcess)
235{
236 RTCritSectEnter(&g_CritSect);
237
238 uint32_t i = g_cProcesses;
239 if (i >= g_cProcessesAlloc)
240 {
241 void *pvNew = RTMemRealloc(g_paProcesses, (i + 16) * sizeof(g_paProcesses[0]));
242 if (RT_UNLIKELY(!pvNew))
243 {
244 RTCritSectLeave(&g_CritSect);
245 return VERR_NO_MEMORY;
246 }
247 g_paProcesses = (struct RTPROCWINENTRY *)pvNew;
248 g_cProcessesAlloc = i + 16;
249 }
250
251 g_paProcesses[i].pid = pid;
252 g_paProcesses[i].hProcess = hProcess;
253 g_cProcesses = i + 1;
254
255 RTCritSectLeave(&g_CritSect);
256 return VINF_SUCCESS;
257}
258
259
260RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
261{
262 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
263 NULL, NULL, NULL, /* standard handles */
264 NULL /*pszAsUser*/, NULL /* pszPassword*/,
265 pProcess);
266}
267
268
269/**
270 * Map some important or much used Windows error codes
271 * to our error codes.
272 *
273 * @return Mapped IPRT status code.
274 * @param dwError Windows error code to map to IPRT code.
275 */
276static int rtProcMapErrorCodes(DWORD dwError)
277{
278 int rc;
279 switch (dwError)
280 {
281 case ERROR_NOACCESS:
282 case ERROR_PRIVILEGE_NOT_HELD:
283 rc = VERR_PERMISSION_DENIED;
284 break;
285
286 case ERROR_PASSWORD_EXPIRED:
287 case ERROR_ACCOUNT_RESTRICTION: /* See: http://support.microsoft.com/kb/303846/ */
288 case ERROR_PASSWORD_RESTRICTION:
289 rc = VERR_AUTHENTICATION_FAILURE;
290 break;
291
292 default:
293 /* Could trigger a debug assertion! */
294 rc = RTErrConvertFromWin32(dwError);
295 break;
296 }
297 return rc;
298}
299
300
301/**
302 * Get the process token (not the process handle like the name might indicate)
303 * of the process indicated by @a dwPID if the @a pSID matches.
304 *
305 * @returns IPRT status code.
306 *
307 * @param dwPID The process identifier.
308 * @param pSID The secure identifier of the user.
309 * @param phToken Where to return the token handle - duplicate,
310 * caller closes it on success.
311 */
312static int rtProcGetProcessHandle(DWORD dwPID, PSID pSID, PHANDLE phToken)
313{
314 AssertPtr(pSID);
315 AssertPtr(phToken);
316
317 DWORD dwErr;
318 BOOL fRc;
319 bool fFound = false;
320 HANDLE hProc = OpenProcess(MAXIMUM_ALLOWED, TRUE, dwPID);
321 if (hProc != NULL)
322 {
323 HANDLE hTokenProc;
324 fRc = OpenProcessToken(hProc,
325 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE
326 | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
327 &hTokenProc);
328 if (fRc)
329 {
330 DWORD dwSize = 0;
331 fRc = GetTokenInformation(hTokenProc, TokenUser, NULL, 0, &dwSize);
332 if (!fRc)
333 dwErr = GetLastError();
334 if ( !fRc
335 && dwErr == ERROR_INSUFFICIENT_BUFFER
336 && dwSize > 0)
337 {
338 PTOKEN_USER pTokenUser = (PTOKEN_USER)RTMemAlloc(dwSize);
339 AssertPtrReturn(pTokenUser, VERR_NO_MEMORY);
340 RT_ZERO(*pTokenUser);
341 if ( GetTokenInformation(hTokenProc,
342 TokenUser,
343 (LPVOID)pTokenUser,
344 dwSize,
345 &dwSize))
346 {
347 if ( IsValidSid(pTokenUser->User.Sid)
348 && EqualSid(pTokenUser->User.Sid, pSID))
349 {
350 if (DuplicateTokenEx(hTokenProc, MAXIMUM_ALLOWED,
351 NULL, SecurityIdentification, TokenPrimary, phToken))
352 {
353 /*
354 * So we found the process instance which belongs to the user we want to
355 * to run our new process under. This duplicated token will be used for
356 * the actual CreateProcessAsUserW() call then.
357 */
358 fFound = true;
359 }
360 else
361 dwErr = GetLastError();
362 }
363 }
364 else
365 dwErr = GetLastError();
366 RTMemFree(pTokenUser);
367 }
368 else
369 dwErr = GetLastError();
370 CloseHandle(hTokenProc);
371 }
372 else
373 dwErr = GetLastError();
374 CloseHandle(hProc);
375 }
376 else
377 dwErr = GetLastError();
378 if (fFound)
379 return VINF_SUCCESS;
380 if (dwErr != NO_ERROR)
381 return RTErrConvertFromWin32(dwErr);
382 return VERR_NOT_FOUND; /* No error occurred, but we didn't find the right process. */
383}
384
385
386/**
387 * Finds a one of the processes in @a papszNames running with user @a pSID and
388 * returns a duplicate handle to its token.
389 *
390 * @returns Success indicator.
391 * @param papszNames The process candidates, in prioritized order.
392 * @param pSID The secure identifier of the user.
393 * @param phToken Where to return the token handle - duplicate,
394 * caller closes it on success.
395 */
396static bool rtProcFindProcessByName(const char * const *papszNames, PSID pSID, PHANDLE phToken)
397{
398 AssertPtr(papszNames);
399 AssertPtr(pSID);
400 AssertPtr(phToken);
401
402 DWORD dwErr = NO_ERROR;
403 bool fFound = false;
404
405 /*
406 * On modern systems (W2K+) try the Toolhelp32 API first; this is more stable
407 * and reliable. Fallback to EnumProcess on NT4.
408 */
409 RTLDRMOD hKernel32;
410 int rc = RTLdrLoad("Kernel32.dll", &hKernel32);
411 if (RT_SUCCESS(rc))
412 {
413 PFNCREATETOOLHELP32SNAPSHOT pfnCreateToolhelp32Snapshot;
414 rc = RTLdrGetSymbol(hKernel32, "CreateToolhelp32Snapshot", (void**)&pfnCreateToolhelp32Snapshot);
415 if (RT_SUCCESS(rc))
416 {
417 PFNPROCESS32FIRST pfnProcess32First;
418 rc = RTLdrGetSymbol(hKernel32, "Process32First", (void**)&pfnProcess32First);
419 if (RT_SUCCESS(rc))
420 {
421 PFNPROCESS32NEXT pfnProcess32Next;
422 rc = RTLdrGetSymbol(hKernel32, "Process32Next", (void**)&pfnProcess32Next);
423 if (RT_SUCCESS(rc))
424 {
425 HANDLE hSnap = pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
426 if (hSnap != INVALID_HANDLE_VALUE)
427 {
428 for (size_t i = 0; papszNames[i] && !fFound; i++)
429 {
430 PROCESSENTRY32 procEntry;
431 procEntry.dwSize = sizeof(PROCESSENTRY32);
432 if (pfnProcess32First(hSnap, &procEntry))
433 {
434 do
435 {
436 if ( _stricmp(procEntry.szExeFile, papszNames[i]) == 0
437 && RT_SUCCESS(rtProcGetProcessHandle(procEntry.th32ProcessID, pSID, phToken)))
438 {
439 fFound = true;
440 break;
441 }
442 } while (pfnProcess32Next(hSnap, &procEntry));
443 }
444 else /* Process32First */
445 dwErr = GetLastError();
446 if (FAILED(dwErr))
447 break;
448 }
449 CloseHandle(hSnap);
450 }
451 else /* hSnap == INVALID_HANDLE_VALUE */
452 dwErr = GetLastError();
453 }
454 }
455 }
456 else /* CreateToolhelp32Snapshot / Toolhelp32 API not available. */
457 {
458 /*
459 * NT4 needs a copy of "PSAPI.dll" (redistributed by Microsoft and not
460 * part of the OS) in order to get a lookup. If we don't have this DLL
461 * we are not able to get a token and therefore no UI will be visible.
462 */
463 RTLDRMOD hPSAPI;
464 int rc = RTLdrLoad("PSAPI.dll", &hPSAPI);
465 if (RT_SUCCESS(rc))
466 {
467 PFNENUMPROCESSES pfnEnumProcesses;
468 rc = RTLdrGetSymbol(hPSAPI, "EnumProcesses", (void**)&pfnEnumProcesses);
469 if (RT_SUCCESS(rc))
470 {
471 PFNGETMODULEBASENAME pfnGetModuleBaseName;
472 rc = RTLdrGetSymbol(hPSAPI, "GetModuleBaseName", (void**)&pfnGetModuleBaseName);
473 if (RT_SUCCESS(rc))
474 {
475 /** @todo Retry if pBytesReturned equals cbBytes! */
476 DWORD adwPIDs[4096]; /* Should be sufficient for now. */
477 DWORD cbBytes = 0;
478 if (pfnEnumProcesses(adwPIDs, sizeof(adwPIDs), &cbBytes))
479 {
480 for (size_t i = 0; papszNames[i] && !fFound; i++)
481 {
482 for (DWORD dwIdx = 0; dwIdx < cbBytes/sizeof(DWORD) && !fFound; dwIdx++)
483 {
484 HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
485 FALSE, adwPIDs[dwIdx]);
486 if (hProc)
487 {
488 char *pszProcName = NULL;
489 DWORD dwSize = 128;
490 do
491 {
492 RTMemRealloc(pszProcName, dwSize);
493 if (pfnGetModuleBaseName(hProc, 0, pszProcName, dwSize) == dwSize)
494 dwSize += 128;
495 if (dwSize > _4K) /* Play safe. */
496 break;
497 } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);
498
499 if (pszProcName)
500 {
501 if ( _stricmp(pszProcName, papszNames[i]) == 0
502 && RT_SUCCESS(rtProcGetProcessHandle(adwPIDs[dwIdx], pSID, phToken)))
503 {
504 fFound = true;
505 }
506 }
507 if (pszProcName)
508 RTStrFree(pszProcName);
509 CloseHandle(hProc);
510 }
511 }
512 }
513 }
514 else
515 dwErr = GetLastError();
516 }
517 }
518 }
519 }
520 RTLdrClose(hKernel32);
521 }
522 Assert(dwErr == NO_ERROR);
523 return fFound;
524}
525
526
527/**
528 * Logs on a specified user and returns its primary token.
529 *
530 * @return int
531 *
532 * @param pwszUser User name.
533 * @param pwszPassword Password.
534 * @param pwszDomain Domain (not used at the moment).
535 * @param phToken Pointer to store the logon token.
536 */
537static int rtProcUserLogon(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain, HANDLE *phToken)
538{
539 /** @todo Add domain support! */
540 BOOL fRc = LogonUserW(pwszUser,
541 /*
542 * Because we have to deal with http://support.microsoft.com/kb/245683
543 * for NULL domain names when running on NT4 here, pass an empty string if so.
544 * However, passing FQDNs should work!
545 */
546 ((DWORD)(LOBYTE(LOWORD(GetVersion()))) < 5) /* < Windows 2000. */
547 ? L"" /* NT4 and older. */
548 : NULL, /* Windows 2000 and up. */
549 pwszPassword,
550 LOGON32_LOGON_INTERACTIVE,
551 LOGON32_PROVIDER_DEFAULT,
552 phToken);
553 if (!fRc)
554 return rtProcMapErrorCodes(GetLastError());
555 return VINF_SUCCESS;
556}
557
558
559/**
560 * Logs off a user, specified by the given token.
561 *
562 * @param hToken The token (=user) to log off.
563 */
564static void rtProcUserLogoff(HANDLE hToken)
565{
566 CloseHandle(hToken);
567}
568
569
570/**
571 * Creates an environment block out of a handed in Unicode and RTENV block.
572 * The RTENV block can overwrite entries already present in the Unicode block.
573 *
574 * @return IPRT status code.
575 *
576 * @param pvBlock Unicode block (array) of environment entries; name=value
577 * @param hEnv Handle of an existing RTENV block to use.
578 * @param ppwszBlock Pointer to the final output.
579 */
580static int rtProcEnvironmentCreateInternal(VOID *pvBlock, RTENV hEnv, PRTUTF16 *ppwszBlock)
581{
582 int rc = VINF_SUCCESS;
583 RTENV hEnvTemp;
584 rc = RTEnvClone(&hEnvTemp, hEnv);
585 if (RT_SUCCESS(rc))
586 {
587 PRTUTF16 pBlock = (PRTUTF16)pvBlock;
588 while ( pBlock
589 && pBlock != '\0'
590 && RT_SUCCESS(rc))
591 {
592 char *pszEntry;
593 rc = RTUtf16ToUtf8(pBlock, &pszEntry);
594 if (RT_SUCCESS(rc))
595 {
596 /* Don't overwrite values which we already have set to a custom value
597 * specified in hEnv ... */
598 if (!RTEnvExistEx(hEnv, pszEntry))
599 rc = RTEnvPutEx(hEnvTemp, pszEntry);
600 RTStrFree(pszEntry);
601 }
602
603 size_t l;
604 /* 32k should be the maximum the environment block can have on Windows. */
605 if (FAILED(StringCbLengthW((LPCWSTR)pBlock, _32K * sizeof(RTUTF16), &l)))
606 break;
607 pBlock += l / sizeof(RTUTF16);
608 if (pBlock[1] == '\0') /* Did we reach the double zero termination (\0\0)? */
609 break;
610 pBlock++; /* Skip zero termination of current string and advance to next string ... */
611 }
612
613 if (RT_SUCCESS(rc))
614 rc = RTEnvQueryUtf16Block(hEnvTemp, ppwszBlock);
615 RTEnvDestroy(hEnvTemp);
616 }
617 return rc;
618}
619
620
621/**
622 * Builds up the environment block for a specified user (identified by a token),
623 * whereas hEnv is an additional set of environment variables which overwrite existing
624 * values of the user profile. ppwszBlock needs to be destroyed after usage
625 * calling rtProcEnvironmentDestroy().
626 *
627 * @return IPRT status code.
628 *
629 * @param hToken Token of the user to use.
630 * @param hEnv Own environment block to extend/overwrite the profile's data with.
631 * @param ppwszBlock Pointer to a pointer of the final UTF16 environment block.
632 */
633static int rtProcEnvironmentCreateFromToken(HANDLE hToken, RTENV hEnv, PRTUTF16 *ppwszBlock)
634{
635 RTLDRMOD hUserenv;
636 int rc = RTLdrLoad("Userenv.dll", &hUserenv);
637 if (RT_SUCCESS(rc))
638 {
639 PFNCREATEENVIRONMENTBLOCK pfnCreateEnvironmentBlock;
640 rc = RTLdrGetSymbol(hUserenv, "CreateEnvironmentBlock", (void**)&pfnCreateEnvironmentBlock);
641 if (RT_SUCCESS(rc))
642 {
643 PFNPFNDESTROYENVIRONMENTBLOCK pfnDestroyEnvironmentBlock;
644 rc = RTLdrGetSymbol(hUserenv, "DestroyEnvironmentBlock", (void**)&pfnDestroyEnvironmentBlock);
645 if (RT_SUCCESS(rc))
646 {
647 LPVOID pEnvBlockProfile = NULL;
648 if (pfnCreateEnvironmentBlock(&pEnvBlockProfile, hToken, FALSE /* Don't inherit from parent. */))
649 {
650 rc = rtProcEnvironmentCreateInternal(pEnvBlockProfile, hEnv, ppwszBlock);
651 pfnDestroyEnvironmentBlock(pEnvBlockProfile);
652 }
653 else
654 rc = RTErrConvertFromWin32(GetLastError());
655 }
656 }
657 RTLdrClose(hUserenv);
658 }
659 /* If we don't have the Userenv-API for whatever reason or something with the
660 * native environment block failed, try to return at least our own environment block. */
661 if (RT_FAILURE(rc))
662 rc = RTEnvQueryUtf16Block(hEnv, ppwszBlock);
663 return rc;
664}
665
666
667/**
668 * Builds up the environment block for a specified user (identified by user name, password
669 * and domain), whereas hEnv is an additional set of environment variables which overwrite
670 * existing values of the user profile. ppwszBlock needs to be destroyed after usage
671 * calling rtProcEnvironmentDestroy().
672 *
673 * @return IPRT status code.
674 *
675 * @param pwszUser User name.
676 * @param pwszPassword Password.
677 * @param pwszDomain Domain.
678 * @param hEnv Own environment block to extend/overwrite the profile's data with.
679 * @param ppwszBlock Pointer to a pointer of the final UTF16 environment block.
680 */
681static int rtProcEnvironmentCreateFromAccount(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain,
682 RTENV hEnv, PRTUTF16 *ppwszBlock)
683{
684 HANDLE hToken;
685 int rc = rtProcUserLogon(pwszUser, pwszPassword, pwszDomain, &hToken);
686 if (RT_SUCCESS(rc))
687 {
688 rc = rtProcEnvironmentCreateFromToken(hToken, hEnv, ppwszBlock);
689 rtProcUserLogoff(hToken);
690 }
691 return rc;
692}
693
694
695/**
696 * Destroys an environment block formerly created by rtProcEnvironmentCreateInternal(),
697 * rtProcEnvironmentCreateFromToken() or rtProcEnvironmentCreateFromAccount().
698 *
699 * @param ppwszBlock Environment block to destroy.
700 */
701static void rtProcEnvironmentDestroy(PRTUTF16 ppwszBlock)
702{
703 RTEnvFreeUtf16Block(ppwszBlock);
704}
705
706
707static int rtProcCreateAsUserHlp(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
708 RTENV hEnv, DWORD dwCreationFlags,
709 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
710{
711 int rc = VINF_SUCCESS;
712 BOOL fRc = FALSE;
713 DWORD dwErr = NO_ERROR;
714
715 /*
716 * If we run as a service CreateProcessWithLogon will fail,
717 * so don't even try it (because of Local System context).
718 */
719 if (!(fFlags & RTPROC_FLAGS_SERVICE))
720 {
721 RTLDRMOD hAdvAPI32;
722 rc = RTLdrLoad("Advapi32.dll", &hAdvAPI32);
723 if (RT_SUCCESS(rc))
724 {
725 /*
726 * This may fail on too old (NT4) platforms or if the calling process
727 * is running on a SYSTEM account (like a service, ERROR_ACCESS_DENIED) on newer
728 * platforms (however, this works on W2K!).
729 */
730 PFNCREATEPROCESSWITHLOGON pfnCreateProcessWithLogonW;
731 rc = RTLdrGetSymbol(hAdvAPI32, "CreateProcessWithLogonW", (void**)&pfnCreateProcessWithLogonW);
732 if (RT_SUCCESS(rc))
733 {
734 PRTUTF16 pwszzBlock;
735 rc = rtProcEnvironmentCreateFromAccount(pwszUser, pwszPassword, NULL /* Domain */,
736 hEnv, &pwszzBlock);
737 if (RT_SUCCESS(rc))
738 {
739 fRc = pfnCreateProcessWithLogonW(pwszUser,
740 NULL, /* lpDomain*/
741 pwszPassword,
742 1 /*LOGON_WITH_PROFILE*/, /* dwLogonFlags */
743 pwszExec,
744 pwszCmdLine,
745 dwCreationFlags,
746 pwszzBlock,
747 NULL, /* pCurrentDirectory */
748 pStartupInfo,
749 pProcInfo);
750 if (!fRc)
751 dwErr = GetLastError();
752 rtProcEnvironmentDestroy(pwszzBlock);
753 }
754 }
755 RTLdrClose(hAdvAPI32);
756 }
757 }
758
759 /*
760 * Did the API call above fail because we're running on a too old OS (NT4) or
761 * we're running as a Windows service?
762 */
763 if ( RT_FAILURE(rc)
764 || (fFlags & RTPROC_FLAGS_SERVICE))
765 {
766 /*
767 * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
768 * we have to do the following:
769 * - Check the credentials supplied and get the user SID.
770 * - If valid get the correct Explorer/VBoxTray instance corresponding to that
771 * user. This of course is only possible if that user is logged in (over
772 * physical console or terminal services).
773 * - If we found the user's Explorer/VBoxTray app, use and modify the token to
774 * use it in order to allow the newly started process to access the user's
775 * desktop. If there's no Explorer/VBoxTray app we cannot display the started
776 * process (but run it without UI).
777 *
778 * The following restrictions apply:
779 * - A process only can show its UI when the user the process should run
780 * under is logged in (has a desktop).
781 * - We do not want to display a process of user A run on the desktop
782 * of user B on multi session systems.
783 *
784 * The following rights are needed in order to use LogonUserW and
785 * CreateProcessAsUserW, so the local policy has to be modified to:
786 * - SE_TCB_NAME = Act as part of the operating system
787 * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
788 * - SE_INCREASE_QUOTA_NAME
789 *
790 * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
791 */
792 PHANDLE phToken = NULL;
793 HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
794 rc = rtProcUserLogon(pwszUser, pwszPassword, NULL /* Domain */, &hTokenLogon);
795 if (RT_SUCCESS(rc))
796 {
797 bool fFound = false;
798 HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
799
800 if (fFlags & RTPROC_FLAGS_SERVICE)
801 {
802 DWORD cbName = 0; /* Must be zero to query size! */
803 DWORD cbDomain = 0;
804 SID_NAME_USE sidNameUse = SidTypeUser;
805 fRc = LookupAccountNameW(NULL,
806 pwszUser,
807 NULL,
808 &cbName,
809 NULL,
810 &cbDomain,
811 &sidNameUse);
812 if (!fRc)
813 dwErr = GetLastError();
814 if ( !fRc
815 && dwErr == ERROR_INSUFFICIENT_BUFFER
816 && cbName > 0)
817 {
818 dwErr = NO_ERROR;
819
820 PSID pSID = (PSID)RTMemAlloc(cbName * sizeof(wchar_t));
821 AssertPtrReturn(pSID, VERR_NO_MEMORY);
822
823 /** @todo No way to allocate a PRTUTF16 directly? */
824 PRTUTF16 pwszDomain = NULL;
825 if (cbDomain > 0)
826 {
827 pwszDomain = (PRTUTF16)RTMemAlloc(cbDomain * sizeof(RTUTF16));
828 AssertPtrReturn(pwszDomain, VERR_NO_MEMORY);
829 }
830
831 /* Note: Also supports FQDNs! */
832 if ( LookupAccountNameW(NULL, /* lpSystemName */
833 pwszUser,
834 pSID,
835 &cbName,
836 pwszDomain,
837 &cbDomain,
838 &sidNameUse)
839 && IsValidSid(pSID))
840 {
841 /* Array of process names we want to look for. */
842 static const char * const s_papszProcNames[] =
843 {
844#ifdef VBOX /* The explorer entry is a fallback in case GA aren't installed. */
845 { "VBoxTray.exe" },
846#endif
847 { "explorer.exe" },
848 NULL
849 };
850 fFound = rtProcFindProcessByName(s_papszProcNames, pSID, &hTokenUserDesktop);
851 }
852 else
853 dwErr = GetLastError(); /* LookupAccountNameW() failed. */
854 RTMemFree(pSID);
855 if (pwszDomain != NULL)
856 RTUtf16Free(pwszDomain);
857 }
858 }
859 else /* !RTPROC_FLAGS_SERVICE */
860 {
861 /* Nothing to do here right now. */
862 }
863
864 /*
865 * If we didn't find a matching VBoxTray, just use the token we got
866 * above from LogonUserW(). This enables us to at least run processes with
867 * desktop interaction without UI.
868 */
869 phToken = fFound ? &hTokenUserDesktop : &hTokenLogon;
870
871 RTLDRMOD hUserenv;
872 int rc = RTLdrLoad("Userenv.dll", &hUserenv);
873 if (RT_SUCCESS(rc))
874 {
875 PFNLOADUSERPROFILEW pfnLoadUserProfileW;
876 rc = RTLdrGetSymbol(hUserenv, "LoadUserProfileW", (void**)&pfnLoadUserProfileW);
877 if (RT_SUCCESS(rc))
878 {
879 PFNUNLOADUSERPROFILE pfnUnloadUserProfile;
880 rc = RTLdrGetSymbol(hUserenv, "UnloadUserProfile", (void**)&pfnUnloadUserProfile);
881 if (RT_SUCCESS(rc))
882 {
883 PROFILEINFOW profileInfo;
884 ZeroMemory(&profileInfo, sizeof(profileInfo));
885 profileInfo.dwSize = sizeof(profileInfo);
886 profileInfo.lpUserName = pwszUser;
887 profileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
888
889 if (pfnLoadUserProfileW(*phToken, &profileInfo))
890 {
891 PRTUTF16 pwszzBlock;
892 rc = rtProcEnvironmentCreateFromToken(*phToken, hEnv, &pwszzBlock);
893 if (RT_SUCCESS(rc))
894 {
895 /*
896 * Useful KB articles:
897 * http://support.microsoft.com/kb/165194/
898 * http://support.microsoft.com/kb/184802/
899 * http://support.microsoft.com/kb/327618/
900 */
901 fRc = CreateProcessAsUserW(*phToken,
902 pwszExec,
903 pwszCmdLine,
904 NULL, /* pProcessAttributes */
905 NULL, /* pThreadAttributes */
906 TRUE, /* fInheritHandles */
907 dwCreationFlags,
908 pwszzBlock,
909 NULL, /* pCurrentDirectory */
910 pStartupInfo,
911 pProcInfo);
912 if (fRc)
913 dwErr = NO_ERROR;
914 else
915 dwErr = GetLastError(); /* CreateProcessAsUserW() failed. */
916 rtProcEnvironmentDestroy(pwszzBlock);
917 }
918 else
919 dwErr = rc;
920 pfnUnloadUserProfile(*phToken, profileInfo.hProfile);
921 }
922 else
923 dwErr = GetLastError(); /* LoadUserProfileW() failed. */
924 }
925 }
926 RTLdrClose(hUserenv);
927 }
928 if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
929 CloseHandle(hTokenUserDesktop);
930 rtProcUserLogoff(hTokenLogon);
931 }
932 }
933
934 if ( RT_SUCCESS(rc)
935 && dwErr != NO_ERROR)
936 {
937 rc = rtProcMapErrorCodes(dwErr);
938 }
939 return rc;
940}
941
942RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
943 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
944 const char *pszPassword, PRTPROCESS phProcess)
945{
946 /*
947 * Input validation
948 */
949 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
950 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
951 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_HIDDEN |RTPROC_FLAGS_SERVICE)), VERR_INVALID_PARAMETER);
952 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
953 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
954 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
955 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
956 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
957 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
958 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
959 /** @todo search the PATH (add flag for this). */
960
961 /*
962 * Initialize the globals.
963 */
964 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
965 AssertRCReturn(rc, rc);
966
967 /*
968 * Get the file descriptors for the handles we've been passed.
969 *
970 * It seems there is no point in trying to convince a child process's CRT
971 * that any of the standard file handles is non-TEXT. So, we don't...
972 */
973 STARTUPINFOW StartupInfo;
974 RT_ZERO(StartupInfo);
975 StartupInfo.cb = sizeof(StartupInfo);
976 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
977#if 1 /* The CRT should keep the standard handles up to date. */
978 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
979 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
980 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
981#else
982 StartupInfo.hStdInput = _get_osfhandle(0);
983 StartupInfo.hStdOutput = _get_osfhandle(1);
984 StartupInfo.hStdError = _get_osfhandle(2);
985#endif
986 /* If we want to have a hidden process (e.g. not visible to
987 * to the user) use the STARTUPINFO flags. */
988 if (fFlags & RTPROC_FLAGS_HIDDEN)
989 {
990 StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
991 StartupInfo.wShowWindow = SW_HIDE;
992 }
993
994 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
995 HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
996 DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
997 for (int i = 0; i < 3; i++)
998 {
999 if (paHandles[i])
1000 {
1001 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
1002 switch (paHandles[i]->enmType)
1003 {
1004 case RTHANDLETYPE_FILE:
1005 *aphStds[i] = paHandles[i]->u.hFile != NIL_RTFILE
1006 ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
1007 : INVALID_HANDLE_VALUE;
1008 break;
1009
1010 case RTHANDLETYPE_PIPE:
1011 *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
1012 ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
1013 : INVALID_HANDLE_VALUE;
1014 break;
1015
1016 case RTHANDLETYPE_SOCKET:
1017 *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
1018 ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
1019 : INVALID_HANDLE_VALUE;
1020 break;
1021
1022 default:
1023 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
1024 }
1025
1026 /* Get the inheritability of the handle. */
1027 if (*aphStds[i] != INVALID_HANDLE_VALUE)
1028 {
1029 if (!GetHandleInformation(*aphStds[i], &afInhStds[i]))
1030 {
1031 rc = RTErrConvertFromWin32(GetLastError());
1032 AssertMsgFailedReturn(("%Rrc %p\n", rc, *aphStds[i]), rc);
1033 }
1034 }
1035 }
1036 }
1037
1038 /*
1039 * Set the inheritability any handles we're handing the child.
1040 */
1041 rc = VINF_SUCCESS;
1042 for (int i = 0; i < 3; i++)
1043 if ( (afInhStds[i] != 0xffffffff)
1044 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
1045 {
1046 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
1047 {
1048 rc = RTErrConvertFromWin32(GetLastError());
1049 AssertMsgFailedBreak(("%Rrc %p\n", rc, *aphStds[i]));
1050 }
1051 }
1052
1053 /*
1054 * Create the environment block, command line and convert the executable
1055 * name.
1056 */
1057 PRTUTF16 pwszzBlock;
1058 if (RT_SUCCESS(rc))
1059 rc = RTEnvQueryUtf16Block(hEnv, &pwszzBlock);
1060 if (RT_SUCCESS(rc))
1061 {
1062 PRTUTF16 pwszCmdLine;
1063 rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
1064 if (RT_SUCCESS(rc))
1065 {
1066 PRTUTF16 pwszExec;
1067 rc = RTStrToUtf16(pszExec, &pwszExec);
1068 if (RT_SUCCESS(rc))
1069 {
1070 /*
1071 * Get going...
1072 */
1073 PROCESS_INFORMATION ProcInfo;
1074 RT_ZERO(ProcInfo);
1075 DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
1076 if (fFlags & RTPROC_FLAGS_DETACHED)
1077 dwCreationFlags |= DETACHED_PROCESS;
1078
1079 /*
1080 * Only use the normal CreateProcess stuff if we have no user name
1081 * and we are not running from a (Windows) service. Otherwise use
1082 * the more advanced version in rtProcCreateAsUserHlp().
1083 */
1084 if ( pszAsUser == NULL
1085 && !(fFlags & RTPROC_FLAGS_SERVICE))
1086 {
1087 if (CreateProcessW(pwszExec,
1088 pwszCmdLine,
1089 NULL, /* pProcessAttributes */
1090 NULL, /* pThreadAttributes */
1091 TRUE, /* fInheritHandles */
1092 dwCreationFlags,
1093 pwszzBlock,
1094 NULL, /* pCurrentDirectory */
1095 &StartupInfo,
1096 &ProcInfo))
1097 rc = VINF_SUCCESS;
1098 else
1099 rc = RTErrConvertFromWin32(GetLastError());
1100 }
1101 else
1102 {
1103 /*
1104 * Convert the additional parameters and use a helper
1105 * function to do the actual work.
1106 */
1107 PRTUTF16 pwszUser;
1108 rc = RTStrToUtf16(pszAsUser, &pwszUser);
1109 if (RT_SUCCESS(rc))
1110 {
1111 PRTUTF16 pwszPassword;
1112 rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
1113 if (RT_SUCCESS(rc))
1114 {
1115 rc = rtProcCreateAsUserHlp(pwszUser, pwszPassword,
1116 pwszExec, pwszCmdLine, hEnv, dwCreationFlags,
1117 &StartupInfo, &ProcInfo, fFlags);
1118
1119 RTUtf16Free(pwszPassword);
1120 }
1121 RTUtf16Free(pwszUser);
1122 }
1123 }
1124 if (RT_SUCCESS(rc))
1125 {
1126 CloseHandle(ProcInfo.hThread);
1127 if (phProcess)
1128 {
1129 /*
1130 * Add the process to the child process list so
1131 * RTProcWait can reuse and close the process handle.
1132 */
1133 rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
1134 *phProcess = ProcInfo.dwProcessId;
1135 }
1136 else
1137 CloseHandle(ProcInfo.hProcess);
1138 rc = VINF_SUCCESS;
1139 }
1140 RTUtf16Free(pwszExec);
1141 }
1142 RTUtf16Free(pwszCmdLine);
1143 }
1144 RTEnvFreeUtf16Block(pwszzBlock);
1145 }
1146
1147 /* Undo any handle inherit changes. */
1148 for (int i = 0; i < 3; i++)
1149 if ( (afInhStds[i] != 0xffffffff)
1150 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
1151 {
1152 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0))
1153 AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
1154 }
1155
1156 return rc;
1157}
1158
1159
1160
1161RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
1162{
1163 AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
1164 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
1165 AssertRCReturn(rc, rc);
1166
1167 /*
1168 * Try find the process among the ones we've spawned, otherwise, attempt
1169 * opening the specified process.
1170 */
1171 HANDLE hProcess = rtProcWinFindPid(Process);
1172 if (hProcess == NULL)
1173 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
1174 if (hProcess != NULL)
1175 {
1176 /*
1177 * Wait for it to terminate.
1178 */
1179 DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
1180 DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
1181 while (WaitRc == WAIT_IO_COMPLETION)
1182 WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
1183 switch (WaitRc)
1184 {
1185 /*
1186 * It has terminated.
1187 */
1188 case WAIT_OBJECT_0:
1189 {
1190 DWORD dwExitCode;
1191 if (GetExitCodeProcess(hProcess, &dwExitCode))
1192 {
1193 /** @todo the exit code can be special statuses. */
1194 if (pProcStatus)
1195 {
1196 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
1197 pProcStatus->iStatus = (int)dwExitCode;
1198 }
1199 rtProcWinRemovePid(Process);
1200 return VINF_SUCCESS;
1201 }
1202 break;
1203 }
1204
1205 /*
1206 * It hasn't terminated just yet.
1207 */
1208 case WAIT_TIMEOUT:
1209 return VERR_PROCESS_RUNNING;
1210
1211 /*
1212 * Something went wrong...
1213 */
1214 case WAIT_FAILED:
1215 break;
1216 case WAIT_ABANDONED:
1217 AssertFailed();
1218 return VERR_GENERAL_FAILURE;
1219 default:
1220 AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
1221 return VERR_GENERAL_FAILURE;
1222 }
1223 }
1224 DWORD dwErr = GetLastError();
1225 if (dwErr == ERROR_INVALID_PARAMETER)
1226 return VERR_PROCESS_NOT_FOUND;
1227 return RTErrConvertFromWin32(dwErr);
1228}
1229
1230
1231RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
1232{
1233 /** @todo this isn't quite right. */
1234 return RTProcWait(Process, fFlags, pProcStatus);
1235}
1236
1237
1238RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
1239{
1240 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
1241 AssertRCReturn(rc, rc);
1242
1243 /*
1244 * Try find the process among the ones we've spawned, otherwise, attempt
1245 * opening the specified process.
1246 */
1247 HANDLE hProcess = rtProcWinFindPid(Process);
1248 if (hProcess != NULL)
1249 {
1250 if (!TerminateProcess(hProcess, 127))
1251 rc = RTErrConvertFromWin32(GetLastError());
1252 }
1253 else
1254 {
1255 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
1256 if (hProcess != NULL)
1257 {
1258 BOOL fRc = TerminateProcess(hProcess, 127);
1259 DWORD dwErr = GetLastError();
1260 CloseHandle(hProcess);
1261 if (!fRc)
1262 rc = RTErrConvertFromWin32(dwErr);
1263 }
1264 }
1265 return rc;
1266}
1267
1268
1269RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
1270{
1271 DWORD_PTR dwProcessAffinityMask = 0xffffffff;
1272 DWORD_PTR dwSystemAffinityMask;
1273
1274 BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
1275 Assert(fRc);
1276
1277 return dwProcessAffinityMask;
1278}
1279
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette