[KERNEL32]
[reactos.git] / reactos / dll / win32 / kernel32 / client / proc.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/proc/proc.c
6 * PURPOSE: Process functions
7 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
8 * UPDATE HISTORY:
9 * Created 01/11/98
10 */
11
12 /* INCLUDES ****************************************************************/
13
14 #include <k32.h>
15
16 #define NDEBUG
17 #include <debug.h>
18
19 typedef INT (WINAPI *MessageBoxW_Proc) (HWND, LPCWSTR, LPCWSTR, UINT);
20
21 /* GLOBALS *******************************************************************/
22
23 static UNICODE_STRING CommandLineStringW;
24 static ANSI_STRING CommandLineStringA;
25 UNICODE_STRING BasePathVariableName = RTL_CONSTANT_STRING(L"PATH");
26
27 static BOOL bCommandLineInitialized = FALSE;
28
29 WaitForInputIdleType lpfnGlobalRegisterWaitForInputIdle;
30
31 LPSTARTUPINFOA lpLocalStartupInfo = NULL;
32
33 VOID WINAPI
34 RegisterWaitForInputIdle(WaitForInputIdleType lpfnRegisterWaitForInputIdle);
35
36 PLDR_DATA_TABLE_ENTRY BasepExeLdrEntry;
37
38 #define CMD_STRING L"cmd /c "
39
40 extern __declspec(noreturn)
41 VOID
42 CALLBACK
43 ConsoleControlDispatcher(DWORD CodeAndFlag);
44
45 BOOLEAN g_AppCertInitialized;
46 BOOLEAN g_HaveAppCerts;
47 LIST_ENTRY BasepAppCertDllsList;
48 RTL_CRITICAL_SECTION gcsAppCert;
49 PBASEP_APPCERT_EMBEDDED_FUNC fEmbeddedCertFunc;
50 NTSTATUS g_AppCertStatus;
51
52 RTL_QUERY_REGISTRY_TABLE BasepAppCertTable[2] =
53 {
54 {
55 BasepConfigureAppCertDlls,
56 1,
57 L"AppCertDlls",
58 &BasepAppCertDllsList,
59 0,
60 NULL,
61 0
62 }
63 };
64
65 PSAFER_REPLACE_PROCESS_THREAD_TOKENS g_SaferReplaceProcessThreadTokens;
66 HMODULE gSaferHandle = (HMODULE)-1;
67
68 /* FUNCTIONS ****************************************************************/
69
70 VOID
71 WINAPI
72 StuffStdHandle(IN HANDLE ProcessHandle,
73 IN HANDLE StandardHandle,
74 IN PHANDLE Address)
75 {
76 NTSTATUS Status;
77 HANDLE DuplicatedHandle;
78 SIZE_T Dummy;
79
80 /* Duplicate the handle */
81 Status = NtDuplicateObject(NtCurrentProcess(),
82 StandardHandle,
83 ProcessHandle,
84 &DuplicatedHandle,
85 DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES,
86 0,
87 0);
88 if (NT_SUCCESS(Status))
89 {
90 /* Write it */
91 NtWriteVirtualMemory(ProcessHandle,
92 Address,
93 &DuplicatedHandle,
94 sizeof(HANDLE),
95 &Dummy);
96 }
97 }
98
99 BOOLEAN
100 WINAPI
101 BuildSubSysCommandLine(IN LPWSTR SubsystemName,
102 IN LPWSTR ApplicationName,
103 IN LPWSTR CommandLine,
104 OUT PUNICODE_STRING SubsysCommandLine)
105 {
106 UNICODE_STRING CommandLineString, ApplicationNameString;
107 PWCHAR Buffer;
108 ULONG Length;
109
110 /* Convert to unicode strings */
111 RtlInitUnicodeString(&CommandLineString, ApplicationName);
112 RtlInitUnicodeString(&ApplicationNameString, CommandLine);
113
114 /* Allocate buffer for the output string */
115 Length = CommandLineString.MaximumLength + ApplicationNameString.MaximumLength + 32;
116 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
117 RtlInitEmptyUnicodeString(SubsysCommandLine, Buffer, Length);
118 if (!Buffer)
119 {
120 /* Fail, no memory */
121 BaseSetLastNTError(STATUS_NO_MEMORY);
122 return FALSE;
123 }
124
125 /* Build the final subsystem command line */
126 RtlAppendUnicodeToString(SubsysCommandLine, SubsystemName);
127 RtlAppendUnicodeStringToString(SubsysCommandLine, &CommandLineString);
128 RtlAppendUnicodeToString(SubsysCommandLine, L" /C ");
129 RtlAppendUnicodeStringToString(SubsysCommandLine, &ApplicationNameString);
130 return TRUE;
131 }
132
133 BOOLEAN
134 WINAPI
135 BasepIsImageVersionOk(IN ULONG ImageMajorVersion,
136 IN ULONG ImageMinorVersion)
137 {
138 /* Accept images for NT 3.1 or higher, as long as they're not newer than us */
139 return ((ImageMajorVersion >= 3) &&
140 ((ImageMajorVersion != 3) ||
141 (ImageMinorVersion >= 10)) &&
142 (ImageMajorVersion <= SharedUserData->NtMajorVersion) &&
143 ((ImageMajorVersion != SharedUserData->NtMajorVersion) ||
144 (ImageMinorVersion <= SharedUserData->NtMinorVersion)));
145 }
146
147 NTSTATUS
148 WINAPI
149 BasepCheckWebBladeHashes(IN HANDLE FileHandle)
150 {
151 NTSTATUS Status;
152 CHAR Hash[16];
153
154 /* Get all the MD5 hashes */
155 Status = RtlComputeImportTableHash(FileHandle, Hash, 1);
156 if (!NT_SUCCESS(Status)) return Status;
157
158 /* Depending on which suite this is, run a bsearch and block the appropriate ones */
159 if (SharedUserData->SuiteMask & VER_SUITE_COMPUTE_SERVER)
160 {
161 DPRINT1("Egad! This is a ReactOS Compute Server and we should prevent you from using certain APIs...but we won't.");
162 }
163 else if (SharedUserData->SuiteMask & VER_SUITE_STORAGE_SERVER)
164 {
165 DPRINT1("Gasp! This is a ReactOS Storage Server and we should prevent you from using certain APIs...but we won't.");
166 }
167 else if (SharedUserData->SuiteMask & VER_SUITE_BLADE)
168 {
169 DPRINT1("Golly! This is a ReactOS Web Blade Server and we should prevent you from using certain APIs...but we won't.");
170 }
171
172 /* Actually, fuck it, don't block anything, we're open source */
173 return STATUS_SUCCESS;
174 }
175
176 NTSTATUS
177 NTAPI
178 BasepSaveAppCertRegistryValue(IN PLIST_ENTRY List,
179 IN PWCHAR ComponentName,
180 IN PWCHAR DllName)
181 {
182 /* Pretty much the only thing this key is used for, is malware */
183 UNIMPLEMENTED;
184 return STATUS_NOT_IMPLEMENTED;
185 }
186
187 NTSTATUS
188 NTAPI
189 BasepConfigureAppCertDlls(IN PWSTR ValueName,
190 IN ULONG ValueType,
191 IN PVOID ValueData,
192 IN ULONG ValueLength,
193 IN PVOID Context,
194 IN PVOID EntryContext)
195 {
196 /* Add this to the certification list */
197 return BasepSaveAppCertRegistryValue(Context, ValueName, ValueData);
198 }
199
200 NTSTATUS
201 WINAPI
202 BasepIsProcessAllowed(IN PCHAR ApplicationName)
203 {
204 NTSTATUS Status;
205 PWCHAR Buffer;
206 UINT Length;
207 HMODULE TrustLibrary;
208 PBASEP_APPCERT_ENTRY Entry;
209 ULONG CertFlag;
210 PLIST_ENTRY NextEntry;
211 HANDLE KeyHandle;
212 UNICODE_STRING CertKey = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\AppCertDlls");
213 OBJECT_ATTRIBUTES KeyAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&CertKey, OBJ_CASE_INSENSITIVE);
214
215 /* Try to initialize the certification subsystem */
216 while (!g_AppCertInitialized)
217 {
218 /* Defaults */
219 Status = STATUS_SUCCESS;
220 Buffer = NULL;
221
222 /* Acquire the lock while initializing and see if we lost a race */
223 RtlEnterCriticalSection(&gcsAppCert);
224 if (g_AppCertInitialized) break;
225
226 /* On embedded, there is a special DLL */
227 if (SharedUserData->SuiteMask & VER_SUITE_EMBEDDEDNT)
228 {
229 /* Allocate a buffer for the name */
230 Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
231 0,
232 MAX_PATH * sizeof(WCHAR) +
233 sizeof(UNICODE_NULL));
234 if (!Buffer)
235 {
236 /* Fail if no memory */
237 Status = STATUS_NO_MEMORY;
238 }
239 else
240 {
241 /* Now get the system32 directory in our buffer, make sure it fits */
242 Length = GetSystemDirectoryW(Buffer, MAX_PATH - sizeof("EmbdTrst.DLL"));
243 if ((Length) && (Length <= MAX_PATH - sizeof("EmbdTrst.DLL")))
244 {
245 /* Add a slash if needed, and add the embedded cert DLL name */
246 if (Buffer[Length - 1] != '\\') Buffer[Length++] = '\\';
247 RtlCopyMemory(&Buffer[Length],
248 L"EmbdTrst.DLL",
249 sizeof(L"EmbdTrst.DLL"));
250
251 /* Try to load it */
252 TrustLibrary = LoadLibraryW(Buffer);
253 if (TrustLibrary)
254 {
255 /* And extract the special function out of it */
256 fEmbeddedCertFunc = (PVOID)GetProcAddress(TrustLibrary,
257 "ImageOkToRunOnEmbeddedNT");
258 }
259 }
260
261 /* If we didn't get this far, set a failure code */
262 if (!fEmbeddedCertFunc) Status = STATUS_UNSUCCESSFUL;
263 }
264 }
265 else
266 {
267 /* Other systems have a registry entry for this */
268 Status = NtOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);
269 if (NT_SUCCESS(Status))
270 {
271 /* Close it, we'll query it through Rtl */
272 NtClose(KeyHandle);
273
274 /* Do the query, which will call a special callback */
275 Status = RtlQueryRegistryValues(2,
276 L"Session Manager",
277 BasepAppCertTable,
278 0,
279 0);
280 if (Status == 0xC0000034) Status = STATUS_SUCCESS;
281 }
282 }
283
284 /* Free any buffer if we had one */
285 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
286
287 /* Check for errors, or a missing embedded/custom certification DLL */
288 if (!NT_SUCCESS(Status) ||
289 (!(fEmbeddedCertFunc) && (IsListEmpty(&BasepAppCertDllsList))))
290 {
291 /* The subsystem is not active on this machine, so give up */
292 g_HaveAppCerts = FALSE;
293 g_AppCertStatus = Status;
294 }
295 else
296 {
297 /* We have certification DLLs active, remember this */
298 g_HaveAppCerts = TRUE;
299 }
300
301 /* We are done the initialization phase, release the lock */
302 g_AppCertInitialized = TRUE;
303 RtlLeaveCriticalSection(&gcsAppCert);
304 }
305
306 /* If there's no certification DLLs present, return the failure code */
307 if (!g_HaveAppCerts) return g_AppCertStatus;
308
309 /* Otherwise, assume success and make sure we have *something* */
310 ASSERT(fEmbeddedCertFunc || !IsListEmpty(&BasepAppCertDllsList));
311 Status = STATUS_SUCCESS;
312
313 /* If the something is an embedded certification DLL, call it and return */
314 if (fEmbeddedCertFunc) return fEmbeddedCertFunc(ApplicationName);
315
316 /* Otherwise we have custom certification DLLs, parse them */
317 NextEntry = BasepAppCertDllsList.Flink;
318 CertFlag = 2;
319 while (NextEntry != &BasepAppCertDllsList)
320 {
321 /* Make sure the entry has a callback */
322 Entry = CONTAINING_RECORD(NextEntry, BASEP_APPCERT_ENTRY, Entry);
323 ASSERT(Entry->fPluginCertFunc != NULL);
324
325 /* Call it and check if it failed */
326 Status = Entry->fPluginCertFunc(ApplicationName, 1);
327 if (!NT_SUCCESS(Status)) CertFlag = 3;
328
329 /* Move on */
330 NextEntry = NextEntry->Flink;
331 }
332
333 /* Now loop them again */
334 NextEntry = BasepAppCertDllsList.Flink;
335 while (NextEntry != &BasepAppCertDllsList)
336 {
337 /* Make sure the entry has a callback */
338 Entry = CONTAINING_RECORD(NextEntry, BASEP_APPCERT_ENTRY, Entry);
339 ASSERT(Entry->fPluginCertFunc != NULL);
340
341 /* Call it, this time with the flag from the loop above */
342 Status = Entry->fPluginCertFunc(ApplicationName, CertFlag);
343 }
344
345 /* All done, return the status */
346 return Status;
347 }
348
349 NTSTATUS
350 WINAPI
351 BasepReplaceProcessThreadTokens(IN HANDLE TokenHandle,
352 IN HANDLE ProcessHandle,
353 IN HANDLE ThreadHandle)
354 {
355 NTSTATUS Status;
356 ANSI_STRING SaferiReplaceProcessThreadTokens = RTL_CONSTANT_STRING("SaferiReplaceProcessThreadTokens");
357
358 /* Enter the application certification lock */
359 RtlEnterCriticalSection(&gcsAppCert);
360
361 /* Check if we already know the function */
362 if (g_SaferReplaceProcessThreadTokens)
363 {
364 /* Call it */
365 Status = g_SaferReplaceProcessThreadTokens(TokenHandle,
366 ProcessHandle,
367 ThreadHandle) ?
368 STATUS_SUCCESS :
369 STATUS_UNSUCCESSFUL;
370 }
371 else
372 {
373 /* Check if the app certification DLL isn't loaded */
374 if (!(gSaferHandle) ||
375 (gSaferHandle == (HMODULE)-1) ||
376 (gSaferHandle == (HMODULE)-2))
377 {
378 /* Then we can't call the function */
379 Status = STATUS_ENTRYPOINT_NOT_FOUND;
380 }
381 else
382 {
383 /* We have the DLL, find the address of the Safer function */
384 Status = LdrGetProcedureAddress(gSaferHandle,
385 &SaferiReplaceProcessThreadTokens,
386 0,
387 (PVOID*)&g_SaferReplaceProcessThreadTokens);
388 if (NT_SUCCESS(Status))
389 {
390 /* Found it, now call it */
391 Status = g_SaferReplaceProcessThreadTokens(TokenHandle,
392 ProcessHandle,
393 ThreadHandle) ?
394 STATUS_SUCCESS :
395 STATUS_UNSUCCESSFUL;
396 }
397 else
398 {
399 /* We couldn't find it, so this must be an unsupported DLL */
400 LdrUnloadDll(gSaferHandle);
401 gSaferHandle = NULL;
402 Status = STATUS_ENTRYPOINT_NOT_FOUND;
403 }
404 }
405 }
406
407 /* Release the lock and return the result */
408 RtlLeaveCriticalSection(&gcsAppCert);
409 return Status;
410 }
411
412 VOID
413 WINAPI
414 BasepSxsCloseHandles(IN PBASE_MSG_SXS_HANDLES Handles)
415 {
416 NTSTATUS Status;
417
418 /* Sanity checks */
419 ASSERT(Handles != NULL);
420 ASSERT(Handles->Process == NULL || Handles->Process == NtCurrentProcess());
421
422 /* Close the file handle */
423 if (Handles->File)
424 {
425 Status = NtClose(Handles->File);
426 ASSERT(NT_SUCCESS(Status));
427 }
428
429 /* Close the section handle */
430 if (Handles->Section)
431 {
432 Status = NtClose(Handles->Section);
433 ASSERT(NT_SUCCESS(Status));
434 }
435
436 /* Unmap the section view */
437 if (Handles->ViewBase.QuadPart)
438 {
439 Status = NtUnmapViewOfSection(NtCurrentProcess(),
440 (PVOID)Handles->ViewBase.LowPart);
441 ASSERT(NT_SUCCESS(Status));
442 }
443 }
444
445 static
446 LONG BaseExceptionFilter(EXCEPTION_POINTERS *ExceptionInfo)
447 {
448 LONG ExceptionDisposition = EXCEPTION_EXECUTE_HANDLER;
449 LPTOP_LEVEL_EXCEPTION_FILTER RealFilter;
450 RealFilter = RtlDecodePointer(GlobalTopLevelExceptionFilter);
451
452 if (RealFilter != NULL)
453 {
454 _SEH2_TRY
455 {
456 ExceptionDisposition = RealFilter(ExceptionInfo);
457 }
458 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
459 {
460 }
461 _SEH2_END;
462 }
463 if ((ExceptionDisposition == EXCEPTION_CONTINUE_SEARCH || ExceptionDisposition == EXCEPTION_EXECUTE_HANDLER) &&
464 RealFilter != UnhandledExceptionFilter)
465 {
466 ExceptionDisposition = UnhandledExceptionFilter(ExceptionInfo);
467 }
468
469 return ExceptionDisposition;
470 }
471
472 VOID
473 WINAPI
474 BaseProcessStartup(PPROCESS_START_ROUTINE lpStartAddress)
475 {
476 UINT uExitCode = 0;
477
478 DPRINT("BaseProcessStartup(..) - setting up exception frame.\n");
479
480 _SEH2_TRY
481 {
482 /* Set our Start Address */
483 NtSetInformationThread(NtCurrentThread(),
484 ThreadQuerySetWin32StartAddress,
485 &lpStartAddress,
486 sizeof(PPROCESS_START_ROUTINE));
487
488 /* Call the Start Routine */
489 uExitCode = (lpStartAddress)();
490 }
491 _SEH2_EXCEPT(BaseExceptionFilter(_SEH2_GetExceptionInformation()))
492 {
493 /* Get the SEH Error */
494 uExitCode = _SEH2_GetExceptionCode();
495 }
496 _SEH2_END;
497
498 /* Exit the Process with our error */
499 ExitProcess(uExitCode);
500 }
501
502 /*
503 * Tells CSR that a new process was created
504 */
505 NTSTATUS
506 WINAPI
507 BasepNotifyCsrOfCreation(ULONG dwCreationFlags,
508 IN HANDLE ProcessId,
509 IN BOOL InheritHandles)
510 {
511 ULONG Request = CREATE_PROCESS;
512 CSR_API_MESSAGE CsrRequest;
513 NTSTATUS Status;
514
515 DPRINT("BasepNotifyCsrOfCreation: Process: %lx, Flags %lx\n",
516 ProcessId, dwCreationFlags);
517
518 /* Fill out the request */
519 CsrRequest.Data.CreateProcessRequest.NewProcessId = ProcessId;
520 CsrRequest.Data.CreateProcessRequest.Flags = dwCreationFlags;
521 CsrRequest.Data.CreateProcessRequest.bInheritHandles = InheritHandles;
522
523 /* Call CSR */
524 Status = CsrClientCallServer(&CsrRequest,
525 NULL,
526 MAKE_CSR_API(Request, CSR_NATIVE),
527 sizeof(CSR_API_MESSAGE));
528 if (!NT_SUCCESS(Status) || !NT_SUCCESS(CsrRequest.Status))
529 {
530 DPRINT1("Failed to tell csrss about new process\n");
531 return CsrRequest.Status;
532 }
533
534 /* Return Success */
535 return STATUS_SUCCESS;
536 }
537
538 NTSTATUS
539 WINAPI
540 BasepNotifyCsrOfThread(IN HANDLE ThreadHandle,
541 IN PCLIENT_ID ClientId)
542 {
543 ULONG Request = CREATE_THREAD;
544 CSR_API_MESSAGE CsrRequest;
545 NTSTATUS Status;
546
547 DPRINT("BasepNotifyCsrOfThread: Thread: %lx, Handle %lx\n",
548 ClientId->UniqueThread, ThreadHandle);
549
550 /* Fill out the request */
551 CsrRequest.Data.CreateThreadRequest.ClientId = *ClientId;
552 CsrRequest.Data.CreateThreadRequest.ThreadHandle = ThreadHandle;
553
554 /* Call CSR */
555 Status = CsrClientCallServer(&CsrRequest,
556 NULL,
557 MAKE_CSR_API(Request, CSR_NATIVE),
558 sizeof(CSR_API_MESSAGE));
559 if (!NT_SUCCESS(Status) || !NT_SUCCESS(CsrRequest.Status))
560 {
561 DPRINT1("Failed to tell csrss about new thread\n");
562 return CsrRequest.Status;
563 }
564
565 /* Return Success */
566 return STATUS_SUCCESS;
567 }
568
569 /*
570 * Creates the first Thread in a Proces
571 */
572 HANDLE
573 WINAPI
574 BasepCreateFirstThread(HANDLE ProcessHandle,
575 LPSECURITY_ATTRIBUTES lpThreadAttributes,
576 PSECTION_IMAGE_INFORMATION SectionImageInfo,
577 PCLIENT_ID ClientId)
578 {
579 OBJECT_ATTRIBUTES LocalObjectAttributes;
580 POBJECT_ATTRIBUTES ObjectAttributes;
581 CONTEXT Context;
582 INITIAL_TEB InitialTeb;
583 NTSTATUS Status;
584 HANDLE hThread;
585
586 DPRINT("BasepCreateFirstThread. hProcess: %lx\n", ProcessHandle);
587
588 /* Create the Thread's Stack */
589 BaseCreateStack(ProcessHandle,
590 SectionImageInfo->MaximumStackSize,
591 SectionImageInfo->CommittedStackSize,
592 &InitialTeb);
593
594 /* Create the Thread's Context */
595 BaseInitializeContext(&Context,
596 NtCurrentPeb(),
597 SectionImageInfo->TransferAddress,
598 InitialTeb.StackBase,
599 0);
600
601 /* Convert the thread attributes */
602 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
603 lpThreadAttributes,
604 NULL);
605
606 /* Create the Kernel Thread Object */
607 Status = NtCreateThread(&hThread,
608 THREAD_ALL_ACCESS,
609 ObjectAttributes,
610 ProcessHandle,
611 ClientId,
612 &Context,
613 &InitialTeb,
614 TRUE);
615 if (!NT_SUCCESS(Status))
616 {
617 return NULL;
618 }
619
620 Status = BasepNotifyCsrOfThread(hThread, ClientId);
621 if (!NT_SUCCESS(Status))
622 {
623 ASSERT(FALSE);
624 }
625
626 /* Success */
627 return hThread;
628 }
629
630 /*
631 * Converts ANSI to Unicode Environment
632 */
633 PVOID
634 WINAPI
635 BasepConvertUnicodeEnvironment(OUT SIZE_T* EnvSize,
636 IN PVOID lpEnvironment)
637 {
638 PCHAR pcScan;
639 ANSI_STRING AnsiEnv;
640 UNICODE_STRING UnicodeEnv;
641 NTSTATUS Status;
642
643 DPRINT("BasepConvertUnicodeEnvironment\n");
644
645 /* Scan the environment to calculate its Unicode size */
646 AnsiEnv.Buffer = pcScan = (PCHAR)lpEnvironment;
647 while (*pcScan)
648 {
649 pcScan += strlen(pcScan) + 1;
650 }
651
652 /* Create our ANSI String */
653 if (pcScan == (PCHAR)lpEnvironment)
654 {
655 AnsiEnv.Length = 2 * sizeof(CHAR);
656 }
657 else
658 {
659
660 AnsiEnv.Length = (USHORT)((ULONG_PTR)pcScan - (ULONG_PTR)lpEnvironment + sizeof(CHAR));
661 }
662 AnsiEnv.MaximumLength = AnsiEnv.Length + 1;
663
664 /* Allocate memory for the Unicode Environment */
665 UnicodeEnv.Buffer = NULL;
666 *EnvSize = AnsiEnv.MaximumLength * sizeof(WCHAR);
667 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
668 (PVOID)&UnicodeEnv.Buffer,
669 0,
670 EnvSize,
671 MEM_COMMIT,
672 PAGE_READWRITE);
673 /* Failure */
674 if (!NT_SUCCESS(Status))
675 {
676 SetLastError(Status);
677 *EnvSize = 0;
678 return NULL;
679 }
680
681 /* Use the allocated size */
682 UnicodeEnv.MaximumLength = (USHORT)*EnvSize;
683
684 /* Convert */
685 RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE);
686 return UnicodeEnv.Buffer;
687 }
688
689 /*
690 * Converts a Win32 Priority Class to NT
691 */
692 ULONG
693 WINAPI
694 BasepConvertPriorityClass(IN ULONG dwCreationFlags)
695 {
696 ULONG ReturnClass;
697
698 if(dwCreationFlags & IDLE_PRIORITY_CLASS)
699 {
700 ReturnClass = PROCESS_PRIORITY_CLASS_IDLE;
701 }
702 else if(dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS)
703 {
704 ReturnClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
705 }
706 else if(dwCreationFlags & NORMAL_PRIORITY_CLASS)
707 {
708 ReturnClass = PROCESS_PRIORITY_CLASS_NORMAL;
709 }
710 else if(dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS)
711 {
712 ReturnClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
713 }
714 else if(dwCreationFlags & HIGH_PRIORITY_CLASS)
715 {
716 ReturnClass = PROCESS_PRIORITY_CLASS_HIGH;
717 }
718 else if(dwCreationFlags & REALTIME_PRIORITY_CLASS)
719 {
720 /* Check for Privilege First */
721 if (BasepIsRealtimeAllowed(TRUE))
722 {
723 ReturnClass = PROCESS_PRIORITY_CLASS_REALTIME;
724 }
725 else
726 {
727 ReturnClass = PROCESS_PRIORITY_CLASS_HIGH;
728 }
729 }
730 else
731 {
732 ReturnClass = PROCESS_PRIORITY_CLASS_INVALID;
733 }
734
735 return ReturnClass;
736 }
737
738 /*
739 * Duplicates a standard handle and writes it where requested.
740 */
741 VOID
742 WINAPI
743 BasepDuplicateAndWriteHandle(IN HANDLE ProcessHandle,
744 IN HANDLE StandardHandle,
745 IN PHANDLE Address)
746 {
747 NTSTATUS Status;
748 HANDLE DuplicatedHandle;
749 SIZE_T Dummy;
750
751 DPRINT("BasepDuplicateAndWriteHandle. hProcess: %lx, Handle: %lx,"
752 "Address: %p\n", ProcessHandle, StandardHandle, Address);
753
754 /* Don't touch Console Handles */
755 if (IsConsoleHandle(StandardHandle)) return;
756
757 /* Duplicate the handle */
758 Status = NtDuplicateObject(NtCurrentProcess(),
759 StandardHandle,
760 ProcessHandle,
761 &DuplicatedHandle,
762 DUPLICATE_SAME_ACCESS | DUPLICATE_SAME_ATTRIBUTES,
763 0,
764 0);
765 if (NT_SUCCESS(Status))
766 {
767 /* Write it */
768 NtWriteVirtualMemory(ProcessHandle,
769 Address,
770 &DuplicatedHandle,
771 sizeof(HANDLE),
772 &Dummy);
773 }
774 }
775
776 BOOLEAN
777 WINAPI
778 BasePushProcessParameters(IN ULONG ParameterFlags,
779 IN HANDLE ProcessHandle,
780 IN PPEB RemotePeb,
781 IN LPCWSTR ApplicationPathName,
782 IN LPWSTR lpCurrentDirectory,
783 IN LPWSTR lpCommandLine,
784 IN LPVOID lpEnvironment,
785 IN LPSTARTUPINFOW StartupInfo,
786 IN DWORD CreationFlags,
787 IN BOOL InheritHandles,
788 IN ULONG ImageSubsystem,
789 IN PVOID AppCompatData,
790 IN ULONG AppCompatDataSize)
791 {
792 WCHAR FullPath[MAX_PATH + 5];
793 PWCHAR Remaining, DllPathString, ScanChar;
794 PRTL_USER_PROCESS_PARAMETERS ProcessParameters, RemoteParameters;
795 PVOID RemoteAppCompatData;
796 UNICODE_STRING DllPath, ImageName, CommandLine, CurrentDirectory;
797 UNICODE_STRING Desktop, Shell, Runtime, Title;
798 NTSTATUS Status;
799 ULONG EnviroSize;
800 SIZE_T Size;
801 BOOLEAN HavePebLock = FALSE, Result;
802 PPEB Peb = NtCurrentPeb();
803
804 /* Get the full path name */
805 Size = GetFullPathNameW(ApplicationPathName,
806 MAX_PATH + 4,
807 FullPath,
808 &Remaining);
809 if ((Size) && (Size <= (MAX_PATH + 4)))
810 {
811 /* Get the DLL Path */
812 DllPathString = BaseComputeProcessDllPath((LPWSTR)ApplicationPathName,
813 lpEnvironment);
814 if (!DllPathString)
815 {
816 /* Fail */
817 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
818 return FALSE;
819 }
820
821 /* Initialize Strings */
822 RtlInitUnicodeString(&DllPath, DllPathString);
823 RtlInitUnicodeString(&ImageName, ApplicationPathName);
824 }
825 else
826 {
827 /* Get the DLL Path */
828 DllPathString = BaseComputeProcessDllPath(FullPath, lpEnvironment);
829 if (!DllPathString)
830 {
831 /* Fail */
832 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
833 return FALSE;
834 }
835
836 /* Initialize Strings */
837 RtlInitUnicodeString(&DllPath, DllPathString);
838 RtlInitUnicodeString(&ImageName, FullPath);
839 }
840
841 /* Initialize Strings */
842 RtlInitUnicodeString(&CommandLine, lpCommandLine);
843 RtlInitUnicodeString(&CurrentDirectory, lpCurrentDirectory);
844
845 /* Initialize more Strings from the Startup Info */
846 if (StartupInfo->lpDesktop)
847 {
848 RtlInitUnicodeString(&Desktop, StartupInfo->lpDesktop);
849 }
850 else
851 {
852 RtlInitUnicodeString(&Desktop, L"");
853 }
854 if (StartupInfo->lpReserved)
855 {
856 RtlInitUnicodeString(&Shell, StartupInfo->lpReserved);
857 }
858 else
859 {
860 RtlInitUnicodeString(&Shell, L"");
861 }
862 if (StartupInfo->lpTitle)
863 {
864 RtlInitUnicodeString(&Title, StartupInfo->lpTitle);
865 }
866 else
867 {
868 RtlInitUnicodeString(&Title, ApplicationPathName);
869 }
870
871 /* This one is special because the length can differ */
872 Runtime.Buffer = (LPWSTR)StartupInfo->lpReserved2;
873 Runtime.MaximumLength = Runtime.Length = StartupInfo->cbReserved2;
874
875 /* Enforce no app compat data if the pointer was NULL */
876 if (!AppCompatData) AppCompatDataSize = 0;
877
878 /* Create the Parameter Block */
879 ProcessParameters = NULL;
880 Status = RtlCreateProcessParameters(&ProcessParameters,
881 &ImageName,
882 &DllPath,
883 lpCurrentDirectory ?
884 &CurrentDirectory : NULL,
885 &CommandLine,
886 lpEnvironment,
887 &Title,
888 &Desktop,
889 &Shell,
890 &Runtime);
891 if (!NT_SUCCESS(Status)) goto FailPath;
892
893 /* Clear the current directory handle if not inheriting */
894 if (!InheritHandles) ProcessParameters->CurrentDirectory.Handle = NULL;
895
896 /* Check if the user passed in an environment */
897 if (lpEnvironment)
898 {
899 /* We should've made it part of the parameters block, enforce this */
900 ASSERT(ProcessParameters->Environment == lpEnvironment);
901 lpEnvironment = ProcessParameters->Environment;
902 }
903 else
904 {
905 /* The user did not, so use the one from the current PEB */
906 HavePebLock = TRUE;
907 RtlAcquirePebLock();
908 lpEnvironment = Peb->ProcessParameters->Environment;
909 }
910
911 /* Save pointer and start lookup */
912 ScanChar = lpEnvironment;
913 if (lpEnvironment)
914 {
915 /* Find the environment size */
916 while ((ScanChar[0]) || (ScanChar[1])) ++ScanChar;
917 ScanChar += (2 * sizeof(UNICODE_NULL));
918 EnviroSize = (ULONG_PTR)ScanChar - (ULONG_PTR)lpEnvironment;
919
920 /* Allocate and Initialize new Environment Block */
921 Size = EnviroSize;
922 ProcessParameters->Environment = NULL;
923 Status = ZwAllocateVirtualMemory(ProcessHandle,
924 (PVOID*)&ProcessParameters->Environment,
925 0,
926 &Size,
927 MEM_COMMIT,
928 PAGE_READWRITE);
929 if (!NT_SUCCESS(Status)) goto FailPath;
930
931 /* Write the Environment Block */
932 Status = ZwWriteVirtualMemory(ProcessHandle,
933 ProcessParameters->Environment,
934 lpEnvironment,
935 EnviroSize,
936 NULL);
937
938 /* No longer need the PEB lock anymore */
939 if (HavePebLock)
940 {
941 /* Release it */
942 RtlReleasePebLock();
943 HavePebLock = FALSE;
944 }
945
946 /* Check if the write failed */
947 if (!NT_SUCCESS(Status)) goto FailPath;
948 }
949
950 /* Write new parameters */
951 ProcessParameters->StartingX = StartupInfo->dwX;
952 ProcessParameters->StartingY = StartupInfo->dwY;
953 ProcessParameters->CountX = StartupInfo->dwXSize;
954 ProcessParameters->CountY = StartupInfo->dwYSize;
955 ProcessParameters->CountCharsX = StartupInfo->dwXCountChars;
956 ProcessParameters->CountCharsY = StartupInfo->dwYCountChars;
957 ProcessParameters->FillAttribute = StartupInfo->dwFillAttribute;
958 ProcessParameters->WindowFlags = StartupInfo->dwFlags;
959 ProcessParameters->ShowWindowFlags = StartupInfo->wShowWindow;
960
961 /* Write the handles only if we have to */
962 if (StartupInfo->dwFlags &
963 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))
964 {
965 ProcessParameters->StandardInput = StartupInfo->hStdInput;
966 ProcessParameters->StandardOutput = StartupInfo->hStdOutput;
967 ProcessParameters->StandardError = StartupInfo->hStdError;
968 }
969
970 /* Use Special Flags for ConDllInitialize in Kernel32 */
971 if (CreationFlags & DETACHED_PROCESS)
972 {
973 ProcessParameters->ConsoleHandle = HANDLE_DETACHED_PROCESS;
974 }
975 else if (CreationFlags & CREATE_NO_WINDOW)
976 {
977 ProcessParameters->ConsoleHandle = HANDLE_CREATE_NO_WINDOW;
978 }
979 else if (CreationFlags & CREATE_NEW_CONSOLE)
980 {
981 ProcessParameters->ConsoleHandle = HANDLE_CREATE_NEW_CONSOLE;
982 }
983 else
984 {
985 /* Inherit our Console Handle */
986 ProcessParameters->ConsoleHandle = Peb->ProcessParameters->ConsoleHandle;
987
988 /* Make sure that the shell isn't trampling on our handles first */
989 if (!(StartupInfo->dwFlags &
990 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
991 {
992 /* Copy the handle if we are inheriting or if it's a console handle */
993 if ((InheritHandles) ||
994 (IsConsoleHandle(Peb->ProcessParameters->StandardInput)))
995 {
996 ProcessParameters->StandardInput = Peb->ProcessParameters->StandardInput;
997 }
998 if ((InheritHandles) ||
999 (IsConsoleHandle(Peb->ProcessParameters->StandardOutput)))
1000 {
1001 ProcessParameters->StandardOutput = Peb->ProcessParameters->StandardOutput;
1002 }
1003 if ((InheritHandles) ||
1004 (IsConsoleHandle(Peb->ProcessParameters->StandardError)))
1005 {
1006 ProcessParameters->StandardError = Peb->ProcessParameters->StandardError;
1007 }
1008 }
1009 }
1010
1011 /* Also set the Console Flag */
1012 if ((CreationFlags & CREATE_NEW_PROCESS_GROUP) &&
1013 (!(CreationFlags & CREATE_NEW_CONSOLE)))
1014 {
1015 ProcessParameters->ConsoleFlags = 1;
1016 }
1017
1018 /* See if the first 1MB should be reserved */
1019 if (ParameterFlags & 1)
1020 {
1021 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_RESERVE_1MB;
1022 }
1023
1024 /* See if the first 16MB should be reserved */
1025 if (ParameterFlags & 2)
1026 {
1027 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_RESERVE_16MB;
1028 }
1029
1030 /* Allocate memory for the parameter block */
1031 Size = ProcessParameters->Length;
1032 RemoteParameters = NULL;
1033 Status = NtAllocateVirtualMemory(ProcessHandle,
1034 (PVOID*)&RemoteParameters,
1035 0,
1036 &Size,
1037 MEM_COMMIT,
1038 PAGE_READWRITE);
1039 if (!NT_SUCCESS(Status)) goto FailPath;
1040
1041 /* Set the allocated size */
1042 ProcessParameters->MaximumLength = Size;
1043
1044 /* Handle some Parameter Flags */
1045 ProcessParameters->Flags |= (CreationFlags & PROFILE_USER) ?
1046 RTL_USER_PROCESS_PARAMETERS_PROFILE_USER : 0;
1047 ProcessParameters->Flags |= (CreationFlags & PROFILE_KERNEL) ?
1048 RTL_USER_PROCESS_PARAMETERS_PROFILE_KERNEL : 0;
1049 ProcessParameters->Flags |= (CreationFlags & PROFILE_SERVER) ?
1050 RTL_USER_PROCESS_PARAMETERS_PROFILE_SERVER : 0;
1051 ProcessParameters->Flags |= (Peb->ProcessParameters->Flags &
1052 RTL_USER_PROCESS_PARAMETERS_DISABLE_HEAP_CHECKS);
1053
1054 /* Write the Parameter Block */
1055 Status = NtWriteVirtualMemory(ProcessHandle,
1056 RemoteParameters,
1057 ProcessParameters,
1058 ProcessParameters->Length,
1059 NULL);
1060 if (!NT_SUCCESS(Status)) goto FailPath;
1061
1062 /* Write the PEB Pointer */
1063 Status = NtWriteVirtualMemory(ProcessHandle,
1064 &RemotePeb->ProcessParameters,
1065 &RemoteParameters,
1066 sizeof(PVOID),
1067 NULL);
1068 if (!NT_SUCCESS(Status)) goto FailPath;
1069
1070 /* Check if there's any app compat data to write */
1071 RemoteAppCompatData = NULL;
1072 if (AppCompatData)
1073 {
1074 /* Allocate some space for the application compatibility data */
1075 Size = AppCompatDataSize;
1076 Status = NtAllocateVirtualMemory(ProcessHandle,
1077 &RemoteAppCompatData,
1078 0,
1079 &Size,
1080 MEM_COMMIT,
1081 PAGE_READWRITE);
1082 if (!NT_SUCCESS(Status)) goto FailPath;
1083
1084 /* Write the application compatibility data */
1085 Status = NtWriteVirtualMemory(ProcessHandle,
1086 RemoteAppCompatData,
1087 AppCompatData,
1088 AppCompatDataSize,
1089 NULL);
1090 if (!NT_SUCCESS(Status)) goto FailPath;
1091 }
1092
1093 /* Write the PEB Pointer to the app compat data (might be NULL) */
1094 Status = NtWriteVirtualMemory(ProcessHandle,
1095 &RemotePeb->pShimData,
1096 &RemoteAppCompatData,
1097 sizeof(PVOID),
1098 NULL);
1099 if (!NT_SUCCESS(Status)) goto FailPath;
1100
1101 /* Now write Peb->ImageSubSystem */
1102 if (ImageSubsystem)
1103 {
1104 NtWriteVirtualMemory(ProcessHandle,
1105 &RemotePeb->ImageSubsystem,
1106 &ImageSubsystem,
1107 sizeof(ImageSubsystem),
1108 NULL);
1109 }
1110
1111 /* Success path */
1112 Result = TRUE;
1113
1114 Quickie:
1115 /* Cleanup */
1116 if (HavePebLock) RtlReleasePebLock();
1117 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath.Buffer);
1118 if (ProcessParameters) RtlDestroyProcessParameters(ProcessParameters);
1119 return Result;
1120 FailPath:
1121 DPRINT1("Failure to create proecss parameters: %lx\n", Status);
1122 BaseSetLastNTError(Status);
1123 Result = FALSE;
1124 goto Quickie;
1125 }
1126
1127 VOID
1128 WINAPI
1129 InitCommandLines(VOID)
1130 {
1131 PRTL_USER_PROCESS_PARAMETERS Params;
1132
1133 /* get command line */
1134 Params = NtCurrentPeb()->ProcessParameters;
1135 RtlNormalizeProcessParams (Params);
1136
1137 /* initialize command line buffers */
1138 CommandLineStringW.Length = Params->CommandLine.Length;
1139 CommandLineStringW.MaximumLength = CommandLineStringW.Length + sizeof(WCHAR);
1140 CommandLineStringW.Buffer = RtlAllocateHeap(GetProcessHeap(),
1141 HEAP_GENERATE_EXCEPTIONS | HEAP_ZERO_MEMORY,
1142 CommandLineStringW.MaximumLength);
1143 if (CommandLineStringW.Buffer == NULL)
1144 {
1145 return;
1146 }
1147
1148 RtlInitAnsiString(&CommandLineStringA, NULL);
1149
1150 /* Copy command line */
1151 RtlCopyUnicodeString(&CommandLineStringW,
1152 &(Params->CommandLine));
1153 CommandLineStringW.Buffer[CommandLineStringW.Length / sizeof(WCHAR)] = 0;
1154
1155 /* convert unicode string to ansi (or oem) */
1156 if (bIsFileApiAnsi)
1157 RtlUnicodeStringToAnsiString(&CommandLineStringA,
1158 &CommandLineStringW,
1159 TRUE);
1160 else
1161 RtlUnicodeStringToOemString(&CommandLineStringA,
1162 &CommandLineStringW,
1163 TRUE);
1164
1165 CommandLineStringA.Buffer[CommandLineStringA.Length] = 0;
1166
1167 bCommandLineInitialized = TRUE;
1168 }
1169
1170 /*
1171 * @implemented
1172 */
1173 BOOL
1174 WINAPI
1175 GetProcessAffinityMask(HANDLE hProcess,
1176 PDWORD_PTR lpProcessAffinityMask,
1177 PDWORD_PTR lpSystemAffinityMask)
1178 {
1179 PROCESS_BASIC_INFORMATION ProcessInfo;
1180 SYSTEM_BASIC_INFORMATION SystemInfo;
1181 NTSTATUS Status;
1182
1183 Status = NtQuerySystemInformation(SystemBasicInformation,
1184 &SystemInfo,
1185 sizeof(SystemInfo),
1186 NULL);
1187 if (!NT_SUCCESS(Status))
1188 {
1189 BaseSetLastNTError(Status);
1190 return FALSE;
1191 }
1192
1193 Status = NtQueryInformationProcess(hProcess,
1194 ProcessBasicInformation,
1195 (PVOID)&ProcessInfo,
1196 sizeof(PROCESS_BASIC_INFORMATION),
1197 NULL);
1198 if (!NT_SUCCESS(Status))
1199 {
1200 BaseSetLastNTError(Status);
1201 return FALSE;
1202 }
1203
1204 *lpProcessAffinityMask = (DWORD)ProcessInfo.AffinityMask;
1205 *lpSystemAffinityMask = (DWORD)SystemInfo.ActiveProcessorsAffinityMask;
1206
1207 return TRUE;
1208 }
1209
1210
1211 /*
1212 * @implemented
1213 */
1214 BOOL
1215 WINAPI
1216 SetProcessAffinityMask(HANDLE hProcess,
1217 DWORD_PTR dwProcessAffinityMask)
1218 {
1219 NTSTATUS Status;
1220
1221 Status = NtSetInformationProcess(hProcess,
1222 ProcessAffinityMask,
1223 (PVOID)&dwProcessAffinityMask,
1224 sizeof(DWORD));
1225 if (!NT_SUCCESS(Status))
1226 {
1227 BaseSetLastNTError(Status);
1228 return FALSE;
1229 }
1230
1231 return TRUE;
1232 }
1233
1234
1235 /*
1236 * @implemented
1237 */
1238 BOOL
1239 WINAPI
1240 GetProcessShutdownParameters(LPDWORD lpdwLevel,
1241 LPDWORD lpdwFlags)
1242 {
1243 CSR_API_MESSAGE CsrRequest;
1244 ULONG Request;
1245 NTSTATUS Status;
1246
1247 Request = GET_SHUTDOWN_PARAMETERS;
1248 Status = CsrClientCallServer(&CsrRequest,
1249 NULL,
1250 MAKE_CSR_API(Request, CSR_NATIVE),
1251 sizeof(CSR_API_MESSAGE));
1252 if (!NT_SUCCESS(Status) || !NT_SUCCESS(CsrRequest.Status))
1253 {
1254 BaseSetLastNTError(Status);
1255 return FALSE;
1256 }
1257
1258 *lpdwLevel = CsrRequest.Data.GetShutdownParametersRequest.Level;
1259 *lpdwFlags = CsrRequest.Data.GetShutdownParametersRequest.Flags;
1260
1261 return TRUE;
1262 }
1263
1264
1265 /*
1266 * @implemented
1267 */
1268 BOOL
1269 WINAPI
1270 SetProcessShutdownParameters(DWORD dwLevel,
1271 DWORD dwFlags)
1272 {
1273 CSR_API_MESSAGE CsrRequest;
1274 ULONG Request;
1275 NTSTATUS Status;
1276
1277 CsrRequest.Data.SetShutdownParametersRequest.Level = dwLevel;
1278 CsrRequest.Data.SetShutdownParametersRequest.Flags = dwFlags;
1279
1280 Request = SET_SHUTDOWN_PARAMETERS;
1281 Status = CsrClientCallServer(&CsrRequest,
1282 NULL,
1283 MAKE_CSR_API(Request, CSR_NATIVE),
1284 sizeof(CSR_API_MESSAGE));
1285 if (!NT_SUCCESS(Status) || !NT_SUCCESS(CsrRequest.Status))
1286 {
1287 BaseSetLastNTError(Status);
1288 return FALSE;
1289 }
1290
1291 return TRUE;
1292 }
1293
1294
1295 /*
1296 * @implemented
1297 */
1298 BOOL
1299 WINAPI
1300 GetProcessWorkingSetSize(HANDLE hProcess,
1301 PSIZE_T lpMinimumWorkingSetSize,
1302 PSIZE_T lpMaximumWorkingSetSize)
1303 {
1304 QUOTA_LIMITS QuotaLimits;
1305 NTSTATUS Status;
1306
1307 Status = NtQueryInformationProcess(hProcess,
1308 ProcessQuotaLimits,
1309 &QuotaLimits,
1310 sizeof(QUOTA_LIMITS),
1311 NULL);
1312 if (!NT_SUCCESS(Status))
1313 {
1314 BaseSetLastNTError(Status);
1315 return FALSE;
1316 }
1317
1318 *lpMinimumWorkingSetSize = QuotaLimits.MinimumWorkingSetSize;
1319 *lpMaximumWorkingSetSize = QuotaLimits.MaximumWorkingSetSize;
1320
1321 return TRUE;
1322 }
1323
1324
1325 /*
1326 * @implemented
1327 */
1328 BOOL
1329 WINAPI
1330 SetProcessWorkingSetSize(HANDLE hProcess,
1331 SIZE_T dwMinimumWorkingSetSize,
1332 SIZE_T dwMaximumWorkingSetSize)
1333 {
1334 QUOTA_LIMITS QuotaLimits;
1335 NTSTATUS Status;
1336
1337 QuotaLimits.MinimumWorkingSetSize = dwMinimumWorkingSetSize;
1338 QuotaLimits.MaximumWorkingSetSize = dwMaximumWorkingSetSize;
1339
1340 Status = NtSetInformationProcess(hProcess,
1341 ProcessQuotaLimits,
1342 &QuotaLimits,
1343 sizeof(QUOTA_LIMITS));
1344 if (!NT_SUCCESS(Status))
1345 {
1346 BaseSetLastNTError(Status);
1347 return FALSE;
1348 }
1349
1350 return TRUE;
1351 }
1352
1353
1354 /*
1355 * @implemented
1356 */
1357 BOOL
1358 WINAPI
1359 GetProcessTimes(HANDLE hProcess,
1360 LPFILETIME lpCreationTime,
1361 LPFILETIME lpExitTime,
1362 LPFILETIME lpKernelTime,
1363 LPFILETIME lpUserTime)
1364 {
1365 KERNEL_USER_TIMES Kut;
1366 NTSTATUS Status;
1367
1368 Status = NtQueryInformationProcess(hProcess,
1369 ProcessTimes,
1370 &Kut,
1371 sizeof(Kut),
1372 NULL);
1373 if (!NT_SUCCESS(Status))
1374 {
1375 BaseSetLastNTError(Status);
1376 return FALSE;
1377 }
1378
1379 lpCreationTime->dwLowDateTime = Kut.CreateTime.u.LowPart;
1380 lpCreationTime->dwHighDateTime = Kut.CreateTime.u.HighPart;
1381
1382 lpExitTime->dwLowDateTime = Kut.ExitTime.u.LowPart;
1383 lpExitTime->dwHighDateTime = Kut.ExitTime.u.HighPart;
1384
1385 lpKernelTime->dwLowDateTime = Kut.KernelTime.u.LowPart;
1386 lpKernelTime->dwHighDateTime = Kut.KernelTime.u.HighPart;
1387
1388 lpUserTime->dwLowDateTime = Kut.UserTime.u.LowPart;
1389 lpUserTime->dwHighDateTime = Kut.UserTime.u.HighPart;
1390
1391 return TRUE;
1392 }
1393
1394
1395 /*
1396 * @implemented
1397 */
1398 HANDLE
1399 WINAPI
1400 GetCurrentProcess(VOID)
1401 {
1402 return (HANDLE)NtCurrentProcess();
1403 }
1404
1405
1406 /*
1407 * @implemented
1408 */
1409 HANDLE
1410 WINAPI
1411 GetCurrentThread(VOID)
1412 {
1413 return (HANDLE)NtCurrentThread();
1414 }
1415
1416
1417 /*
1418 * @implemented
1419 */
1420 DWORD
1421 WINAPI
1422 GetCurrentProcessId(VOID)
1423 {
1424 return HandleToUlong(GetTeb()->ClientId.UniqueProcess);
1425 }
1426
1427
1428 /*
1429 * @implemented
1430 */
1431 BOOL
1432 WINAPI
1433 GetExitCodeProcess(HANDLE hProcess,
1434 LPDWORD lpExitCode)
1435 {
1436 PROCESS_BASIC_INFORMATION ProcessBasic;
1437 NTSTATUS Status;
1438
1439 Status = NtQueryInformationProcess(hProcess,
1440 ProcessBasicInformation,
1441 &ProcessBasic,
1442 sizeof(PROCESS_BASIC_INFORMATION),
1443 NULL);
1444 if (!NT_SUCCESS(Status))
1445 {
1446 BaseSetLastNTError(Status);
1447 return FALSE;
1448 }
1449
1450 *lpExitCode = (DWORD)ProcessBasic.ExitStatus;
1451
1452 return TRUE;
1453 }
1454
1455
1456 /*
1457 * @implemented
1458 */
1459 DWORD
1460 WINAPI
1461 GetProcessId(HANDLE Process)
1462 {
1463 PROCESS_BASIC_INFORMATION ProcessBasic;
1464 NTSTATUS Status;
1465
1466 Status = NtQueryInformationProcess(Process,
1467 ProcessBasicInformation,
1468 &ProcessBasic,
1469 sizeof(PROCESS_BASIC_INFORMATION),
1470 NULL);
1471 if (!NT_SUCCESS(Status))
1472 {
1473 BaseSetLastNTError(Status);
1474 return 0;
1475 }
1476
1477 return (DWORD)ProcessBasic.UniqueProcessId;
1478 }
1479
1480
1481 /*
1482 * @implemented
1483 */
1484 HANDLE
1485 WINAPI
1486 OpenProcess(DWORD dwDesiredAccess,
1487 BOOL bInheritHandle,
1488 DWORD dwProcessId)
1489 {
1490 NTSTATUS errCode;
1491 HANDLE ProcessHandle;
1492 OBJECT_ATTRIBUTES ObjectAttributes;
1493 CLIENT_ID ClientId;
1494
1495 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
1496 ClientId.UniqueThread = 0;
1497
1498 InitializeObjectAttributes(&ObjectAttributes,
1499 NULL,
1500 (bInheritHandle ? OBJ_INHERIT : 0),
1501 NULL,
1502 NULL);
1503
1504 errCode = NtOpenProcess(&ProcessHandle,
1505 dwDesiredAccess,
1506 &ObjectAttributes,
1507 &ClientId);
1508 if (!NT_SUCCESS(errCode))
1509 {
1510 BaseSetLastNTError(errCode);
1511 return NULL;
1512 }
1513
1514 return ProcessHandle;
1515 }
1516
1517
1518 /*
1519 * @implemented
1520 */
1521 UINT
1522 WINAPI
1523 WinExec(LPCSTR lpCmdLine,
1524 UINT uCmdShow)
1525 {
1526 STARTUPINFOA StartupInfo;
1527 PROCESS_INFORMATION ProcessInformation;
1528 DWORD dosErr;
1529
1530 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
1531 StartupInfo.cb = sizeof(STARTUPINFOA);
1532 StartupInfo.wShowWindow = (WORD)uCmdShow;
1533 StartupInfo.dwFlags = 0;
1534
1535 if (!CreateProcessA(NULL,
1536 (PVOID)lpCmdLine,
1537 NULL,
1538 NULL,
1539 FALSE,
1540 0,
1541 NULL,
1542 NULL,
1543 &StartupInfo,
1544 &ProcessInformation))
1545 {
1546 dosErr = GetLastError();
1547 return dosErr < 32 ? dosErr : ERROR_BAD_FORMAT;
1548 }
1549
1550 if (NULL != lpfnGlobalRegisterWaitForInputIdle)
1551 {
1552 lpfnGlobalRegisterWaitForInputIdle(ProcessInformation.hProcess,
1553 10000);
1554 }
1555
1556 NtClose(ProcessInformation.hProcess);
1557 NtClose(ProcessInformation.hThread);
1558
1559 return 33; /* Something bigger than 31 means success. */
1560 }
1561
1562
1563 /*
1564 * @implemented
1565 */
1566 VOID
1567 WINAPI
1568 RegisterWaitForInputIdle(WaitForInputIdleType lpfnRegisterWaitForInputIdle)
1569 {
1570 lpfnGlobalRegisterWaitForInputIdle = lpfnRegisterWaitForInputIdle;
1571 return;
1572 }
1573
1574 /*
1575 * @implemented
1576 */
1577 VOID
1578 WINAPI
1579 GetStartupInfoW(LPSTARTUPINFOW lpStartupInfo)
1580 {
1581 PRTL_USER_PROCESS_PARAMETERS Params;
1582
1583 if (lpStartupInfo == NULL)
1584 {
1585 SetLastError(ERROR_INVALID_PARAMETER);
1586 return;
1587 }
1588
1589 Params = NtCurrentPeb()->ProcessParameters;
1590
1591 lpStartupInfo->cb = sizeof(STARTUPINFOW);
1592 lpStartupInfo->lpDesktop = Params->DesktopInfo.Buffer;
1593 lpStartupInfo->lpTitle = Params->WindowTitle.Buffer;
1594 lpStartupInfo->dwX = Params->StartingX;
1595 lpStartupInfo->dwY = Params->StartingY;
1596 lpStartupInfo->dwXSize = Params->CountX;
1597 lpStartupInfo->dwYSize = Params->CountY;
1598 lpStartupInfo->dwXCountChars = Params->CountCharsX;
1599 lpStartupInfo->dwYCountChars = Params->CountCharsY;
1600 lpStartupInfo->dwFillAttribute = Params->FillAttribute;
1601 lpStartupInfo->dwFlags = Params->WindowFlags;
1602 lpStartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags;
1603 lpStartupInfo->cbReserved2 = Params->RuntimeData.Length;
1604 lpStartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer;
1605
1606 lpStartupInfo->hStdInput = Params->StandardInput;
1607 lpStartupInfo->hStdOutput = Params->StandardOutput;
1608 lpStartupInfo->hStdError = Params->StandardError;
1609 }
1610
1611
1612 /*
1613 * @implemented
1614 */
1615 VOID
1616 WINAPI
1617 GetStartupInfoA(LPSTARTUPINFOA lpStartupInfo)
1618 {
1619 PRTL_USER_PROCESS_PARAMETERS Params;
1620 ANSI_STRING AnsiString;
1621
1622 if (lpStartupInfo == NULL)
1623 {
1624 SetLastError(ERROR_INVALID_PARAMETER);
1625 return;
1626 }
1627
1628 Params = NtCurrentPeb ()->ProcessParameters;
1629
1630 RtlAcquirePebLock ();
1631
1632 /* FIXME - not thread-safe */
1633 if (lpLocalStartupInfo == NULL)
1634 {
1635 /* create new local startup info (ansi) */
1636 lpLocalStartupInfo = RtlAllocateHeap(RtlGetProcessHeap(),
1637 0,
1638 sizeof(STARTUPINFOA));
1639 if (lpLocalStartupInfo == NULL)
1640 {
1641 RtlReleasePebLock();
1642 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1643 return;
1644 }
1645
1646 lpLocalStartupInfo->cb = sizeof(STARTUPINFOA);
1647
1648 /* copy window title string */
1649 RtlUnicodeStringToAnsiString(&AnsiString,
1650 &Params->WindowTitle,
1651 TRUE);
1652 lpLocalStartupInfo->lpTitle = AnsiString.Buffer;
1653
1654 /* copy desktop info string */
1655 RtlUnicodeStringToAnsiString(&AnsiString,
1656 &Params->DesktopInfo,
1657 TRUE);
1658 lpLocalStartupInfo->lpDesktop = AnsiString.Buffer;
1659
1660 /* copy shell info string */
1661 RtlUnicodeStringToAnsiString(&AnsiString,
1662 &Params->ShellInfo,
1663 TRUE);
1664 lpLocalStartupInfo->lpReserved = AnsiString.Buffer;
1665
1666 lpLocalStartupInfo->dwX = Params->StartingX;
1667 lpLocalStartupInfo->dwY = Params->StartingY;
1668 lpLocalStartupInfo->dwXSize = Params->CountX;
1669 lpLocalStartupInfo->dwYSize = Params->CountY;
1670 lpLocalStartupInfo->dwXCountChars = Params->CountCharsX;
1671 lpLocalStartupInfo->dwYCountChars = Params->CountCharsY;
1672 lpLocalStartupInfo->dwFillAttribute = Params->FillAttribute;
1673 lpLocalStartupInfo->dwFlags = Params->WindowFlags;
1674 lpLocalStartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags;
1675 lpLocalStartupInfo->cbReserved2 = Params->RuntimeData.Length;
1676 lpLocalStartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer;
1677
1678 lpLocalStartupInfo->hStdInput = Params->StandardInput;
1679 lpLocalStartupInfo->hStdOutput = Params->StandardOutput;
1680 lpLocalStartupInfo->hStdError = Params->StandardError;
1681 }
1682
1683 RtlReleasePebLock();
1684
1685 /* copy local startup info data to external startup info */
1686 memcpy(lpStartupInfo,
1687 lpLocalStartupInfo,
1688 sizeof(STARTUPINFOA));
1689 }
1690
1691
1692 /*
1693 * @implemented
1694 */
1695 BOOL
1696 WINAPI
1697 FlushInstructionCache(HANDLE hProcess,
1698 LPCVOID lpBaseAddress,
1699 SIZE_T dwSize)
1700 {
1701 NTSTATUS Status;
1702
1703 Status = NtFlushInstructionCache(hProcess,
1704 (PVOID)lpBaseAddress,
1705 dwSize);
1706 if (!NT_SUCCESS(Status))
1707 {
1708 BaseSetLastNTError(Status);
1709 return FALSE;
1710 }
1711
1712 return TRUE;
1713 }
1714
1715
1716 /*
1717 * @implemented
1718 */
1719 VOID
1720 WINAPI
1721 ExitProcess(UINT uExitCode)
1722 {
1723 CSR_API_MESSAGE CsrRequest;
1724 ULONG Request;
1725 NTSTATUS Status;
1726
1727 /* kill sibling threads ... we want to be alone at this point */
1728 NtTerminateProcess(NULL, 0);
1729
1730 /* unload all dll's */
1731 LdrShutdownProcess();
1732
1733 /* notify csrss of process termination */
1734 Request = TERMINATE_PROCESS;
1735 Status = CsrClientCallServer(&CsrRequest,
1736 NULL,
1737 MAKE_CSR_API(Request, CSR_NATIVE),
1738 sizeof(CSR_API_MESSAGE));
1739 if (!NT_SUCCESS(Status) || !NT_SUCCESS(CsrRequest.Status))
1740 {
1741 DPRINT("Failed to tell csrss about terminating process\n");
1742 }
1743
1744 NtTerminateProcess(NtCurrentProcess (),
1745 uExitCode);
1746
1747 /* should never get here */
1748 ASSERT(0);
1749 while(1);
1750 }
1751
1752
1753 /*
1754 * @implemented
1755 */
1756 BOOL
1757 WINAPI
1758 TerminateProcess(HANDLE hProcess,
1759 UINT uExitCode)
1760 {
1761 NTSTATUS Status;
1762
1763 if (hProcess == NULL)
1764 {
1765 return FALSE;
1766 }
1767
1768 Status = NtTerminateProcess(hProcess, uExitCode);
1769 if (NT_SUCCESS(Status))
1770 {
1771 return TRUE;
1772 }
1773
1774 BaseSetLastNTError(Status);
1775 return FALSE;
1776 }
1777
1778
1779 /*
1780 * @unimplemented
1781 */
1782 VOID
1783 WINAPI
1784 FatalAppExitA(UINT uAction,
1785 LPCSTR lpMessageText)
1786 {
1787 UNICODE_STRING MessageTextU;
1788 ANSI_STRING MessageText;
1789
1790 RtlInitAnsiString(&MessageText, (LPSTR)lpMessageText);
1791
1792 RtlAnsiStringToUnicodeString(&MessageTextU,
1793 &MessageText,
1794 TRUE);
1795
1796 FatalAppExitW(uAction, MessageTextU.Buffer);
1797
1798 RtlFreeUnicodeString(&MessageTextU);
1799 }
1800
1801
1802 /*
1803 * @unimplemented
1804 */
1805 VOID
1806 WINAPI
1807 FatalAppExitW(UINT uAction,
1808 LPCWSTR lpMessageText)
1809 {
1810 static const WCHAR szUser32[] = L"user32.dll\0";
1811
1812 HMODULE hModule = GetModuleHandleW(szUser32);
1813 MessageBoxW_Proc pMessageBoxW = NULL;
1814
1815 DPRINT1("AppExit\n");
1816
1817 if (hModule)
1818 pMessageBoxW = (MessageBoxW_Proc)GetProcAddress(hModule, "MessageBoxW");
1819
1820 if (pMessageBoxW)
1821 pMessageBoxW(0, lpMessageText, NULL, MB_SYSTEMMODAL | MB_OK);
1822 else
1823 DPRINT1("%s\n", lpMessageText);
1824
1825 ExitProcess(0);
1826 }
1827
1828
1829 /*
1830 * @implemented
1831 */
1832 VOID
1833 WINAPI
1834 FatalExit(int ExitCode)
1835 {
1836 ExitProcess(ExitCode);
1837 }
1838
1839
1840 /*
1841 * @implemented
1842 */
1843 DWORD
1844 WINAPI
1845 GetPriorityClass(HANDLE hProcess)
1846 {
1847 NTSTATUS Status;
1848 PROCESS_PRIORITY_CLASS PriorityClass;
1849
1850 Status = NtQueryInformationProcess(hProcess,
1851 ProcessPriorityClass,
1852 &PriorityClass,
1853 sizeof(PROCESS_PRIORITY_CLASS),
1854 NULL);
1855 if(NT_SUCCESS(Status))
1856 {
1857 switch(PriorityClass.PriorityClass)
1858 {
1859 case PROCESS_PRIORITY_CLASS_IDLE:
1860 return IDLE_PRIORITY_CLASS;
1861
1862 case PROCESS_PRIORITY_CLASS_BELOW_NORMAL:
1863 return BELOW_NORMAL_PRIORITY_CLASS;
1864
1865 case PROCESS_PRIORITY_CLASS_NORMAL:
1866 return NORMAL_PRIORITY_CLASS;
1867
1868 case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL:
1869 return ABOVE_NORMAL_PRIORITY_CLASS;
1870
1871 case PROCESS_PRIORITY_CLASS_HIGH:
1872 return HIGH_PRIORITY_CLASS;
1873
1874 case PROCESS_PRIORITY_CLASS_REALTIME:
1875 return REALTIME_PRIORITY_CLASS;
1876
1877 default:
1878 return NORMAL_PRIORITY_CLASS;
1879 }
1880 }
1881
1882 BaseSetLastNTError(Status);
1883 return FALSE;
1884 }
1885
1886
1887 /*
1888 * @implemented
1889 */
1890 BOOL
1891 WINAPI
1892 SetPriorityClass(HANDLE hProcess,
1893 DWORD dwPriorityClass)
1894 {
1895 NTSTATUS Status;
1896 PROCESS_PRIORITY_CLASS PriorityClass;
1897
1898 switch (dwPriorityClass)
1899 {
1900 case IDLE_PRIORITY_CLASS:
1901 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
1902 break;
1903
1904 case BELOW_NORMAL_PRIORITY_CLASS:
1905 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
1906 break;
1907
1908 case NORMAL_PRIORITY_CLASS:
1909 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
1910 break;
1911
1912 case ABOVE_NORMAL_PRIORITY_CLASS:
1913 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
1914 break;
1915
1916 case HIGH_PRIORITY_CLASS:
1917 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
1918 break;
1919
1920 case REALTIME_PRIORITY_CLASS:
1921 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_REALTIME;
1922 break;
1923
1924 default:
1925 SetLastError(ERROR_INVALID_PARAMETER);
1926 return FALSE;
1927 }
1928
1929 PriorityClass.Foreground = FALSE;
1930
1931 Status = NtSetInformationProcess(hProcess,
1932 ProcessPriorityClass,
1933 &PriorityClass,
1934 sizeof(PROCESS_PRIORITY_CLASS));
1935 if (!NT_SUCCESS(Status))
1936 {
1937 BaseSetLastNTError(Status);
1938 return FALSE;
1939 }
1940
1941 return TRUE;
1942 }
1943
1944
1945 /*
1946 * @implemented
1947 */
1948 DWORD
1949 WINAPI
1950 GetProcessVersion(DWORD ProcessId)
1951 {
1952 DWORD Version = 0;
1953 PIMAGE_NT_HEADERS NtHeader = NULL;
1954 IMAGE_NT_HEADERS NtHeaders;
1955 IMAGE_DOS_HEADER DosHeader;
1956 PROCESS_BASIC_INFORMATION ProcessBasicInfo;
1957 PVOID BaseAddress = NULL;
1958 HANDLE ProcessHandle = NULL;
1959 NTSTATUS Status;
1960 SIZE_T Count;
1961 PEB Peb;
1962
1963 _SEH2_TRY
1964 {
1965 if (0 == ProcessId || GetCurrentProcessId() == ProcessId)
1966 {
1967 /* Caller's */
1968 BaseAddress = (PVOID) NtCurrentPeb()->ImageBaseAddress;
1969 NtHeader = RtlImageNtHeader(BaseAddress);
1970
1971 Version = (NtHeader->OptionalHeader.MajorOperatingSystemVersion << 16) |
1972 (NtHeader->OptionalHeader.MinorOperatingSystemVersion);
1973 }
1974 else
1975 {
1976 /* Other process */
1977 ProcessHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
1978 FALSE,
1979 ProcessId);
1980
1981 if (!ProcessHandle) return 0;
1982
1983 Status = NtQueryInformationProcess(ProcessHandle,
1984 ProcessBasicInformation,
1985 &ProcessBasicInfo,
1986 sizeof(ProcessBasicInfo),
1987 NULL);
1988
1989 if (!NT_SUCCESS(Status)) goto Error;
1990
1991 Status = NtReadVirtualMemory(ProcessHandle,
1992 ProcessBasicInfo.PebBaseAddress,
1993 &Peb,
1994 sizeof(Peb),
1995 &Count);
1996
1997 if (!NT_SUCCESS(Status) || Count != sizeof(Peb)) goto Error;
1998
1999 memset(&DosHeader, 0, sizeof(DosHeader));
2000 Status = NtReadVirtualMemory(ProcessHandle,
2001 Peb.ImageBaseAddress,
2002 &DosHeader,
2003 sizeof(DosHeader),
2004 &Count);
2005
2006 if (!NT_SUCCESS(Status) || Count != sizeof(DosHeader)) goto Error;
2007 if (DosHeader.e_magic != IMAGE_DOS_SIGNATURE) goto Error;
2008
2009 memset(&NtHeaders, 0, sizeof(NtHeaders));
2010 Status = NtReadVirtualMemory(ProcessHandle,
2011 (char *)Peb.ImageBaseAddress + DosHeader.e_lfanew,
2012 &NtHeaders,
2013 sizeof(NtHeaders),
2014 &Count);
2015
2016 if (!NT_SUCCESS(Status) || Count != sizeof(NtHeaders)) goto Error;
2017 if (NtHeaders.Signature != IMAGE_NT_SIGNATURE) goto Error;
2018
2019 Version = MAKELONG(NtHeaders.OptionalHeader.MinorSubsystemVersion,
2020 NtHeaders.OptionalHeader.MajorSubsystemVersion);
2021
2022 Error:
2023 if (!NT_SUCCESS(Status))
2024 {
2025 BaseSetLastNTError(Status);
2026 }
2027 }
2028 }
2029 _SEH2_FINALLY
2030 {
2031 if (ProcessHandle) CloseHandle(ProcessHandle);
2032 }
2033 _SEH2_END;
2034
2035 return Version;
2036 }
2037
2038
2039 /*
2040 * @implemented
2041 */
2042 BOOL
2043 WINAPI
2044 GetProcessIoCounters(HANDLE hProcess,
2045 PIO_COUNTERS lpIoCounters)
2046 {
2047 NTSTATUS Status;
2048
2049 Status = NtQueryInformationProcess(hProcess,
2050 ProcessIoCounters,
2051 lpIoCounters,
2052 sizeof(IO_COUNTERS),
2053 NULL);
2054 if (!NT_SUCCESS(Status))
2055 {
2056 BaseSetLastNTError(Status);
2057 return FALSE;
2058 }
2059
2060 return TRUE;
2061 }
2062
2063
2064 /*
2065 * @implemented
2066 */
2067 BOOL
2068 WINAPI
2069 GetProcessPriorityBoost(HANDLE hProcess,
2070 PBOOL pDisablePriorityBoost)
2071 {
2072 NTSTATUS Status;
2073 ULONG PriorityBoost;
2074
2075 Status = NtQueryInformationProcess(hProcess,
2076 ProcessPriorityBoost,
2077 &PriorityBoost,
2078 sizeof(ULONG),
2079 NULL);
2080 if (NT_SUCCESS(Status))
2081 {
2082 *pDisablePriorityBoost = PriorityBoost;
2083 return TRUE;
2084 }
2085
2086 BaseSetLastNTError(Status);
2087 return FALSE;
2088 }
2089
2090
2091 /*
2092 * @implemented
2093 */
2094 BOOL
2095 WINAPI
2096 SetProcessPriorityBoost(HANDLE hProcess,
2097 BOOL bDisablePriorityBoost)
2098 {
2099 NTSTATUS Status;
2100 ULONG PriorityBoost = (bDisablePriorityBoost ? TRUE : FALSE); /* prevent setting values other than 1 and 0 */
2101
2102 Status = NtSetInformationProcess(hProcess,
2103 ProcessPriorityBoost,
2104 &PriorityBoost,
2105 sizeof(ULONG));
2106 if (!NT_SUCCESS(Status))
2107 {
2108 BaseSetLastNTError(Status);
2109 return FALSE;
2110 }
2111
2112 return TRUE;
2113 }
2114
2115
2116 /*
2117 * @implemented
2118 */
2119 BOOL
2120 WINAPI
2121 GetProcessHandleCount(HANDLE hProcess,
2122 PDWORD pdwHandleCount)
2123 {
2124 ULONG phc;
2125 NTSTATUS Status;
2126
2127 Status = NtQueryInformationProcess(hProcess,
2128 ProcessHandleCount,
2129 &phc,
2130 sizeof(ULONG),
2131 NULL);
2132 if(NT_SUCCESS(Status))
2133 {
2134 *pdwHandleCount = phc;
2135 return TRUE;
2136 }
2137
2138 BaseSetLastNTError(Status);
2139 return FALSE;
2140 }
2141
2142
2143 /*
2144 * @implemented
2145 */
2146 BOOL
2147 WINAPI
2148 IsWow64Process(HANDLE hProcess,
2149 PBOOL Wow64Process)
2150 {
2151 ULONG_PTR pbi;
2152 NTSTATUS Status;
2153
2154 Status = NtQueryInformationProcess(hProcess,
2155 ProcessWow64Information,
2156 &pbi,
2157 sizeof(pbi),
2158 NULL);
2159 if (!NT_SUCCESS(Status))
2160 {
2161 SetLastError(RtlNtStatusToDosError(Status));
2162 return FALSE;
2163 }
2164
2165 *Wow64Process = (pbi != 0);
2166
2167 return TRUE;
2168 }
2169
2170 /*
2171 * @implemented
2172 */
2173 LPSTR
2174 WINAPI
2175 GetCommandLineA(VOID)
2176 {
2177 DPRINT("CommandLine \'%s\'\n", CommandLineStringA.Buffer);
2178 return CommandLineStringA.Buffer;
2179 }
2180
2181
2182 /*
2183 * @implemented
2184 */
2185 LPWSTR
2186 WINAPI
2187 GetCommandLineW(VOID)
2188 {
2189 DPRINT("CommandLine \'%S\'\n", CommandLineStringW.Buffer);
2190 return CommandLineStringW.Buffer;
2191 }
2192
2193 /*
2194 * @implemented
2195 */
2196 BOOL
2197 NTAPI
2198 ReadProcessMemory(IN HANDLE hProcess,
2199 IN LPCVOID lpBaseAddress,
2200 IN LPVOID lpBuffer,
2201 IN SIZE_T nSize,
2202 OUT SIZE_T* lpNumberOfBytesRead)
2203 {
2204 NTSTATUS Status;
2205
2206 /* Do the read */
2207 Status = NtReadVirtualMemory(hProcess,
2208 (PVOID)lpBaseAddress,
2209 lpBuffer,
2210 nSize,
2211 lpNumberOfBytesRead);
2212 if (!NT_SUCCESS(Status))
2213 {
2214 /* We failed */
2215 BaseSetLastNTError (Status);
2216 return FALSE;
2217 }
2218
2219 /* Return success */
2220 return TRUE;
2221 }
2222
2223 /*
2224 * @implemented
2225 */
2226 BOOL
2227 NTAPI
2228 WriteProcessMemory(IN HANDLE hProcess,
2229 IN LPVOID lpBaseAddress,
2230 IN LPCVOID lpBuffer,
2231 IN SIZE_T nSize,
2232 OUT SIZE_T *lpNumberOfBytesWritten)
2233 {
2234 NTSTATUS Status;
2235 ULONG OldValue;
2236 SIZE_T RegionSize;
2237 PVOID Base;
2238 BOOLEAN UnProtect;
2239
2240 /* Set parameters for protect call */
2241 RegionSize = nSize;
2242 Base = lpBaseAddress;
2243
2244 /* Check the current status */
2245 Status = NtProtectVirtualMemory(hProcess,
2246 &Base,
2247 &RegionSize,
2248 PAGE_EXECUTE_READWRITE,
2249 &OldValue);
2250 if (NT_SUCCESS(Status))
2251 {
2252 /* Check if we are unprotecting */
2253 UnProtect = OldValue & (PAGE_READWRITE |
2254 PAGE_WRITECOPY |
2255 PAGE_EXECUTE_READWRITE |
2256 PAGE_EXECUTE_WRITECOPY) ? FALSE : TRUE;
2257 if (!UnProtect)
2258 {
2259 /* Set the new protection */
2260 Status = NtProtectVirtualMemory(hProcess,
2261 &Base,
2262 &RegionSize,
2263 OldValue,
2264 &OldValue);
2265
2266 /* Write the memory */
2267 Status = NtWriteVirtualMemory(hProcess,
2268 lpBaseAddress,
2269 (LPVOID)lpBuffer,
2270 nSize,
2271 lpNumberOfBytesWritten);
2272 if (!NT_SUCCESS(Status))
2273 {
2274 /* We failed */
2275 BaseSetLastNTError(Status);
2276 return FALSE;
2277 }
2278
2279 /* Flush the ITLB */
2280 NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
2281 return TRUE;
2282 }
2283 else
2284 {
2285 /* Check if we were read only */
2286 if ((OldValue & PAGE_NOACCESS) || (OldValue & PAGE_READONLY))
2287 {
2288 /* Restore protection and fail */
2289 NtProtectVirtualMemory(hProcess,
2290 &Base,
2291 &RegionSize,
2292 OldValue,
2293 &OldValue);
2294 BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
2295 return FALSE;
2296 }
2297
2298 /* Otherwise, do the write */
2299 Status = NtWriteVirtualMemory(hProcess,
2300 lpBaseAddress,
2301 (LPVOID)lpBuffer,
2302 nSize,
2303 lpNumberOfBytesWritten);
2304
2305 /* And restore the protection */
2306 NtProtectVirtualMemory(hProcess,
2307 &Base,
2308 &RegionSize,
2309 OldValue,
2310 &OldValue);
2311 if (!NT_SUCCESS(Status))
2312 {
2313 /* We failed */
2314 BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
2315 return FALSE;
2316 }
2317
2318 /* Flush the ITLB */
2319 NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
2320 return TRUE;
2321 }
2322 }
2323 else
2324 {
2325 /* We failed */
2326 BaseSetLastNTError(Status);
2327 return FALSE;
2328 }
2329 }
2330
2331 /*
2332 * @implemented
2333 */
2334 BOOL
2335 WINAPI
2336 ProcessIdToSessionId(IN DWORD dwProcessId,
2337 OUT DWORD *pSessionId)
2338 {
2339 PROCESS_SESSION_INFORMATION SessionInformation;
2340 OBJECT_ATTRIBUTES ObjectAttributes;
2341 CLIENT_ID ClientId;
2342 HANDLE ProcessHandle;
2343 NTSTATUS Status;
2344
2345 if (IsBadWritePtr(pSessionId, sizeof(DWORD)))
2346 {
2347 SetLastError(ERROR_INVALID_PARAMETER);
2348 return FALSE;
2349 }
2350
2351 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
2352 ClientId.UniqueThread = 0;
2353
2354 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
2355
2356 Status = NtOpenProcess(&ProcessHandle,
2357 PROCESS_QUERY_INFORMATION,
2358 &ObjectAttributes,
2359 &ClientId);
2360 if (NT_SUCCESS(Status))
2361 {
2362 Status = NtQueryInformationProcess(ProcessHandle,
2363 ProcessSessionInformation,
2364 &SessionInformation,
2365 sizeof(SessionInformation),
2366 NULL);
2367 NtClose(ProcessHandle);
2368
2369 if (NT_SUCCESS(Status))
2370 {
2371 *pSessionId = SessionInformation.SessionId;
2372 return TRUE;
2373 }
2374 }
2375
2376 BaseSetLastNTError(Status);
2377 return FALSE;
2378 }
2379
2380 BOOL
2381 WINAPI
2382 SetProcessWorkingSetSizeEx(IN HANDLE hProcess,
2383 IN SIZE_T dwMinimumWorkingSetSize,
2384 IN SIZE_T dwMaximumWorkingSetSize,
2385 IN DWORD Flags)
2386 {
2387 STUB;
2388 return FALSE;
2389 }
2390
2391
2392 BOOL
2393 WINAPI
2394 GetProcessWorkingSetSizeEx(IN HANDLE hProcess,
2395 OUT PSIZE_T lpMinimumWorkingSetSize,
2396 OUT PSIZE_T lpMaximumWorkingSetSize,
2397 OUT PDWORD Flags)
2398 {
2399 STUB;
2400 return FALSE;
2401 }
2402
2403 /*
2404 * @implemented
2405 */
2406 BOOL
2407 WINAPI
2408 CreateProcessInternalW(HANDLE hToken,
2409 LPCWSTR lpApplicationName,
2410 LPWSTR lpCommandLine,
2411 LPSECURITY_ATTRIBUTES lpProcessAttributes,
2412 LPSECURITY_ATTRIBUTES lpThreadAttributes,
2413 BOOL bInheritHandles,
2414 DWORD dwCreationFlags,
2415 LPVOID lpEnvironment,
2416 LPCWSTR lpCurrentDirectory,
2417 LPSTARTUPINFOW lpStartupInfo,
2418 LPPROCESS_INFORMATION lpProcessInformation,
2419 PHANDLE hNewToken)
2420 {
2421 NTSTATUS Status;
2422 PROCESS_PRIORITY_CLASS PriorityClass;
2423 BOOLEAN FoundQuotes = FALSE;
2424 BOOLEAN QuotesNeeded = FALSE;
2425 BOOLEAN CmdLineIsAppName = FALSE;
2426 UNICODE_STRING ApplicationName = { 0, 0, NULL };
2427 OBJECT_ATTRIBUTES LocalObjectAttributes;
2428 POBJECT_ATTRIBUTES ObjectAttributes;
2429 HANDLE hSection = NULL, hProcess = NULL, hThread = NULL, hDebug = NULL;
2430 SECTION_IMAGE_INFORMATION SectionImageInfo;
2431 LPWSTR CurrentDirectory = NULL;
2432 LPWSTR CurrentDirectoryPart;
2433 PROCESS_BASIC_INFORMATION ProcessBasicInfo;
2434 STARTUPINFOW StartupInfo;
2435 ULONG Dummy;
2436 LPWSTR BatchCommandLine;
2437 ULONG CmdLineLength;
2438 UNICODE_STRING CommandLineString;
2439 PWCHAR Extension;
2440 LPWSTR QuotedCmdLine = NULL;
2441 LPWSTR ScanString;
2442 LPWSTR NullBuffer = NULL;
2443 LPWSTR NameBuffer = NULL;
2444 WCHAR SaveChar = 0;
2445 ULONG RetVal;
2446 UINT Error = 0;
2447 BOOLEAN SearchDone = FALSE;
2448 BOOLEAN Escape = FALSE;
2449 CLIENT_ID ClientId;
2450 PPEB OurPeb = NtCurrentPeb();
2451 PPEB RemotePeb;
2452 SIZE_T EnvSize = 0;
2453 BOOL Ret = FALSE;
2454
2455 /* FIXME should process
2456 * HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
2457 * key (see http://blogs.msdn.com/oldnewthing/archive/2005/12/19/505449.aspx)
2458 */
2459
2460 DPRINT("CreateProcessW: lpApplicationName: %S lpCommandLine: %S"
2461 " lpEnvironment: %p lpCurrentDirectory: %S dwCreationFlags: %lx\n",
2462 lpApplicationName, lpCommandLine, lpEnvironment, lpCurrentDirectory,
2463 dwCreationFlags);
2464
2465 /* Flags we don't handle yet */
2466 if (dwCreationFlags & CREATE_SEPARATE_WOW_VDM)
2467 {
2468 DPRINT1("CREATE_SEPARATE_WOW_VDM not handled\n");
2469 }
2470 if (dwCreationFlags & CREATE_SHARED_WOW_VDM)
2471 {
2472 DPRINT1("CREATE_SHARED_WOW_VDM not handled\n");
2473 }
2474 if (dwCreationFlags & CREATE_FORCEDOS)
2475 {
2476 DPRINT1("CREATE_FORCEDOS not handled\n");
2477 }
2478
2479 /* Fail on this flag, it's only valid with the WithLogonW function */
2480 if (dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL)
2481 {
2482 DPRINT1("Invalid flag used\n");
2483 SetLastError(ERROR_INVALID_PARAMETER);
2484 return FALSE;
2485 }
2486
2487 /* This combination is illegal (see MSDN) */
2488 if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
2489 (DETACHED_PROCESS | CREATE_NEW_CONSOLE))
2490 {
2491 DPRINT1("Invalid flag combo used\n");
2492 SetLastError(ERROR_INVALID_PARAMETER);
2493 return FALSE;
2494 }
2495
2496 /* Another illegal combo */
2497 if ((dwCreationFlags & (CREATE_SEPARATE_WOW_VDM | CREATE_SHARED_WOW_VDM)) ==
2498 (CREATE_SEPARATE_WOW_VDM | CREATE_SHARED_WOW_VDM))
2499 {
2500 DPRINT1("Invalid flag combo used\n");
2501 SetLastError(ERROR_INVALID_PARAMETER);
2502 return FALSE;
2503 }
2504
2505 if (lpCurrentDirectory)
2506 {
2507 if ((GetFileAttributesW(lpCurrentDirectory) == INVALID_FILE_ATTRIBUTES) ||
2508 !(GetFileAttributesW(lpCurrentDirectory) & FILE_ATTRIBUTE_DIRECTORY))
2509 {
2510 SetLastError(ERROR_DIRECTORY);
2511 return FALSE;
2512 }
2513 }
2514
2515 /*
2516 * We're going to modify and mask out flags and stuff in lpStartupInfo,
2517 * so we'll use our own local copy for that.
2518 */
2519 StartupInfo = *lpStartupInfo;
2520
2521 /* FIXME: Use default Separate/Shared VDM Flag */
2522
2523 /* If we are inside a Job, use Separate VDM so it won't escape the Job */
2524 if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM))
2525 {
2526 if (NtIsProcessInJob(NtCurrentProcess(), NULL))
2527 {
2528 /* Remove the shared flag and add the separate flag. */
2529 dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) |
2530 CREATE_SEPARATE_WOW_VDM;
2531 }
2532 }
2533
2534 /*
2535 * According to some sites, ShellExecuteEx uses an undocumented flag to
2536 * send private handle data (such as HMONITOR or HICON). See:
2537 * www.catch22.net/tuts/undoc01.asp. This implies that we can't use the
2538 * standard handles anymore since we'd be overwriting this private data
2539 */
2540 if ((StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
2541 (StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
2542 {
2543 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
2544 }
2545
2546 /* Start by zeroing out the fields */
2547 RtlZeroMemory(lpProcessInformation, sizeof(PROCESS_INFORMATION));
2548
2549 /* Easy stuff first, convert the process priority class */
2550 PriorityClass.Foreground = FALSE;
2551 PriorityClass.PriorityClass = (UCHAR)BasepConvertPriorityClass(dwCreationFlags);
2552
2553 if (lpCommandLine)
2554 {
2555 /* Search for escape sequences */
2556 ScanString = lpCommandLine;
2557 while (NULL != (ScanString = wcschr(ScanString, L'^')))
2558 {
2559 ScanString++;
2560 if (*ScanString == L'\"' || *ScanString == L'^' || *ScanString == L'\"')
2561 {
2562 Escape = TRUE;
2563 break;
2564 }
2565 }
2566 }
2567
2568 /* Get the application name and do all the proper formating necessary */
2569 GetAppName:
2570 /* See if we have an application name (oh please let us have one!) */
2571 if (!lpApplicationName)
2572 {
2573 /* The fun begins */
2574 NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
2575 0,
2576 MAX_PATH * sizeof(WCHAR));
2577 if (NameBuffer == NULL)
2578 {
2579 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2580 goto Cleanup;
2581 }
2582
2583 /* This is all we have to work with :( */
2584 lpApplicationName = lpCommandLine;
2585
2586 /* Initialize our friends at the beginning */
2587 NullBuffer = (LPWSTR)lpApplicationName;
2588 ScanString = (LPWSTR)lpApplicationName;
2589
2590 /* We will start by looking for a quote */
2591 if (*ScanString == L'\"')
2592 {
2593 /* That was quick */
2594 SearchDone = TRUE;
2595
2596 /* Advance past quote */
2597 ScanString++;
2598 lpApplicationName = ScanString;
2599
2600 /* Find the closing quote */
2601 while (*ScanString)
2602 {
2603 if (*ScanString == L'\"' && *(ScanString - 1) != L'^')
2604 {
2605 /* Found it */
2606 NullBuffer = ScanString;
2607 FoundQuotes = TRUE;
2608 break;
2609 }
2610
2611 /* Keep looking */
2612 ScanString++;
2613 NullBuffer = ScanString;
2614 }
2615 }
2616 else
2617 {
2618 /* No quotes, so we'll be looking for white space */
2619 WhiteScan:
2620 /* Reset the pointer */
2621 lpApplicationName = lpCommandLine;
2622
2623 /* Find whitespace of Tab */
2624 while (*ScanString)
2625 {
2626 if (*ScanString == ' ' || *ScanString == '\t')
2627 {
2628 /* Found it */
2629 NullBuffer = ScanString;
2630 break;
2631 }
2632
2633 /* Keep looking */
2634 ScanString++;
2635 NullBuffer = ScanString;
2636 }
2637 }
2638
2639 /* Set the Null Buffer */
2640 SaveChar = *NullBuffer;
2641 *NullBuffer = UNICODE_NULL;
2642
2643 /* Do a search for the file */
2644 DPRINT("Ready for SearchPathW: %S\n", lpApplicationName);
2645 RetVal = SearchPathW(NULL,
2646 lpApplicationName,
2647 L".exe",
2648 MAX_PATH,
2649 NameBuffer,
2650 NULL) * sizeof(WCHAR);
2651
2652 /* Did it find something? */
2653 if (RetVal)
2654 {
2655 /* Get file attributes */
2656 ULONG Attributes = GetFileAttributesW(NameBuffer);
2657 if (Attributes & FILE_ATTRIBUTE_DIRECTORY)
2658 {
2659 /* Give it a length of 0 to fail, this was a directory. */
2660 RetVal = 0;
2661 }
2662 else
2663 {
2664 /* It's a file! */
2665 RetVal += sizeof(WCHAR);
2666 }
2667 }
2668
2669 /* Now check if we have a file, and if the path size is OK */
2670 if (!RetVal || RetVal >= (MAX_PATH * sizeof(WCHAR)))
2671 {
2672 ULONG PathType;
2673 HANDLE hFile;
2674
2675 /* We failed, try to get the Path Type */
2676 DPRINT("SearchPathW failed. Retval: %ld\n", RetVal);
2677 PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2678
2679 /* If it's not relative, try to get the error */
2680 if (PathType != RtlPathTypeRelative)
2681 {
2682 /* This should fail, and give us a detailed LastError */
2683 hFile = CreateFileW(lpApplicationName,
2684 GENERIC_READ,
2685 FILE_SHARE_READ | FILE_SHARE_WRITE,
2686 NULL,
2687 OPEN_EXISTING,
2688 FILE_ATTRIBUTE_NORMAL,
2689 NULL);
2690
2691 /* Did it actually NOT fail? */
2692 if (hFile != INVALID_HANDLE_VALUE)
2693 {
2694 /* Fake the error */
2695 CloseHandle(hFile);
2696 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2697 }
2698 }
2699 else
2700 {
2701 /* Immediately set the error */
2702 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2703 }
2704
2705 /* Did we already fail once? */
2706 if (Error)
2707 {
2708 SetLastError(Error);
2709 }
2710 else
2711 {
2712 /* Not yet, cache it */
2713 Error = GetLastError();
2714 }
2715
2716 /* Put back the command line */
2717 *NullBuffer = SaveChar;
2718 lpApplicationName = NameBuffer;
2719
2720 /*
2721 * If the search isn't done and we still have cmdline
2722 * then start over. Ex: c:\ha ha ha\haha.exe
2723 */
2724 if (*ScanString && !SearchDone)
2725 {
2726 /* Move in the buffer */
2727 ScanString++;
2728 NullBuffer = ScanString;
2729
2730 /* We will have to add a quote, since there is a space*/
2731 QuotesNeeded = TRUE;
2732
2733 /* And we will also fake the fact we found one */
2734 FoundQuotes = TRUE;
2735
2736 /* Start over */
2737 goto WhiteScan;
2738 }
2739
2740 /* We totally failed */
2741 goto Cleanup;
2742 }
2743
2744 /* Put back the command line */
2745 *NullBuffer = SaveChar;
2746 lpApplicationName = NameBuffer;
2747 DPRINT("SearchPathW suceeded (%ld): %S\n", RetVal, NameBuffer);
2748 }
2749 else if (!lpCommandLine || *lpCommandLine == UNICODE_NULL)
2750 {
2751 /* We have an app name (good!) but no command line */
2752 CmdLineIsAppName = TRUE;
2753 lpCommandLine = (LPWSTR)lpApplicationName;
2754 }
2755
2756 /* At this point the name has been toyed with enough to be openable */
2757 Status = BasepMapFile(lpApplicationName, &hSection, &ApplicationName);
2758
2759 /* Check for failure */
2760 if (!NT_SUCCESS(Status))
2761 {
2762 /* Could be a non-PE File */
2763 switch (Status)
2764 {
2765 /* Check if the Kernel tells us it's not even valid MZ */
2766 case STATUS_INVALID_IMAGE_NE_FORMAT:
2767 case STATUS_INVALID_IMAGE_PROTECT:
2768 case STATUS_INVALID_IMAGE_NOT_MZ:
2769
2770 #if 0
2771 /* If it's a DOS app, use VDM */
2772 if ((BasepCheckDosApp(&ApplicationName)))
2773 {
2774 DPRINT1("Launching VDM...\n");
2775 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
2776 RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer);
2777 return CreateProcessW(L"ntvdm.exe",
2778 (LPWSTR)((ULONG_PTR)lpApplicationName), /* FIXME: Buffer must be writable!!! */
2779 lpProcessAttributes,
2780 lpThreadAttributes,
2781 bInheritHandles,
2782 dwCreationFlags,
2783 lpEnvironment,
2784 lpCurrentDirectory,
2785 &StartupInfo,
2786 lpProcessInformation);
2787 }
2788 #endif
2789 /* It's a batch file */
2790 Extension = &ApplicationName.Buffer[ApplicationName.Length /
2791 sizeof(WCHAR) - 4];
2792
2793 /* Make sure the extensions are correct */
2794 if (_wcsnicmp(Extension, L".bat", 4) && _wcsnicmp(Extension, L".cmd", 4))
2795 {
2796 SetLastError(ERROR_BAD_EXE_FORMAT);
2797 return FALSE;
2798 }
2799
2800 /* Calculate the length of the command line */
2801 CmdLineLength = wcslen(CMD_STRING) + wcslen(lpCommandLine) + 1;
2802
2803 /* If we found quotes, then add them into the length size */
2804 if (CmdLineIsAppName || FoundQuotes) CmdLineLength += 2;
2805 CmdLineLength *= sizeof(WCHAR);
2806
2807 /* Allocate space for the new command line */
2808 BatchCommandLine = RtlAllocateHeap(RtlGetProcessHeap(),
2809 0,
2810 CmdLineLength);
2811 if (BatchCommandLine == NULL)
2812 {
2813 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2814 goto Cleanup;
2815 }
2816
2817 /* Build it */
2818 wcscpy(BatchCommandLine, CMD_STRING);
2819 if (CmdLineIsAppName || FoundQuotes)
2820 {
2821 wcscat(BatchCommandLine, L"\"");
2822 }
2823 wcscat(BatchCommandLine, lpCommandLine);
2824 if (CmdLineIsAppName || FoundQuotes)
2825 {
2826 wcscat(BatchCommandLine, L"\"");
2827 }
2828
2829 /* Create it as a Unicode String */
2830 RtlInitUnicodeString(&CommandLineString, BatchCommandLine);
2831
2832 /* Set the command line to this */
2833 lpCommandLine = CommandLineString.Buffer;
2834 lpApplicationName = NULL;
2835
2836 /* Free memory */
2837 RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer);
2838 ApplicationName.Buffer = NULL;
2839 goto GetAppName;
2840 break;
2841
2842 case STATUS_INVALID_IMAGE_WIN_16:
2843
2844 /* It's a Win16 Image, use VDM */
2845 DPRINT1("Launching VDM...\n");
2846 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
2847 RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer);
2848 return CreateProcessW(L"ntvdm.exe",
2849 (LPWSTR)((ULONG_PTR)lpApplicationName), /* FIXME: Buffer must be writable!!! */
2850 lpProcessAttributes,
2851 lpThreadAttributes,
2852 bInheritHandles,
2853 dwCreationFlags,
2854 lpEnvironment,
2855 lpCurrentDirectory,
2856 &StartupInfo,
2857 lpProcessInformation);
2858
2859 case STATUS_OBJECT_NAME_NOT_FOUND:
2860 case STATUS_OBJECT_PATH_NOT_FOUND:
2861 BaseSetLastNTError(Status);
2862 goto Cleanup;
2863
2864 default:
2865 /* Invalid Image Type */
2866 SetLastError(ERROR_BAD_EXE_FORMAT);
2867 goto Cleanup;
2868 }
2869 }
2870
2871 /* Use our desktop if we didn't get any */
2872 if (!StartupInfo.lpDesktop)
2873 {
2874 StartupInfo.lpDesktop = OurPeb->ProcessParameters->DesktopInfo.Buffer;
2875 }
2876
2877 /* FIXME: Check if Application is allowed to run */
2878
2879 /* FIXME: Allow CREATE_SEPARATE only for WOW Apps, once we have that. */
2880
2881 /* Get some information about the executable */
2882 Status = ZwQuerySection(hSection,
2883 SectionImageInformation,
2884 &SectionImageInfo,
2885 sizeof(SectionImageInfo),
2886 NULL);
2887 if(!NT_SUCCESS(Status))
2888 {
2889 DPRINT1("Unable to get SectionImageInformation, status 0x%x\n", Status);
2890 BaseSetLastNTError(Status);
2891 goto Cleanup;
2892 }
2893
2894 /* Don't execute DLLs */
2895 if (SectionImageInfo.ImageCharacteristics & IMAGE_FILE_DLL)
2896 {
2897 DPRINT1("Can't execute a DLL\n");
2898 SetLastError(ERROR_BAD_EXE_FORMAT);
2899 goto Cleanup;
2900 }
2901
2902 /* FIXME: Check for Debugger */
2903
2904 /* FIXME: Check if Machine Type and SubSys Version Match */
2905
2906 /* We don't support POSIX or anything else for now */
2907 if (IMAGE_SUBSYSTEM_WINDOWS_GUI != SectionImageInfo.SubSystemType &&
2908 IMAGE_SUBSYSTEM_WINDOWS_CUI != SectionImageInfo.SubSystemType)
2909 {
2910 DPRINT1("Invalid subsystem %d\n", SectionImageInfo.SubSystemType);
2911 SetLastError(ERROR_BAD_EXE_FORMAT);
2912 goto Cleanup;
2913 }
2914
2915 if (IMAGE_SUBSYSTEM_WINDOWS_GUI == SectionImageInfo.SubSystemType)
2916 {
2917 /* Do not create a console for GUI applications */
2918 dwCreationFlags &= ~CREATE_NEW_CONSOLE;
2919 dwCreationFlags |= DETACHED_PROCESS;
2920 }
2921
2922 /* Initialize the process object attributes */
2923 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
2924 lpProcessAttributes,
2925 NULL);
2926
2927 /* Check if we're going to be debugged */
2928 if (dwCreationFlags & DEBUG_PROCESS)
2929 {
2930 /* FIXME: Set process flag */
2931 }
2932
2933 /* Check if we're going to be debugged */
2934 if (dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
2935 {
2936 /* Connect to DbgUi */
2937 Status = DbgUiConnectToDbg();
2938 if (!NT_SUCCESS(Status))
2939 {
2940 DPRINT1("Failed to connect to DbgUI!\n");
2941 BaseSetLastNTError(Status);
2942 goto Cleanup;
2943 }
2944
2945 /* Get the debug object */
2946 hDebug = DbgUiGetThreadDebugObject();
2947
2948 /* Check if only this process will be debugged */
2949 if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS)
2950 {
2951 /* FIXME: Set process flag */
2952 }
2953 }
2954
2955 /* Create the Process */
2956 Status = NtCreateProcess(&hProcess,
2957 PROCESS_ALL_ACCESS,
2958 ObjectAttributes,
2959 NtCurrentProcess(),
2960 (BOOLEAN)bInheritHandles,
2961 hSection,
2962 hDebug,
2963 NULL);
2964 if (!NT_SUCCESS(Status))
2965 {
2966 DPRINT1("Unable to create process, status 0x%x\n", Status);
2967 BaseSetLastNTError(Status);
2968 goto Cleanup;
2969 }
2970
2971 if (PriorityClass.PriorityClass != PROCESS_PRIORITY_CLASS_INVALID)
2972 {
2973 /* Set new class */
2974 Status = NtSetInformationProcess(hProcess,
2975 ProcessPriorityClass,
2976 &PriorityClass,
2977 sizeof(PROCESS_PRIORITY_CLASS));
2978 if(!NT_SUCCESS(Status))
2979 {
2980 DPRINT1("Unable to set new process priority, status 0x%x\n", Status);
2981 BaseSetLastNTError(Status);
2982 goto Cleanup;
2983 }
2984 }
2985
2986 /* Set Error Mode */
2987 if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE)
2988 {
2989 ULONG ErrorMode = SEM_FAILCRITICALERRORS;
2990 NtSetInformationProcess(hProcess,
2991 ProcessDefaultHardErrorMode,
2992 &ErrorMode,
2993 sizeof(ULONG));
2994 }
2995
2996 /* Convert the directory to a full path */
2997 if (lpCurrentDirectory)
2998 {
2999 /* Allocate a buffer */
3000 CurrentDirectory = RtlAllocateHeap(RtlGetProcessHeap(),
3001 0,
3002 (MAX_PATH + 1) * sizeof(WCHAR));
3003 if (CurrentDirectory == NULL)
3004 {
3005 DPRINT1("Cannot allocate memory for directory name\n");
3006 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3007 goto Cleanup;
3008 }
3009
3010 /* Get the length */
3011 if (GetFullPathNameW(lpCurrentDirectory,
3012 MAX_PATH,
3013 CurrentDirectory,
3014 &CurrentDirectoryPart) > MAX_PATH)
3015 {
3016 DPRINT1("Directory name too long\n");
3017 SetLastError(ERROR_DIRECTORY);
3018 goto Cleanup;
3019 }
3020 }
3021
3022 /* Insert quotes if needed */
3023 if (QuotesNeeded || CmdLineIsAppName)
3024 {
3025 /* Allocate a buffer */
3026 QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
3027 0,
3028 (wcslen(lpCommandLine) + 2 + 1) *
3029 sizeof(WCHAR));
3030 if (QuotedCmdLine == NULL)
3031 {
3032 DPRINT1("Cannot allocate memory for quoted command line\n");
3033 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3034 goto Cleanup;
3035 }
3036
3037 /* Copy the first quote */
3038 wcscpy(QuotedCmdLine, L"\"");
3039
3040 /* Save a null char */
3041 if (QuotesNeeded)
3042 {
3043 SaveChar = *NullBuffer;
3044 *NullBuffer = UNICODE_NULL;
3045 }
3046
3047 /* Add the command line and the finishing quote */
3048 wcscat(QuotedCmdLine, lpCommandLine);
3049 wcscat(QuotedCmdLine, L"\"");
3050
3051 /* Add the null char */
3052 if (QuotesNeeded)
3053 {
3054 *NullBuffer = SaveChar;
3055 wcscat(QuotedCmdLine, NullBuffer);
3056 }
3057
3058 DPRINT("Quoted CmdLine: %S\n", QuotedCmdLine);
3059 }
3060
3061 if (Escape)
3062 {
3063 if (QuotedCmdLine == NULL)
3064 {
3065 QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
3066 0,
3067 (wcslen(lpCommandLine) + 1) * sizeof(WCHAR));
3068 if (QuotedCmdLine == NULL)
3069 {
3070 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3071 goto Cleanup;
3072 }
3073 wcscpy(QuotedCmdLine, lpCommandLine);
3074 }
3075
3076 ScanString = QuotedCmdLine;
3077 while (NULL != (ScanString = wcschr(ScanString, L'^')))
3078 {
3079 ScanString++;
3080 if (*ScanString == L'\"' || *ScanString == L'^' || *ScanString == L'\\')
3081 {
3082 memmove(ScanString-1, ScanString, wcslen(ScanString) * sizeof(WCHAR) + sizeof(WCHAR));
3083 }
3084 }
3085 }
3086
3087 /* Get the Process Information */
3088 Status = NtQueryInformationProcess(hProcess,
3089 ProcessBasicInformation,
3090 &ProcessBasicInfo,
3091 sizeof(ProcessBasicInfo),
3092 NULL);
3093
3094 /* Convert the environment */
3095 if(lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3096 {
3097 lpEnvironment = BasepConvertUnicodeEnvironment(&EnvSize, lpEnvironment);
3098 if (!lpEnvironment) goto Cleanup;
3099 }
3100
3101 /* Create Process Environment */
3102 RemotePeb = ProcessBasicInfo.PebBaseAddress;
3103 Ret = BasePushProcessParameters(0,
3104 hProcess,
3105 RemotePeb,
3106 (LPWSTR)lpApplicationName,
3107 CurrentDirectory,
3108 (QuotesNeeded || CmdLineIsAppName || Escape) ?
3109 QuotedCmdLine : lpCommandLine,
3110 lpEnvironment,
3111 &StartupInfo,
3112 dwCreationFlags,
3113 bInheritHandles,
3114 0,
3115 NULL,
3116 0);
3117 if (!Ret) goto Cleanup;
3118
3119 /* Cleanup Environment */
3120 if (lpEnvironment && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3121 {
3122 RtlDestroyEnvironment(lpEnvironment);
3123 }
3124
3125 /* Close the section */
3126 NtClose(hSection);
3127 hSection = NULL;
3128
3129 /* Duplicate the handles if needed */
3130 if (!bInheritHandles && !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
3131 SectionImageInfo.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI)
3132 {
3133 PRTL_USER_PROCESS_PARAMETERS RemoteParameters;
3134
3135 /* Get the remote parameters */
3136 Status = NtReadVirtualMemory(hProcess,
3137 &RemotePeb->ProcessParameters,
3138 &RemoteParameters,
3139 sizeof(PVOID),
3140 NULL);
3141 if (!NT_SUCCESS(Status))
3142 {
3143 DPRINT1("Failed to read memory\n");
3144 goto Cleanup;
3145 }
3146
3147 /* Duplicate and write the handles */
3148 BasepDuplicateAndWriteHandle(hProcess,
3149 OurPeb->ProcessParameters->StandardInput,
3150 &RemoteParameters->StandardInput);
3151 BasepDuplicateAndWriteHandle(hProcess,
3152 OurPeb->ProcessParameters->StandardOutput,
3153 &RemoteParameters->StandardOutput);
3154 BasepDuplicateAndWriteHandle(hProcess,
3155 OurPeb->ProcessParameters->StandardError,
3156 &RemoteParameters->StandardError);
3157 }
3158
3159 /* Notify CSRSS */
3160 Status = BasepNotifyCsrOfCreation(dwCreationFlags,
3161 (HANDLE)ProcessBasicInfo.UniqueProcessId,
3162 bInheritHandles);
3163
3164 if (!NT_SUCCESS(Status))
3165 {
3166 DPRINT1("CSR Notification Failed\n");
3167 BaseSetLastNTError(Status);
3168 goto Cleanup;
3169 }
3170
3171 /* Create the first thread */
3172 DPRINT("Creating thread for process (EntryPoint = 0x%p)\n",
3173 SectionImageInfo.TransferAddress);
3174 hThread = BasepCreateFirstThread(hProcess,
3175 lpThreadAttributes,
3176 &SectionImageInfo,
3177 &ClientId);
3178
3179 if (hThread == NULL)
3180 {
3181 DPRINT1("Could not create Initial Thread\n");
3182 /* FIXME - set last error code */
3183 goto Cleanup;
3184 }
3185
3186 if (!(dwCreationFlags & CREATE_SUSPENDED))
3187 {
3188 NtResumeThread(hThread, &Dummy);
3189 }
3190
3191 /* Return Data */
3192 lpProcessInformation->dwProcessId = (DWORD)ClientId.UniqueProcess;
3193 lpProcessInformation->dwThreadId = (DWORD)ClientId.UniqueThread;
3194 lpProcessInformation->hProcess = hProcess;
3195 lpProcessInformation->hThread = hThread;
3196 DPRINT("hThread[%p]: %p inside hProcess[%p]: %p\n", hThread,
3197 ClientId.UniqueThread, ClientId.UniqueProcess, hProcess);
3198 hProcess = hThread = NULL;
3199 Ret = TRUE;
3200
3201 Cleanup:
3202 /* De-allocate heap strings */
3203 if (NameBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
3204 if (ApplicationName.Buffer)
3205 RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName.Buffer);
3206 if (CurrentDirectory) RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory);
3207 if (QuotedCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine);
3208
3209 /* Kill any handles still alive */
3210 if (hSection) NtClose(hSection);
3211 if (hThread)
3212 {
3213 /* We don't know any more details then this */
3214 NtTerminateProcess(hProcess, STATUS_UNSUCCESSFUL);
3215 NtClose(hThread);
3216 }
3217 if (hProcess) NtClose(hProcess);
3218
3219 /* Return Success */
3220 return Ret;
3221 }
3222
3223 /*
3224 * @implemented
3225 */
3226 BOOL
3227 WINAPI
3228 CreateProcessW(LPCWSTR lpApplicationName,
3229 LPWSTR lpCommandLine,
3230 LPSECURITY_ATTRIBUTES lpProcessAttributes,
3231 LPSECURITY_ATTRIBUTES lpThreadAttributes,
3232 BOOL bInheritHandles,
3233 DWORD dwCreationFlags,
3234 LPVOID lpEnvironment,
3235 LPCWSTR lpCurrentDirectory,
3236 LPSTARTUPINFOW lpStartupInfo,
3237 LPPROCESS_INFORMATION lpProcessInformation)
3238 {
3239 /* Call the internal (but exported) version */
3240 return CreateProcessInternalW(0,
3241 lpApplicationName,
3242 lpCommandLine,
3243 lpProcessAttributes,
3244 lpThreadAttributes,
3245 bInheritHandles,
3246 dwCreationFlags,
3247 lpEnvironment,
3248 lpCurrentDirectory,
3249 lpStartupInfo,
3250 lpProcessInformation,
3251 NULL);
3252 }
3253
3254 /*
3255 * @implemented
3256 */
3257 BOOL
3258 WINAPI
3259 CreateProcessInternalA(HANDLE hToken,
3260 LPCSTR lpApplicationName,
3261 LPSTR lpCommandLine,
3262 LPSECURITY_ATTRIBUTES lpProcessAttributes,
3263 LPSECURITY_ATTRIBUTES lpThreadAttributes,
3264 BOOL bInheritHandles,
3265 DWORD dwCreationFlags,
3266 LPVOID lpEnvironment,
3267 LPCSTR lpCurrentDirectory,
3268 LPSTARTUPINFOA lpStartupInfo,
3269 LPPROCESS_INFORMATION lpProcessInformation,
3270 PHANDLE hNewToken)
3271 {
3272 PUNICODE_STRING CommandLine = NULL;
3273 UNICODE_STRING DummyString;
3274 UNICODE_STRING LiveCommandLine;
3275 UNICODE_STRING ApplicationName;
3276 UNICODE_STRING CurrentDirectory;
3277 BOOL bRetVal;
3278 STARTUPINFOW StartupInfo;
3279
3280 DPRINT("dwCreationFlags %x, lpEnvironment %x, lpCurrentDirectory %x, "
3281 "lpStartupInfo %x, lpProcessInformation %x\n",
3282 dwCreationFlags, lpEnvironment, lpCurrentDirectory,
3283 lpStartupInfo, lpProcessInformation);
3284
3285 /* Copy Startup Info */
3286 RtlMoveMemory(&StartupInfo, lpStartupInfo, sizeof(*lpStartupInfo));
3287
3288 /* Initialize all strings to nothing */
3289 LiveCommandLine.Buffer = NULL;
3290 DummyString.Buffer = NULL;
3291 ApplicationName.Buffer = NULL;
3292 CurrentDirectory.Buffer = NULL;
3293 StartupInfo.lpDesktop = NULL;
3294 StartupInfo.lpReserved = NULL;
3295 StartupInfo.lpTitle = NULL;
3296
3297 /* Convert the Command line */
3298 if (lpCommandLine)
3299 {
3300 /* If it's too long, then we'll have a problem */
3301 if ((strlen(lpCommandLine) + 1) * sizeof(WCHAR) <
3302 NtCurrentTeb()->StaticUnicodeString.MaximumLength)
3303 {
3304 /* Cache it in the TEB */
3305 CommandLine = Basep8BitStringToStaticUnicodeString(lpCommandLine);
3306 }
3307 else
3308 {
3309 /* Use a dynamic version */
3310 Basep8BitStringToDynamicUnicodeString(&LiveCommandLine,
3311 lpCommandLine);
3312 }
3313 }
3314 else
3315 {
3316 /* The logic below will use CommandLine, so we must make it valid */
3317 CommandLine = &DummyString;
3318 }
3319
3320 /* Convert the Name and Directory */
3321 if (lpApplicationName)
3322 {
3323 Basep8BitStringToDynamicUnicodeString(&ApplicationName,
3324 lpApplicationName);
3325 }
3326 if (lpCurrentDirectory)
3327 {
3328 Basep8BitStringToDynamicUnicodeString(&CurrentDirectory,
3329 lpCurrentDirectory);
3330 }
3331
3332 /* Now convert Startup Strings */
3333 if (lpStartupInfo->lpReserved)
3334 {
3335 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpReserved,
3336 &StartupInfo.lpReserved);
3337 }
3338 if (lpStartupInfo->lpDesktop)
3339 {
3340 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpDesktop,
3341 &StartupInfo.lpDesktop);
3342 }
3343 if (lpStartupInfo->lpTitle)
3344 {
3345 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpTitle,
3346 &StartupInfo.lpTitle);
3347 }
3348
3349 /* Call the Unicode function */
3350 bRetVal = CreateProcessInternalW(hToken,
3351 ApplicationName.Buffer,
3352 LiveCommandLine.Buffer ?
3353 LiveCommandLine.Buffer : CommandLine->Buffer,
3354 lpProcessAttributes,
3355 lpThreadAttributes,
3356 bInheritHandles,
3357 dwCreationFlags,
3358 lpEnvironment,
3359 CurrentDirectory.Buffer,
3360 &StartupInfo,
3361 lpProcessInformation,
3362 hNewToken);
3363
3364 /* Clean up */
3365 RtlFreeUnicodeString(&ApplicationName);
3366 RtlFreeUnicodeString(&LiveCommandLine);
3367 RtlFreeUnicodeString(&CurrentDirectory);
3368 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpDesktop);
3369 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpReserved);
3370 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpTitle);
3371
3372 /* Return what Unicode did */
3373 return bRetVal;
3374 }
3375
3376 /*
3377 * FUNCTION: The CreateProcess function creates a new process and its
3378 * primary thread. The new process executes the specified executable file
3379 * ARGUMENTS:
3380 *
3381 * lpApplicationName = Pointer to name of executable module
3382 * lpCommandLine = Pointer to command line string
3383 * lpProcessAttributes = Process security attributes
3384 * lpThreadAttributes = Thread security attributes
3385 * bInheritHandles = Handle inheritance flag
3386 * dwCreationFlags = Creation flags
3387 * lpEnvironment = Pointer to new environment block
3388 * lpCurrentDirectory = Pointer to current directory name
3389 * lpStartupInfo = Pointer to startup info
3390 * lpProcessInformation = Pointer to process information
3391 *
3392 * @implemented
3393 */
3394 BOOL
3395 WINAPI
3396 CreateProcessA(LPCSTR lpApplicationName,
3397 LPSTR lpCommandLine,
3398 LPSECURITY_ATTRIBUTES lpProcessAttributes,
3399 LPSECURITY_ATTRIBUTES lpThreadAttributes,
3400 BOOL bInheritHandles,
3401 DWORD dwCreationFlags,
3402 LPVOID lpEnvironment,
3403 LPCSTR lpCurrentDirectory,
3404 LPSTARTUPINFOA lpStartupInfo,
3405 LPPROCESS_INFORMATION lpProcessInformation)
3406 {
3407 /* Call the internal (but exported) version */
3408 return CreateProcessInternalA(0,
3409 lpApplicationName,
3410 lpCommandLine,
3411 lpProcessAttributes,
3412 lpThreadAttributes,
3413 bInheritHandles,
3414 dwCreationFlags,
3415 lpEnvironment,
3416 lpCurrentDirectory,
3417 lpStartupInfo,
3418 lpProcessInformation,
3419 NULL);
3420 }
3421
3422 /* EOF */