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