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