VirtualBox

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

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

IPRT/process-win: Added dynamic loading of Userenv-APIs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 47.7 KB
Line 
1/* $Id: process-win.cpp 32656 2010-09-21 08:47:18Z 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 * Get the process token (not the process handle like the name might indicate)
271 * of the process indicated by @a dwPID if the @a pSID matches.
272 *
273 * @returns IPRT status code.
274 *
275 * @param dwPID The process identifier.
276 * @param pSID The secure identifier of the user.
277 * @param phToken Where to return the token handle - duplicate,
278 * caller closes it on success.
279 */
280static int rtProcGetProcessHandle(DWORD dwPID, PSID pSID, PHANDLE phToken)
281{
282 AssertPtr(pSID);
283 AssertPtr(phToken);
284
285 DWORD dwErr;
286 BOOL fRc;
287 bool fFound = false;
288 HANDLE hProc = OpenProcess(MAXIMUM_ALLOWED, TRUE, dwPID);
289 if (hProc != NULL)
290 {
291 HANDLE hTokenProc;
292 fRc = OpenProcessToken(hProc,
293 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE
294 | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
295 &hTokenProc);
296 if (fRc)
297 {
298 DWORD dwSize = 0;
299 fRc = GetTokenInformation(hTokenProc, TokenUser, NULL, 0, &dwSize);
300 if (!fRc)
301 dwErr = GetLastError();
302 if ( !fRc
303 && dwErr == ERROR_INSUFFICIENT_BUFFER
304 && dwSize > 0)
305 {
306 PTOKEN_USER pTokenUser = (PTOKEN_USER)RTMemAlloc(dwSize);
307 AssertPtrReturn(pTokenUser, VERR_NO_MEMORY);
308 RT_ZERO(*pTokenUser);
309 if ( GetTokenInformation(hTokenProc,
310 TokenUser,
311 (LPVOID)pTokenUser,
312 dwSize,
313 &dwSize))
314 {
315 if ( IsValidSid(pTokenUser->User.Sid)
316 && EqualSid(pTokenUser->User.Sid, pSID))
317 {
318 if (DuplicateTokenEx(hTokenProc, MAXIMUM_ALLOWED,
319 NULL, SecurityIdentification, TokenPrimary, phToken))
320 {
321 /*
322 * So we found the process instance which belongs to the user we want to
323 * to run our new process under. This duplicated token will be used for
324 * the actual CreateProcessAsUserW() call then.
325 */
326 fFound = true;
327 }
328 else
329 dwErr = GetLastError();
330 }
331 }
332 else
333 dwErr = GetLastError();
334 RTMemFree(pTokenUser);
335 }
336 else
337 dwErr = GetLastError();
338 CloseHandle(hTokenProc);
339 }
340 else
341 dwErr = GetLastError();
342 CloseHandle(hProc);
343 }
344 else
345 dwErr = GetLastError();
346 if (fFound)
347 return VINF_SUCCESS;
348 if (dwErr != NO_ERROR)
349 return RTErrConvertFromWin32(dwErr);
350 return VERR_NOT_FOUND; /* No error occured, but we didn't find the right process. */
351}
352
353
354/**
355 * Finds a one of the processes in @a papszNames running with user @a pSID and
356 * returns a duplicate handle to its token.
357 *
358 * @returns Success indicator.
359 * @param papszNames The process candidates, in prioritized order.
360 * @param pSID The secure identifier of the user.
361 * @param phToken Where to return the token handle - duplicate,
362 * caller closes it on success.
363 */
364static bool rtProcFindProcessByName(const char * const *papszNames, PSID pSID, PHANDLE phToken)
365{
366 AssertPtr(papszNames);
367 AssertPtr(pSID);
368 AssertPtr(phToken);
369
370 DWORD dwErr = NO_ERROR;
371 bool fFound = false;
372
373 /*
374 * On modern systems (W2K+) try the Toolhelp32 API first; this is more stable
375 * and reliable. Fallback to EnumProcess on NT4.
376 */
377 RTLDRMOD hKernel32;
378 int rc = RTLdrLoad("Kernel32.dll", &hKernel32);
379 if (RT_SUCCESS(rc))
380 {
381 PFNCREATETOOLHELP32SNAPSHOT pfnCreateToolhelp32Snapshot;
382 rc = RTLdrGetSymbol(hKernel32, "CreateToolhelp32Snapshot", (void**)&pfnCreateToolhelp32Snapshot);
383 if (RT_SUCCESS(rc))
384 {
385 PFNPROCESS32FIRST pfnProcess32First;
386 rc = RTLdrGetSymbol(hKernel32, "Process32First", (void**)&pfnProcess32First);
387 if (RT_SUCCESS(rc))
388 {
389 PFNPROCESS32NEXT pfnProcess32Next;
390 rc = RTLdrGetSymbol(hKernel32, "Process32Next", (void**)&pfnProcess32Next);
391 if (RT_SUCCESS(rc))
392 {
393 HANDLE hSnap = pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
394 if (hSnap != INVALID_HANDLE_VALUE)
395 {
396 for (size_t i = 0; papszNames[i] && !fFound; i++)
397 {
398 PROCESSENTRY32 procEntry;
399 procEntry.dwSize = sizeof(PROCESSENTRY32);
400 if (pfnProcess32First(hSnap, &procEntry))
401 {
402 do
403 {
404 if ( _stricmp(procEntry.szExeFile, papszNames[i]) == 0
405 && RT_SUCCESS(rtProcGetProcessHandle(procEntry.th32ProcessID, pSID, phToken)))
406 {
407 fFound = true;
408 break;
409 }
410 } while (pfnProcess32Next(hSnap, &procEntry));
411 }
412 else /* Process32First */
413 dwErr = GetLastError();
414 if (FAILED(dwErr))
415 break;
416 }
417 CloseHandle(hSnap);
418 }
419 else /* hSnap == INVALID_HANDLE_VALUE */
420 dwErr = GetLastError();
421 }
422 }
423 }
424 else /* CreateToolhelp32Snapshot / Toolhelp32 API not available. */
425 {
426 /*
427 * NT4 needs a copy of "PSAPI.dll" (redistributed by Microsoft and not
428 * part of the OS) in order to get a lookup. If we don't have this DLL
429 * we are not able to get a token and therefore no UI will be visible.
430 */
431 RTLDRMOD hPSAPI;
432 int rc = RTLdrLoad("PSAPI.dll", &hPSAPI);
433 if (RT_SUCCESS(rc))
434 {
435 PFNENUMPROCESSES pfnEnumProcesses;
436 rc = RTLdrGetSymbol(hPSAPI, "EnumProcesses", (void**)&pfnEnumProcesses);
437 if (RT_SUCCESS(rc))
438 {
439 PFNGETMODULEBASENAME pfnGetModuleBaseName;
440 rc = RTLdrGetSymbol(hPSAPI, "GetModuleBaseName", (void**)&pfnGetModuleBaseName);
441 if (RT_SUCCESS(rc))
442 {
443 /** @todo Retry if pBytesReturned equals cbBytes! */
444 DWORD adwPIDs[4096]; /* Should be sufficient for now. */
445 DWORD cbBytes = 0;
446 if (pfnEnumProcesses(adwPIDs, sizeof(adwPIDs), &cbBytes))
447 {
448 for (size_t i = 0; papszNames[i] && !fFound; i++)
449 {
450 for (DWORD dwIdx = 0; dwIdx < cbBytes/sizeof(DWORD) && !fFound; dwIdx++)
451 {
452 HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
453 FALSE, adwPIDs[dwIdx]);
454 if (hProc)
455 {
456 char *pszProcName = NULL;
457 DWORD dwSize = 128;
458 do
459 {
460 RTMemRealloc(pszProcName, dwSize);
461 if (pfnGetModuleBaseName(hProc, 0, pszProcName, dwSize) == dwSize)
462 dwSize += 128;
463 if (dwSize > _4K) /* Play safe. */
464 break;
465 } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);
466
467 if (pszProcName)
468 {
469 if ( _stricmp(pszProcName, papszNames[i]) == 0
470 && RT_SUCCESS(rtProcGetProcessHandle(adwPIDs[dwIdx], pSID, phToken)))
471 {
472 fFound = true;
473 }
474 }
475 if (pszProcName)
476 RTStrFree(pszProcName);
477 CloseHandle(hProc);
478 }
479 }
480 }
481 }
482 else
483 dwErr = GetLastError();
484 }
485 }
486 }
487 }
488 RTLdrClose(hKernel32);
489 }
490 Assert(dwErr == NO_ERROR);
491 return fFound;
492}
493
494
495/**
496 * Logs on a specified user and returns its primary token.
497 *
498 * @return int
499 *
500 * @param pwszUser User name.
501 * @param pwszPassword Password.
502 * @param pwszDomain Domain (not used at the moment).
503 * @param phToken Pointer to store the logon token.
504 */
505static int rtProcUserLogon(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain, HANDLE *phToken)
506{
507 int rc = VINF_SUCCESS;
508 /** @todo Add domain support! */
509 BOOL fRc = LogonUserW(pwszUser,
510 /*
511 * Because we have to deal with http://support.microsoft.com/kb/245683
512 * for NULL domain names when running on NT4 here, pass an empty string if so.
513 * However, passing FQDNs should work!
514 */
515 ((DWORD)(LOBYTE(LOWORD(GetVersion()))) < 5) /* < Windows 2000. */
516 ? L"" /* NT4 and older. */
517 : NULL, /* Windows 2000 and up. */
518 pwszPassword,
519 LOGON32_LOGON_INTERACTIVE,
520 LOGON32_PROVIDER_DEFAULT,
521 phToken);
522 if (!fRc)
523 rc = RTErrConvertFromWin32(GetLastError());
524 return rc;
525}
526
527
528/**
529 * Logs off a user, specified by the given tokken.
530 *
531 * @param hToken The token (=user) to log off.
532 */
533static void rtProcUserLogoff(HANDLE hToken)
534{
535 CloseHandle(hToken);
536}
537
538
539/**
540 * Creates an environment block out of a handed in Unicode and RTENV block.
541 * The RTENV block can overwrite entries already present in the Unicode block.
542 *
543 * @return IPRT status code.
544 *
545 * @param pvBlock Unicode block (array) of environment entries; name=value
546 * @param hEnv Handle of an existing RTENV block to use.
547 * @param ppwszBlock Pointer to the final output.
548 */
549static int rtProcEnvironmentCreateInternal(VOID *pvBlock, RTENV hEnv, PRTUTF16 *ppwszBlock)
550{
551 int rc = VINF_SUCCESS;
552 RTENV hEnvTemp;
553 rc = RTEnvClone(&hEnvTemp, hEnv);
554 if (RT_SUCCESS(rc))
555 {
556 PRTUTF16 pBlock = (PRTUTF16)pvBlock;
557 while ( pBlock
558 && pBlock != '\0'
559 && RT_SUCCESS(rc))
560 {
561 char *pszEntry;
562 rc = RTUtf16ToUtf8(pBlock, &pszEntry);
563 if (RT_SUCCESS(rc))
564 {
565 /* Don't overwrite values which we already have set to a custom value
566 * specified in hEnv ... */
567 if (!RTEnvExistEx(hEnv, pszEntry))
568 rc = RTEnvPutEx(hEnvTemp, pszEntry);
569 RTStrFree(pszEntry);
570 }
571
572 size_t l;
573 /* 32k should be the maximum the environment block can have on Windows. */
574 if (FAILED(StringCbLengthW((LPCWSTR)pBlock, _32K * sizeof(RTUTF16), &l)))
575 break;
576 pBlock += l / sizeof(RTUTF16);
577 if (pBlock[1] == '\0') /* Did we reach the double zero termination (\0\0)? */
578 break;
579 pBlock++; /* Skip zero termination of current string and advance to next string ... */
580 }
581
582 if (RT_SUCCESS(rc))
583 rc = RTEnvQueryUtf16Block(hEnvTemp, ppwszBlock);
584 RTEnvDestroy(hEnvTemp);
585 }
586 return rc;
587}
588
589
590/**
591 * Builds up the environment block for a specified user (identified by a token),
592 * whereas hEnv is an additional set of environment variables which overwrite existing
593 * values of the user profile. ppwszBlock needs to be destroyed after usage
594 * calling rtProcEnvironmentDestroy().
595 *
596 * @return IPRT status code.
597 *
598 * @param hToken Token of the user to use.
599 * @param hEnv Own environment block to extend/overwrite the profile's data with.
600 * @param ppwszBlock Pointer to a pointer of the final UTF16 environment block.
601 */
602static int rtProcEnvironmentCreateFromToken(HANDLE hToken, RTENV hEnv, PRTUTF16 *ppwszBlock)
603{
604 RTLDRMOD hUserenv;
605 int rc = RTLdrLoad("Userenv.dll", &hUserenv);
606 if (RT_SUCCESS(rc))
607 {
608 PFNCREATEENVIRONMENTBLOCK pfnCreateEnvironmentBlock;
609 rc = RTLdrGetSymbol(hUserenv, "CreateEnvironmentBlock", (void**)&pfnCreateEnvironmentBlock);
610 if (RT_SUCCESS(rc))
611 {
612 PFNPFNDESTROYENVIRONMENTBLOCK pfnDestroyEnvironmentBlock;
613 rc = RTLdrGetSymbol(hUserenv, "DestroyEnvironmentBlock", (void**)&pfnDestroyEnvironmentBlock);
614 if (RT_SUCCESS(rc))
615 {
616 LPVOID pEnvBlockProfile = NULL;
617 if (pfnCreateEnvironmentBlock(&pEnvBlockProfile, hToken, FALSE /* Don't inherit from parent. */))
618 {
619 rc = rtProcEnvironmentCreateInternal(pEnvBlockProfile, hEnv, ppwszBlock);
620 pfnDestroyEnvironmentBlock(pEnvBlockProfile);
621 }
622 else
623 rc = RTErrConvertFromWin32(GetLastError());
624 }
625 }
626 RTLdrClose(hUserenv);
627 }
628 return rc;
629}
630
631
632/**
633 * Builds up the environment block for a specified user (identified by user name, password
634 * and domain), whereas hEnv is an additional set of environment variables which overwrite
635 * existing values of the user profile. ppwszBlock needs to be destroyed after usage
636 * calling rtProcEnvironmentDestroy().
637 *
638 * @return IPRT status code.
639 *
640 * @param pwszUser User name.
641 * @param pwszPassword Password.
642 * @param pwszDomain Domain.
643 * @param hEnv Own environment block to extend/overwrite the profile's data with.
644 * @param ppwszBlock Pointer to a pointer of the final UTF16 environment block.
645 */
646static int rtProcEnvironmentCreateFromAccount(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain,
647 RTENV hEnv, PRTUTF16 *ppwszBlock)
648{
649 HANDLE hToken;
650 int rc = rtProcUserLogon(pwszUser, pwszPassword, pwszDomain, &hToken);
651 if (RT_SUCCESS(rc))
652 {
653 rc = rtProcEnvironmentCreateFromToken(hToken, hEnv, ppwszBlock);
654 rtProcUserLogoff(hToken);
655 }
656 return rc;
657}
658
659
660/**
661 * Destroys an environment block formerly created by rtProcEnvironmentCreateInternal(),
662 * rtProcEnvironmentCreateFromToken() or rtProcEnvironmentCreateFromAccount().
663 *
664 * @param ppwszBlock Environment block to destroy.
665 */
666static void rtProcEnvironmentDestroy(PRTUTF16 ppwszBlock)
667{
668 RTEnvFreeUtf16Block(ppwszBlock);
669}
670
671
672static int rtProcCreateAsUserHlp(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
673 RTENV hEnv, DWORD dwCreationFlags,
674 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
675{
676 int rc = VINF_SUCCESS;
677 BOOL fRc = FALSE;
678 DWORD dwErr = NO_ERROR;
679
680 /*
681 * If we run as a service CreateProcessWithLogon will fail,
682 * so don't even try it (because of Local System context).
683 */
684 if (!(fFlags & RTPROC_FLAGS_SERVICE))
685 {
686 RTLDRMOD hAdvAPI32;
687 rc = RTLdrLoad("Advapi32.dll", &hAdvAPI32);
688 if (RT_SUCCESS(rc))
689 {
690 /*
691 * This may fail on too old (NT4) platforms or if the calling process
692 * is running on a SYSTEM account (like a service, ERROR_ACCESS_DENIED) on newer
693 * platforms (however, this works on W2K!).
694 */
695 PFNCREATEPROCESSWITHLOGON pfnCreateProcessWithLogonW;
696 rc = RTLdrGetSymbol(hAdvAPI32, "CreateProcessWithLogonW", (void**)&pfnCreateProcessWithLogonW);
697 if (RT_SUCCESS(rc))
698 {
699 PRTUTF16 pwszzBlock;
700 rc = rtProcEnvironmentCreateFromAccount(pwszUser, pwszPassword, NULL /* Domain */,
701 hEnv, &pwszzBlock);
702 if (RT_SUCCESS(rc))
703 {
704 fRc = pfnCreateProcessWithLogonW(pwszUser,
705 NULL, /* lpDomain*/
706 pwszPassword,
707 1 /*LOGON_WITH_PROFILE*/, /* dwLogonFlags */
708 pwszExec,
709 pwszCmdLine,
710 dwCreationFlags,
711 pwszzBlock,
712 NULL, /* pCurrentDirectory */
713 pStartupInfo,
714 pProcInfo);
715 if (!fRc)
716 dwErr = GetLastError();
717 rtProcEnvironmentDestroy(pwszzBlock);
718 }
719 }
720 RTLdrClose(hAdvAPI32);
721 }
722 }
723
724 /*
725 * Did the API call above fail because we're running on a too old OS (NT4) or
726 * we're running as a Windows service?
727 */
728 if ( RT_FAILURE(rc)
729 || (fFlags & RTPROC_FLAGS_SERVICE))
730 {
731 /*
732 * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
733 * we have to do the following:
734 * - Check the credentials supplied and get the user SID.
735 * - If valid get the correct Explorer/VBoxTray instance corresponding to that
736 * user. This of course is only possible if that user is logged in (over
737 * physical console or terminal services).
738 * - If we found the user's Explorer/VBoxTray app, use and modify the token to
739 * use it in order to allow the newly started process acess the user's
740 * desktop. If there's no Explorer/VBoxTray app we cannot display the started
741 * process (but run it without UI).
742 *
743 * The following restrictions apply:
744 * - A process only can show its UI when the user the process should run
745 * under is logged in (has a desktop).
746 * - We do not want to display a process of user A run on the desktop
747 * of user B on multi session systems.
748 *
749 * The following rights are needed in order to use LogonUserW and
750 * CreateProcessAsUserW, so the local policy has to be modified to:
751 * - SE_TCB_NAME = Act as part of the operating system
752 * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
753 * - SE_INCREASE_QUOTA_NAME
754 *
755 * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
756 */
757 PHANDLE phToken = NULL;
758 HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
759 rc = rtProcUserLogon(pwszUser, pwszPassword, NULL /* Domain */, &hTokenLogon);
760 if (RT_SUCCESS(rc))
761 {
762 bool fFound = false;
763 HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
764
765 if (fFlags & RTPROC_FLAGS_SERVICE)
766 {
767 DWORD cbName = 0; /* Must be zero to query size! */
768 DWORD cbDomain = 0;
769 SID_NAME_USE sidNameUse = SidTypeUser;
770 fRc = LookupAccountNameW(NULL,
771 pwszUser,
772 NULL,
773 &cbName,
774 NULL,
775 &cbDomain,
776 &sidNameUse);
777 if (!fRc)
778 dwErr = GetLastError();
779 if ( !fRc
780 && dwErr == ERROR_INSUFFICIENT_BUFFER
781 && cbName > 0)
782 {
783 dwErr = NO_ERROR;
784
785 PSID pSID = (PSID)RTMemAlloc(cbName * sizeof(wchar_t));
786 AssertPtrReturn(pSID, VERR_NO_MEMORY);
787
788 /** @todo No way to allocate a PRTUTF16 directly? */
789 PRTUTF16 pwszDomain = NULL;
790 if (cbDomain > 0)
791 {
792 pwszDomain = (PRTUTF16)RTMemAlloc(cbDomain * sizeof(RTUTF16));
793 AssertPtrReturn(pwszDomain, VERR_NO_MEMORY);
794 }
795
796 /* Note: Also supports FQDNs! */
797 if ( LookupAccountNameW(NULL, /* lpSystemName */
798 pwszUser,
799 pSID,
800 &cbName,
801 pwszDomain,
802 &cbDomain,
803 &sidNameUse)
804 && IsValidSid(pSID))
805 {
806 /* Array of process names we want to look for. */
807 static const char * const s_papszProcNames[] =
808 {
809#ifdef VBOX /* The explorer entry is a fallback in case GA aren't installed. */
810 { "VBoxTray.exe" },
811#endif
812 { "explorer.exe" },
813 NULL
814 };
815 fFound = rtProcFindProcessByName(s_papszProcNames, pSID, &hTokenUserDesktop);
816 }
817 else
818 dwErr = GetLastError(); /* LookupAccountNameW() failed. */
819 RTMemFree(pSID);
820 if (pwszDomain != NULL)
821 RTUtf16Free(pwszDomain);
822 }
823 }
824 else /* !RTPROC_FLAGS_SERVICE */
825 {
826 /* Nothing to do here right now. */
827 }
828
829 /*
830 * If we didn't find a matching VBoxTray, just use the token we got
831 * above from LogonUserW(). This enables us to at least run processes with
832 * desktop interaction without UI.
833 */
834 phToken = fFound ? &hTokenUserDesktop : &hTokenLogon;
835
836 RTLDRMOD hUserenv;
837 int rc = RTLdrLoad("Userenv.dll", &hUserenv);
838 if (RT_SUCCESS(rc))
839 {
840 PFNLOADUSERPROFILEW pfnLoadUserProfileW;
841 rc = RTLdrGetSymbol(hUserenv, "LoadUserProfileW", (void**)&pfnLoadUserProfileW);
842 if (RT_SUCCESS(rc))
843 {
844 PFNUNLOADUSERPROFILE pfnUnloadUserProfile;
845 rc = RTLdrGetSymbol(hUserenv, "UnloadUserProfile", (void**)&pfnUnloadUserProfile);
846 if (RT_SUCCESS(rc))
847 {
848 PROFILEINFOW profileInfo;
849 ZeroMemory(&profileInfo, sizeof(profileInfo));
850 profileInfo.dwSize = sizeof(profileInfo);
851 profileInfo.lpUserName = pwszUser;
852 profileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
853
854 if (pfnLoadUserProfileW(*phToken, &profileInfo))
855 {
856 PRTUTF16 pwszzBlock;
857 rc = rtProcEnvironmentCreateFromToken(*phToken, hEnv, &pwszzBlock);
858 if (RT_SUCCESS(rc))
859 {
860 /*
861 * Useful KB articles:
862 * http://support.microsoft.com/kb/165194/
863 * http://support.microsoft.com/kb/184802/
864 * http://support.microsoft.com/kb/327618/
865 */
866 fRc = CreateProcessAsUserW(*phToken,
867 pwszExec,
868 pwszCmdLine,
869 NULL, /* pProcessAttributes */
870 NULL, /* pThreadAttributes */
871 TRUE, /* fInheritHandles */
872 dwCreationFlags,
873 pwszzBlock,
874 NULL, /* pCurrentDirectory */
875 pStartupInfo,
876 pProcInfo);
877 if (fRc)
878 dwErr = NO_ERROR;
879 else
880 dwErr = GetLastError(); /* CreateProcessAsUserW() failed. */
881 rtProcEnvironmentDestroy(pwszzBlock);
882 }
883 else
884 dwErr = rc;
885 pfnUnloadUserProfile(*phToken, profileInfo.hProfile);
886 }
887 else
888 dwErr = GetLastError(); /* LoadUserProfileW() failed. */
889 }
890 }
891 RTLdrClose(hUserenv);
892 }
893 if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
894 CloseHandle(hTokenUserDesktop);
895 rtProcUserLogoff(hTokenLogon);
896 }
897 else
898 dwErr = rc; /* rtProcUserLogon() failed. */
899 }
900
901 if (dwErr != NO_ERROR)
902 {
903 /*
904 * Map some important or much used Windows error codes
905 * to our error codes.
906 */
907 switch (dwErr)
908 {
909 case ERROR_NOACCESS:
910 case ERROR_PRIVILEGE_NOT_HELD:
911 rc = VERR_PERMISSION_DENIED;
912 break;
913
914 case ERROR_PASSWORD_EXPIRED:
915 case ERROR_ACCOUNT_RESTRICTION: /* See: http://support.microsoft.com/kb/303846/ */
916 rc = VERR_AUTHENTICATION_FAILURE;
917 break;
918
919 default:
920 /* Could trigger a debug assertion! */
921 rc = RTErrConvertFromWin32(dwErr);
922 break;
923 }
924 }
925 else
926 rc = VINF_SUCCESS;
927 return rc;
928}
929
930RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
931 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
932 const char *pszPassword, PRTPROCESS phProcess)
933{
934 /*
935 * Input validation
936 */
937 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
938 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
939 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SERVICE)), VERR_INVALID_PARAMETER);
940 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
941 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
942 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
943 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
944 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
945 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
946 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
947 /** @todo search the PATH (add flag for this). */
948
949 /*
950 * Initialize the globals.
951 */
952 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
953 AssertRCReturn(rc, rc);
954
955 /*
956 * Get the file descriptors for the handles we've been passed.
957 *
958 * It seems there is no point in trying to convince a child process's CRT
959 * that any of the standard file handles is non-TEXT. So, we don't...
960 */
961 STARTUPINFOW StartupInfo;
962 RT_ZERO(StartupInfo);
963 StartupInfo.cb = sizeof(StartupInfo);
964 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
965#if 1 /* The CRT should keep the standard handles up to date. */
966 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
967 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
968 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
969#else
970 StartupInfo.hStdInput = _get_osfhandle(0);
971 StartupInfo.hStdOutput = _get_osfhandle(1);
972 StartupInfo.hStdError = _get_osfhandle(2);
973#endif
974 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
975 HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
976 DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
977 for (int i = 0; i < 3; i++)
978 {
979 if (paHandles[i])
980 {
981 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
982 switch (paHandles[i]->enmType)
983 {
984 case RTHANDLETYPE_FILE:
985 *aphStds[i] = paHandles[i]->u.hFile != NIL_RTFILE
986 ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
987 : INVALID_HANDLE_VALUE;
988 break;
989
990 case RTHANDLETYPE_PIPE:
991 *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
992 ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
993 : INVALID_HANDLE_VALUE;
994 break;
995
996 case RTHANDLETYPE_SOCKET:
997 *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
998 ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
999 : INVALID_HANDLE_VALUE;
1000 break;
1001
1002 default:
1003 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
1004 }
1005
1006 /* Get the inheritability of the handle. */
1007 if (*aphStds[i] != INVALID_HANDLE_VALUE)
1008 {
1009 if (!GetHandleInformation(*aphStds[i], &afInhStds[i]))
1010 {
1011 rc = RTErrConvertFromWin32(GetLastError());
1012 AssertMsgFailedReturn(("%Rrc %p\n", rc, *aphStds[i]), rc);
1013 }
1014 }
1015 }
1016 }
1017
1018 /*
1019 * Set the inheritability any handles we're handing the child.
1020 */
1021 rc = VINF_SUCCESS;
1022 for (int i = 0; i < 3; i++)
1023 if ( (afInhStds[i] != 0xffffffff)
1024 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
1025 {
1026 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
1027 {
1028 rc = RTErrConvertFromWin32(GetLastError());
1029 AssertMsgFailedBreak(("%Rrc %p\n", rc, *aphStds[i]));
1030 }
1031 }
1032
1033 /*
1034 * Create the environment block, command line and convert the executable
1035 * name.
1036 */
1037 PRTUTF16 pwszzBlock;
1038 if (RT_SUCCESS(rc))
1039 rc = RTEnvQueryUtf16Block(hEnv, &pwszzBlock);
1040 if (RT_SUCCESS(rc))
1041 {
1042 PRTUTF16 pwszCmdLine;
1043 rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
1044 if (RT_SUCCESS(rc))
1045 {
1046 PRTUTF16 pwszExec;
1047 rc = RTStrToUtf16(pszExec, &pwszExec);
1048 if (RT_SUCCESS(rc))
1049 {
1050 /*
1051 * Get going...
1052 */
1053 PROCESS_INFORMATION ProcInfo;
1054 RT_ZERO(ProcInfo);
1055 DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
1056 if (fFlags & RTPROC_FLAGS_DETACHED)
1057 dwCreationFlags |= DETACHED_PROCESS;
1058
1059 /*
1060 * Only use the normal CreateProcess stuff if we have no user name
1061 * and we are not running from a (Windows) service. Otherwise use
1062 * the more advanced version in rtProcCreateAsUserHlp().
1063 */
1064 if ( pszAsUser == NULL
1065 && !(fFlags & RTPROC_FLAGS_SERVICE))
1066 {
1067 if (CreateProcessW(pwszExec,
1068 pwszCmdLine,
1069 NULL, /* pProcessAttributes */
1070 NULL, /* pThreadAttributes */
1071 TRUE, /* fInheritHandles */
1072 dwCreationFlags,
1073 pwszzBlock,
1074 NULL, /* pCurrentDirectory */
1075 &StartupInfo,
1076 &ProcInfo))
1077 rc = VINF_SUCCESS;
1078 else
1079 rc = RTErrConvertFromWin32(GetLastError());
1080 }
1081 else
1082 {
1083 /*
1084 * Convert the additional parameters and use a helper
1085 * function to do the actual work.
1086 */
1087 PRTUTF16 pwszUser;
1088 rc = RTStrToUtf16(pszAsUser, &pwszUser);
1089 if (RT_SUCCESS(rc))
1090 {
1091 PRTUTF16 pwszPassword;
1092 rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
1093 if (RT_SUCCESS(rc))
1094 {
1095 rc = rtProcCreateAsUserHlp(pwszUser, pwszPassword,
1096 pwszExec, pwszCmdLine, hEnv, dwCreationFlags,
1097 &StartupInfo, &ProcInfo, fFlags);
1098
1099 RTUtf16Free(pwszPassword);
1100 }
1101 RTUtf16Free(pwszUser);
1102 }
1103 }
1104 if (RT_SUCCESS(rc))
1105 {
1106 CloseHandle(ProcInfo.hThread);
1107 if (phProcess)
1108 {
1109 /*
1110 * Add the process to the child process list so
1111 * RTProcWait can reuse and close the process handle.
1112 */
1113 rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
1114 *phProcess = ProcInfo.dwProcessId;
1115 }
1116 else
1117 CloseHandle(ProcInfo.hProcess);
1118 rc = VINF_SUCCESS;
1119 }
1120 RTUtf16Free(pwszExec);
1121 }
1122 RTUtf16Free(pwszCmdLine);
1123 }
1124 RTEnvFreeUtf16Block(pwszzBlock);
1125 }
1126
1127 /* Undo any handle inherit changes. */
1128 for (int i = 0; i < 3; i++)
1129 if ( (afInhStds[i] != 0xffffffff)
1130 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
1131 {
1132 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0))
1133 AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
1134 }
1135
1136 return rc;
1137}
1138
1139
1140
1141RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
1142{
1143 AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
1144 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
1145 AssertRCReturn(rc, rc);
1146
1147 /*
1148 * Try find the process among the ones we've spawned, otherwise, attempt
1149 * opening the specified process.
1150 */
1151 HANDLE hProcess = rtProcWinFindPid(Process);
1152 if (hProcess == NULL)
1153 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
1154 if (hProcess != NULL)
1155 {
1156 /*
1157 * Wait for it to terminate.
1158 */
1159 DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
1160 DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
1161 while (WaitRc == WAIT_IO_COMPLETION)
1162 WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
1163 switch (WaitRc)
1164 {
1165 /*
1166 * It has terminated.
1167 */
1168 case WAIT_OBJECT_0:
1169 {
1170 DWORD dwExitCode;
1171 if (GetExitCodeProcess(hProcess, &dwExitCode))
1172 {
1173 /** @todo the exit code can be special statuses. */
1174 if (pProcStatus)
1175 {
1176 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
1177 pProcStatus->iStatus = (int)dwExitCode;
1178 }
1179 rtProcWinRemovePid(Process);
1180 return VINF_SUCCESS;
1181 }
1182 break;
1183 }
1184
1185 /*
1186 * It hasn't terminated just yet.
1187 */
1188 case WAIT_TIMEOUT:
1189 return VERR_PROCESS_RUNNING;
1190
1191 /*
1192 * Something went wrong...
1193 */
1194 case WAIT_FAILED:
1195 break;
1196 case WAIT_ABANDONED:
1197 AssertFailed();
1198 return VERR_GENERAL_FAILURE;
1199 default:
1200 AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
1201 return VERR_GENERAL_FAILURE;
1202 }
1203 }
1204 DWORD dwErr = GetLastError();
1205 if (dwErr == ERROR_INVALID_PARAMETER)
1206 return VERR_PROCESS_NOT_FOUND;
1207 return RTErrConvertFromWin32(dwErr);
1208}
1209
1210
1211RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
1212{
1213 /** @todo this isn't quite right. */
1214 return RTProcWait(Process, fFlags, pProcStatus);
1215}
1216
1217
1218RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
1219{
1220 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
1221 AssertRCReturn(rc, rc);
1222
1223 /*
1224 * Try find the process among the ones we've spawned, otherwise, attempt
1225 * opening the specified process.
1226 */
1227 HANDLE hProcess = rtProcWinFindPid(Process);
1228 if (hProcess != NULL)
1229 {
1230 if (!TerminateProcess(hProcess, 127))
1231 rc = RTErrConvertFromWin32(GetLastError());
1232 }
1233 else
1234 {
1235 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
1236 if (hProcess != NULL)
1237 {
1238 BOOL fRc = TerminateProcess(hProcess, 127);
1239 DWORD dwErr = GetLastError();
1240 CloseHandle(hProcess);
1241 if (!fRc)
1242 rc = RTErrConvertFromWin32(dwErr);
1243 }
1244 }
1245 return rc;
1246}
1247
1248
1249RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
1250{
1251 DWORD_PTR dwProcessAffinityMask = 0xffffffff;
1252 DWORD_PTR dwSystemAffinityMask;
1253
1254 BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
1255 Assert(fRc);
1256
1257 return dwProcessAffinityMask;
1258}
1259
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