[KERNEL32]
[reactos.git] / reactos / 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
53 /* FUNCTIONS ****************************************************************/
54
55 VOID
56 WINAPI
57 StuffStdHandle(IN HANDLE ProcessHandle,
58 IN HANDLE StandardHandle,
59 IN PHANDLE Address)
60 {
61 NTSTATUS Status;
62 HANDLE DuplicatedHandle;
63 SIZE_T NumberOfBytesWritten;
64
65 /* If there is no handle to duplicate, return immediately */
66 if (!StandardHandle) return;
67
68 /* Duplicate the handle */
69 Status = NtDuplicateObject(NtCurrentProcess(),
70 StandardHandle,
71 ProcessHandle,
72 &DuplicatedHandle,
73 0,
74 0,
75 DUPLICATE_SAME_ACCESS |
76 DUPLICATE_SAME_ATTRIBUTES);
77 if (!NT_SUCCESS(Status)) return;
78
79 /* Write it */
80 NtWriteVirtualMemory(ProcessHandle,
81 Address,
82 &DuplicatedHandle,
83 sizeof(HANDLE),
84 &NumberOfBytesWritten);
85 }
86
87 BOOLEAN
88 WINAPI
89 BuildSubSysCommandLine(IN LPCWSTR SubsystemName,
90 IN LPCWSTR ApplicationName,
91 IN LPCWSTR CommandLine,
92 OUT PUNICODE_STRING SubsysCommandLine)
93 {
94 UNICODE_STRING CommandLineString, ApplicationNameString;
95 PWCHAR Buffer;
96 ULONG Length;
97
98 /* Convert to unicode strings */
99 RtlInitUnicodeString(&CommandLineString, ApplicationName);
100 RtlInitUnicodeString(&ApplicationNameString, CommandLine);
101
102 /* Allocate buffer for the output string */
103 Length = CommandLineString.MaximumLength + ApplicationNameString.MaximumLength + 32;
104 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length);
105 RtlInitEmptyUnicodeString(SubsysCommandLine, Buffer, Length);
106 if (!Buffer)
107 {
108 /* Fail, no memory */
109 BaseSetLastNTError(STATUS_NO_MEMORY);
110 return FALSE;
111 }
112
113 /* Build the final subsystem command line */
114 RtlAppendUnicodeToString(SubsysCommandLine, SubsystemName);
115 RtlAppendUnicodeStringToString(SubsysCommandLine, &CommandLineString);
116 RtlAppendUnicodeToString(SubsysCommandLine, L" /C ");
117 RtlAppendUnicodeStringToString(SubsysCommandLine, &ApplicationNameString);
118 return TRUE;
119 }
120
121 BOOLEAN
122 WINAPI
123 BasepIsImageVersionOk(IN ULONG ImageMajorVersion,
124 IN ULONG ImageMinorVersion)
125 {
126 /* Accept images for NT 3.1 or higher, as long as they're not newer than us */
127 return ((ImageMajorVersion >= 3) &&
128 ((ImageMajorVersion != 3) ||
129 (ImageMinorVersion >= 10)) &&
130 (ImageMajorVersion <= SharedUserData->NtMajorVersion) &&
131 ((ImageMajorVersion != SharedUserData->NtMajorVersion) ||
132 (ImageMinorVersion <= SharedUserData->NtMinorVersion)));
133 }
134
135 NTSTATUS
136 WINAPI
137 BasepCheckWebBladeHashes(IN HANDLE FileHandle)
138 {
139 NTSTATUS Status;
140 CHAR Hash[16];
141
142 /* Get all the MD5 hashes */
143 Status = RtlComputeImportTableHash(FileHandle, Hash, 1);
144 if (!NT_SUCCESS(Status)) return Status;
145
146 /* Depending on which suite this is, run a bsearch and block the appropriate ones */
147 if (SharedUserData->SuiteMask & VER_SUITE_COMPUTE_SERVER)
148 {
149 DPRINT1("Egad! This is a ReactOS Compute Server and we should prevent you from using certain APIs...but we won't.");
150 }
151 else if (SharedUserData->SuiteMask & VER_SUITE_STORAGE_SERVER)
152 {
153 DPRINT1("Gasp! This is a ReactOS Storage Server and we should prevent you from using certain APIs...but we won't.");
154 }
155 else if (SharedUserData->SuiteMask & VER_SUITE_BLADE)
156 {
157 DPRINT1("Golly! This is a ReactOS Web Blade Server and we should prevent you from using certain APIs...but we won't.");
158 }
159
160 /* Actually, fuck it, don't block anything, we're open source */
161 return STATUS_SUCCESS;
162 }
163
164 NTSTATUS
165 NTAPI
166 BasepSaveAppCertRegistryValue(IN PLIST_ENTRY List,
167 IN PWCHAR ComponentName,
168 IN PWCHAR DllName)
169 {
170 /* Pretty much the only thing this key is used for, is malware */
171 UNIMPLEMENTED;
172 return STATUS_NOT_IMPLEMENTED;
173 }
174
175 NTSTATUS
176 NTAPI
177 BasepConfigureAppCertDlls(IN PWSTR ValueName,
178 IN ULONG ValueType,
179 IN PVOID ValueData,
180 IN ULONG ValueLength,
181 IN PVOID Context,
182 IN PVOID EntryContext)
183 {
184 /* Add this to the certification list */
185 return BasepSaveAppCertRegistryValue(Context, ValueName, ValueData);
186 }
187
188 NTSTATUS
189 WINAPI
190 BasepIsProcessAllowed(IN LPWSTR ApplicationName)
191 {
192 NTSTATUS Status, Status1;
193 PWCHAR Buffer;
194 UINT Length;
195 HMODULE TrustLibrary;
196 PBASEP_APPCERT_ENTRY Entry;
197 ULONG CertFlag;
198 PLIST_ENTRY NextEntry;
199 HANDLE KeyHandle;
200 UNICODE_STRING CertKey = RTL_CONSTANT_STRING(L"\\Registry\\MACHINE\\System\\CurrentControlSet\\Control\\Session Manager\\AppCertDlls");
201 OBJECT_ATTRIBUTES KeyAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES(&CertKey, OBJ_CASE_INSENSITIVE);
202
203 /* Try to initialize the certification subsystem */
204 while (!g_AppCertInitialized)
205 {
206 /* Defaults */
207 Status = STATUS_SUCCESS;
208 Buffer = NULL;
209
210 /* Acquire the lock while initializing and see if we lost a race */
211 RtlEnterCriticalSection(&gcsAppCert);
212 if (g_AppCertInitialized) break;
213
214 /* On embedded, there is a special DLL */
215 if (SharedUserData->SuiteMask & VER_SUITE_EMBEDDEDNT)
216 {
217 /* Allocate a buffer for the name */
218 Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
219 0,
220 MAX_PATH * sizeof(WCHAR) +
221 sizeof(UNICODE_NULL));
222 if (!Buffer)
223 {
224 /* Fail if no memory */
225 Status = STATUS_NO_MEMORY;
226 }
227 else
228 {
229 /* Now get the system32 directory in our buffer, make sure it fits */
230 Length = GetSystemDirectoryW(Buffer, MAX_PATH - sizeof("EmbdTrst.DLL"));
231 if ((Length) && (Length <= MAX_PATH - sizeof("EmbdTrst.DLL")))
232 {
233 /* Add a slash if needed, and add the embedded cert DLL name */
234 if (Buffer[Length - 1] != '\\') Buffer[Length++] = '\\';
235 RtlCopyMemory(&Buffer[Length],
236 L"EmbdTrst.DLL",
237 sizeof(L"EmbdTrst.DLL"));
238
239 /* Try to load it */
240 TrustLibrary = LoadLibraryW(Buffer);
241 if (TrustLibrary)
242 {
243 /* And extract the special function out of it */
244 fEmbeddedCertFunc = (PVOID)GetProcAddress(TrustLibrary,
245 "ImageOkToRunOnEmbeddedNT");
246 }
247 }
248
249 /* If we didn't get this far, set a failure code */
250 if (!fEmbeddedCertFunc) Status = STATUS_UNSUCCESSFUL;
251 }
252 }
253 else
254 {
255 /* Other systems have a registry entry for this */
256 Status1 = NtOpenKey(&KeyHandle, KEY_READ, &KeyAttributes);
257 if (NT_SUCCESS(Status1))
258 {
259 /* Close it, we'll query it through Rtl */
260 NtClose(KeyHandle);
261
262 /* Do the query, which will call a special callback */
263 Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL,
264 L"Session Manager",
265 BasepAppCertTable,
266 NULL,
267 NULL);
268 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
269 {
270 Status = STATUS_SUCCESS;
271 }
272 }
273 }
274
275 /* Free any buffer if we had one */
276 if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
277
278 /* Check for errors, or a missing embedded/custom certification DLL */
279 if (!NT_SUCCESS(Status) ||
280 (!(fEmbeddedCertFunc) && (IsListEmpty(&BasepAppCertDllsList))))
281 {
282 /* The subsystem is not active on this machine, so give up */
283 g_HaveAppCerts = FALSE;
284 g_AppCertStatus = Status;
285 }
286 else
287 {
288 /* We have certification DLLs active, remember this */
289 g_HaveAppCerts = TRUE;
290 }
291
292 /* We are done the initialization phase, release the lock */
293 g_AppCertInitialized = TRUE;
294 RtlLeaveCriticalSection(&gcsAppCert);
295 }
296
297 /* If there's no certification DLLs present, return the failure code */
298 if (!g_HaveAppCerts) return g_AppCertStatus;
299
300 /* Otherwise, assume success and make sure we have *something* */
301 ASSERT(fEmbeddedCertFunc || !IsListEmpty(&BasepAppCertDllsList));
302 Status = STATUS_SUCCESS;
303
304 /* If the something is an embedded certification DLL, call it and return */
305 if (fEmbeddedCertFunc) return fEmbeddedCertFunc(ApplicationName);
306
307 /* Otherwise we have custom certification DLLs, parse them */
308 NextEntry = BasepAppCertDllsList.Flink;
309 CertFlag = 2;
310 while (NextEntry != &BasepAppCertDllsList)
311 {
312 /* Make sure the entry has a callback */
313 Entry = CONTAINING_RECORD(NextEntry, BASEP_APPCERT_ENTRY, Entry);
314 ASSERT(Entry->fPluginCertFunc != NULL);
315
316 /* Call it and check if it failed */
317 Status = Entry->fPluginCertFunc(ApplicationName, 1);
318 if (!NT_SUCCESS(Status)) CertFlag = 3;
319
320 /* Move on */
321 NextEntry = NextEntry->Flink;
322 }
323
324 /* Now loop them again */
325 NextEntry = BasepAppCertDllsList.Flink;
326 while (NextEntry != &BasepAppCertDllsList)
327 {
328 /* Make sure the entry has a callback */
329 Entry = CONTAINING_RECORD(NextEntry, BASEP_APPCERT_ENTRY, Entry);
330 ASSERT(Entry->fPluginCertFunc != NULL);
331
332 /* Call it, this time with the flag from the loop above */
333 Status = Entry->fPluginCertFunc(ApplicationName, CertFlag);
334 }
335
336 /* All done, return the status */
337 return Status;
338 }
339
340 NTSTATUS
341 WINAPI
342 BasepReplaceProcessThreadTokens(IN HANDLE TokenHandle,
343 IN HANDLE ProcessHandle,
344 IN HANDLE ThreadHandle)
345 {
346 NTSTATUS Status;
347 ANSI_STRING SaferiReplaceProcessThreadTokens = RTL_CONSTANT_STRING("SaferiReplaceProcessThreadTokens");
348
349 /* Enter the application certification lock */
350 RtlEnterCriticalSection(&gcsAppCert);
351
352 /* Check if we already know the function */
353 if (g_SaferReplaceProcessThreadTokens)
354 {
355 /* Call it */
356 Status = g_SaferReplaceProcessThreadTokens(TokenHandle,
357 ProcessHandle,
358 ThreadHandle) ?
359 STATUS_SUCCESS :
360 STATUS_UNSUCCESSFUL;
361 }
362 else
363 {
364 /* Check if the app certification DLL isn't loaded */
365 if (!(gSaferHandle) ||
366 (gSaferHandle == (HMODULE)-1) ||
367 (gSaferHandle == (HMODULE)-2))
368 {
369 /* Then we can't call the function */
370 Status = STATUS_ENTRYPOINT_NOT_FOUND;
371 }
372 else
373 {
374 /* We have the DLL, find the address of the Safer function */
375 Status = LdrGetProcedureAddress(gSaferHandle,
376 &SaferiReplaceProcessThreadTokens,
377 0,
378 (PVOID*)&g_SaferReplaceProcessThreadTokens);
379 if (NT_SUCCESS(Status))
380 {
381 /* Found it, now call it */
382 Status = g_SaferReplaceProcessThreadTokens(TokenHandle,
383 ProcessHandle,
384 ThreadHandle) ?
385 STATUS_SUCCESS :
386 STATUS_UNSUCCESSFUL;
387 }
388 else
389 {
390 /* We couldn't find it, so this must be an unsupported DLL */
391 LdrUnloadDll(gSaferHandle);
392 gSaferHandle = NULL;
393 Status = STATUS_ENTRYPOINT_NOT_FOUND;
394 }
395 }
396 }
397
398 /* Release the lock and return the result */
399 RtlLeaveCriticalSection(&gcsAppCert);
400 return Status;
401 }
402
403 VOID
404 WINAPI
405 BasepSxsCloseHandles(IN PBASE_MSG_SXS_HANDLES Handles)
406 {
407 NTSTATUS Status;
408
409 /* Sanity checks */
410 ASSERT(Handles != NULL);
411 ASSERT(Handles->Process == NULL || Handles->Process == NtCurrentProcess());
412
413 /* Close the file handle */
414 if (Handles->File)
415 {
416 Status = NtClose(Handles->File);
417 ASSERT(NT_SUCCESS(Status));
418 }
419
420 /* Close the section handle */
421 if (Handles->Section)
422 {
423 Status = NtClose(Handles->Section);
424 ASSERT(NT_SUCCESS(Status));
425 }
426
427 /* Unmap the section view */
428 if (Handles->ViewBase.QuadPart)
429 {
430 Status = NtUnmapViewOfSection(NtCurrentProcess(),
431 (PVOID)Handles->ViewBase.LowPart);
432 ASSERT(NT_SUCCESS(Status));
433 }
434 }
435
436 static
437 LONG BaseExceptionFilter(EXCEPTION_POINTERS *ExceptionInfo)
438 {
439 LONG ExceptionDisposition = EXCEPTION_EXECUTE_HANDLER;
440 LPTOP_LEVEL_EXCEPTION_FILTER RealFilter;
441 RealFilter = RtlDecodePointer(GlobalTopLevelExceptionFilter);
442
443 if (RealFilter != NULL)
444 {
445 _SEH2_TRY
446 {
447 ExceptionDisposition = RealFilter(ExceptionInfo);
448 }
449 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
450 {
451 }
452 _SEH2_END;
453 }
454 if ((ExceptionDisposition == EXCEPTION_CONTINUE_SEARCH || ExceptionDisposition == EXCEPTION_EXECUTE_HANDLER) &&
455 RealFilter != UnhandledExceptionFilter)
456 {
457 ExceptionDisposition = UnhandledExceptionFilter(ExceptionInfo);
458 }
459
460 return ExceptionDisposition;
461 }
462
463 VOID
464 WINAPI
465 BaseProcessStartup(PPROCESS_START_ROUTINE lpStartAddress)
466 {
467 DPRINT("BaseProcessStartup(..) - setting up exception frame.\n");
468
469 _SEH2_TRY
470 {
471 /* Set our Start Address */
472 NtSetInformationThread(NtCurrentThread(),
473 ThreadQuerySetWin32StartAddress,
474 &lpStartAddress,
475 sizeof(PPROCESS_START_ROUTINE));
476
477 /* Call the Start Routine */
478 ExitThread(lpStartAddress());
479 }
480 _SEH2_EXCEPT(BaseExceptionFilter(_SEH2_GetExceptionInformation()))
481 {
482 /* Get the Exit code from the SEH Handler */
483 if (!BaseRunningInServerProcess)
484 {
485 /* Kill the whole process, usually */
486 ExitProcess(_SEH2_GetExceptionCode());
487 }
488 else
489 {
490 /* If running inside CSRSS, kill just this thread */
491 ExitThread(_SEH2_GetExceptionCode());
492 }
493 }
494 _SEH2_END;
495 }
496
497 NTSTATUS
498 WINAPI
499 BasepNotifyCsrOfThread(IN HANDLE ThreadHandle,
500 IN PCLIENT_ID ClientId)
501 {
502 NTSTATUS Status;
503 BASE_API_MESSAGE ApiMessage;
504 PBASE_CREATE_THREAD CreateThreadRequest = &ApiMessage.Data.CreateThreadRequest;
505
506 DPRINT("BasepNotifyCsrOfThread: Thread: %p, Handle %p\n",
507 ClientId->UniqueThread, ThreadHandle);
508
509 /* Fill out the request */
510 CreateThreadRequest->ClientId = *ClientId;
511 CreateThreadRequest->ThreadHandle = ThreadHandle;
512
513 /* Call CSR */
514 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
515 NULL,
516 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateThread),
517 sizeof(BASE_CREATE_THREAD));
518 if (!NT_SUCCESS(Status))
519 {
520 DPRINT1("Failed to tell CSRSS about new thread: %lx\n", Status);
521 return Status;
522 }
523
524 /* Return Success */
525 return STATUS_SUCCESS;
526 }
527
528 BOOLEAN
529 WINAPI
530 BasePushProcessParameters(IN ULONG ParameterFlags,
531 IN HANDLE ProcessHandle,
532 IN PPEB RemotePeb,
533 IN LPCWSTR ApplicationPathName,
534 IN LPWSTR lpCurrentDirectory,
535 IN LPWSTR lpCommandLine,
536 IN LPVOID lpEnvironment,
537 IN LPSTARTUPINFOW StartupInfo,
538 IN DWORD CreationFlags,
539 IN BOOL InheritHandles,
540 IN ULONG ImageSubsystem,
541 IN PVOID AppCompatData,
542 IN ULONG AppCompatDataSize)
543 {
544 WCHAR FullPath[MAX_PATH + 5];
545 PWCHAR Remaining, DllPathString, ScanChar;
546 PRTL_USER_PROCESS_PARAMETERS ProcessParameters, RemoteParameters;
547 PVOID RemoteAppCompatData;
548 UNICODE_STRING DllPath, ImageName, CommandLine, CurrentDirectory;
549 UNICODE_STRING Desktop, Shell, Runtime, Title;
550 NTSTATUS Status;
551 ULONG EnviroSize;
552 SIZE_T Size;
553 BOOLEAN HavePebLock = FALSE, Result;
554 PPEB Peb = NtCurrentPeb();
555
556 /* Get the full path name */
557 Size = GetFullPathNameW(ApplicationPathName,
558 MAX_PATH + 4,
559 FullPath,
560 &Remaining);
561 if ((Size) && (Size <= (MAX_PATH + 4)))
562 {
563 /* Get the DLL Path */
564 DllPathString = BaseComputeProcessDllPath(FullPath, lpEnvironment);
565 if (!DllPathString)
566 {
567 /* Fail */
568 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
569 return FALSE;
570 }
571
572 /* Initialize Strings */
573 RtlInitUnicodeString(&DllPath, DllPathString);
574 RtlInitUnicodeString(&ImageName, FullPath);
575 }
576 else
577 {
578 /* Couldn't get the path name. Just take the original path */
579 DllPathString = BaseComputeProcessDllPath((LPWSTR)ApplicationPathName,
580 lpEnvironment);
581 if (!DllPathString)
582 {
583 /* Fail */
584 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
585 return FALSE;
586 }
587
588 /* Initialize Strings */
589 RtlInitUnicodeString(&DllPath, DllPathString);
590 RtlInitUnicodeString(&ImageName, ApplicationPathName);
591 }
592
593 /* Initialize Strings */
594 RtlInitUnicodeString(&CommandLine, lpCommandLine);
595 RtlInitUnicodeString(&CurrentDirectory, lpCurrentDirectory);
596
597 /* Initialize more Strings from the Startup Info */
598 if (StartupInfo->lpDesktop)
599 {
600 RtlInitUnicodeString(&Desktop, StartupInfo->lpDesktop);
601 }
602 else
603 {
604 RtlInitUnicodeString(&Desktop, L"");
605 }
606 if (StartupInfo->lpReserved)
607 {
608 RtlInitUnicodeString(&Shell, StartupInfo->lpReserved);
609 }
610 else
611 {
612 RtlInitUnicodeString(&Shell, L"");
613 }
614 if (StartupInfo->lpTitle)
615 {
616 RtlInitUnicodeString(&Title, StartupInfo->lpTitle);
617 }
618 else
619 {
620 RtlInitUnicodeString(&Title, ApplicationPathName);
621 }
622
623 /* This one is special because the length can differ */
624 Runtime.Buffer = (LPWSTR)StartupInfo->lpReserved2;
625 Runtime.MaximumLength = Runtime.Length = StartupInfo->cbReserved2;
626
627 /* Enforce no app compat data if the pointer was NULL */
628 if (!AppCompatData) AppCompatDataSize = 0;
629
630 /* Create the Parameter Block */
631 ProcessParameters = NULL;
632 DPRINT1("ImageName: '%wZ'\n", &ImageName);
633 DPRINT1("DllPath : '%wZ'\n", &DllPath);
634 DPRINT1("CurDir : '%wZ'\n", &CurrentDirectory);
635 DPRINT1("CmdLine : '%wZ'\n", &CommandLine);
636 DPRINT1("Title : '%wZ'\n", &Title);
637 DPRINT1("Desktop : '%wZ'\n", &Desktop);
638 DPRINT1("Shell : '%wZ'\n", &Shell);
639 DPRINT1("Runtime : '%wZ'\n", &Runtime);
640 Status = RtlCreateProcessParameters(&ProcessParameters,
641 &ImageName,
642 &DllPath,
643 lpCurrentDirectory ?
644 &CurrentDirectory : NULL,
645 &CommandLine,
646 lpEnvironment,
647 &Title,
648 &Desktop,
649 &Shell,
650 &Runtime);
651 if (!NT_SUCCESS(Status)) goto FailPath;
652
653 /* Clear the current directory handle if not inheriting */
654 if (!InheritHandles) ProcessParameters->CurrentDirectory.Handle = NULL;
655
656 /* Check if the user passed in an environment */
657 if (lpEnvironment)
658 {
659 /* We should've made it part of the parameters block, enforce this */
660 ASSERT(ProcessParameters->Environment == lpEnvironment);
661 lpEnvironment = ProcessParameters->Environment;
662 }
663 else
664 {
665 /* The user did not, so use the one from the current PEB */
666 HavePebLock = TRUE;
667 RtlAcquirePebLock();
668 lpEnvironment = Peb->ProcessParameters->Environment;
669 }
670
671 /* Save pointer and start lookup */
672 ScanChar = lpEnvironment;
673 if (lpEnvironment)
674 {
675 /* Find the environment size */
676 while ((ScanChar[0]) || (ScanChar[1])) ++ScanChar;
677 ScanChar += (2 * sizeof(UNICODE_NULL));
678 EnviroSize = (ULONG_PTR)ScanChar - (ULONG_PTR)lpEnvironment;
679
680 /* Allocate and Initialize new Environment Block */
681 Size = EnviroSize;
682 ProcessParameters->Environment = NULL;
683 Status = NtAllocateVirtualMemory(ProcessHandle,
684 (PVOID*)&ProcessParameters->Environment,
685 0,
686 &Size,
687 MEM_COMMIT,
688 PAGE_READWRITE);
689 if (!NT_SUCCESS(Status)) goto FailPath;
690
691 /* Write the Environment Block */
692 Status = NtWriteVirtualMemory(ProcessHandle,
693 ProcessParameters->Environment,
694 lpEnvironment,
695 EnviroSize,
696 NULL);
697
698 /* No longer need the PEB lock anymore */
699 if (HavePebLock)
700 {
701 /* Release it */
702 RtlReleasePebLock();
703 HavePebLock = FALSE;
704 }
705
706 /* Check if the write failed */
707 if (!NT_SUCCESS(Status)) goto FailPath;
708 }
709
710 /* Write new parameters */
711 ProcessParameters->StartingX = StartupInfo->dwX;
712 ProcessParameters->StartingY = StartupInfo->dwY;
713 ProcessParameters->CountX = StartupInfo->dwXSize;
714 ProcessParameters->CountY = StartupInfo->dwYSize;
715 ProcessParameters->CountCharsX = StartupInfo->dwXCountChars;
716 ProcessParameters->CountCharsY = StartupInfo->dwYCountChars;
717 ProcessParameters->FillAttribute = StartupInfo->dwFillAttribute;
718 ProcessParameters->WindowFlags = StartupInfo->dwFlags;
719 ProcessParameters->ShowWindowFlags = StartupInfo->wShowWindow;
720
721 /* Write the handles only if we have to */
722 if (StartupInfo->dwFlags &
723 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))
724 {
725 ProcessParameters->StandardInput = StartupInfo->hStdInput;
726 ProcessParameters->StandardOutput = StartupInfo->hStdOutput;
727 ProcessParameters->StandardError = StartupInfo->hStdError;
728 }
729
730 /* Use Special Flags for BasepInitConsole in Kernel32 */
731 if (CreationFlags & DETACHED_PROCESS)
732 {
733 ProcessParameters->ConsoleHandle = HANDLE_DETACHED_PROCESS;
734 }
735 else if (CreationFlags & CREATE_NEW_CONSOLE)
736 {
737 ProcessParameters->ConsoleHandle = HANDLE_CREATE_NEW_CONSOLE;
738 }
739 else if (CreationFlags & CREATE_NO_WINDOW)
740 {
741 ProcessParameters->ConsoleHandle = HANDLE_CREATE_NO_WINDOW;
742 }
743 else
744 {
745 /* Inherit our Console Handle */
746 ProcessParameters->ConsoleHandle = Peb->ProcessParameters->ConsoleHandle;
747
748 /* Make sure that the shell isn't trampling on our handles first */
749 if (!(StartupInfo->dwFlags &
750 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
751 {
752 /* Copy the handle if we are inheriting or if it's a console handle */
753 if ((InheritHandles) ||
754 (IsConsoleHandle(Peb->ProcessParameters->StandardInput)))
755 {
756 ProcessParameters->StandardInput = Peb->ProcessParameters->StandardInput;
757 }
758 if ((InheritHandles) ||
759 (IsConsoleHandle(Peb->ProcessParameters->StandardOutput)))
760 {
761 ProcessParameters->StandardOutput = Peb->ProcessParameters->StandardOutput;
762 }
763 if ((InheritHandles) ||
764 (IsConsoleHandle(Peb->ProcessParameters->StandardError)))
765 {
766 ProcessParameters->StandardError = Peb->ProcessParameters->StandardError;
767 }
768 }
769 }
770
771 /* Also set the Console Flag */
772 if ((CreationFlags & CREATE_NEW_PROCESS_GROUP) &&
773 (!(CreationFlags & CREATE_NEW_CONSOLE)))
774 {
775 ProcessParameters->ConsoleFlags = 1;
776 }
777
778 /* Check if there's a .local file present */
779 if (ParameterFlags & 1)
780 {
781 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_LOCAL_DLL_PATH;
782 }
783
784 /* Check if we failed to open the IFEO key */
785 if (ParameterFlags & 2)
786 {
787 ProcessParameters->Flags |= RTL_USER_PROCESS_PARAMETERS_IMAGE_KEY_MISSING;
788 }
789
790 /* Allocate memory for the parameter block */
791 Size = ProcessParameters->Length;
792 RemoteParameters = NULL;
793 Status = NtAllocateVirtualMemory(ProcessHandle,
794 (PVOID*)&RemoteParameters,
795 0,
796 &Size,
797 MEM_COMMIT,
798 PAGE_READWRITE);
799 if (!NT_SUCCESS(Status)) goto FailPath;
800
801 /* Set the allocated size */
802 ProcessParameters->MaximumLength = Size;
803
804 /* Handle some Parameter Flags */
805 ProcessParameters->Flags |= (CreationFlags & PROFILE_USER) ?
806 RTL_USER_PROCESS_PARAMETERS_PROFILE_USER : 0;
807 ProcessParameters->Flags |= (CreationFlags & PROFILE_KERNEL) ?
808 RTL_USER_PROCESS_PARAMETERS_PROFILE_KERNEL : 0;
809 ProcessParameters->Flags |= (CreationFlags & PROFILE_SERVER) ?
810 RTL_USER_PROCESS_PARAMETERS_PROFILE_SERVER : 0;
811 ProcessParameters->Flags |= (Peb->ProcessParameters->Flags &
812 RTL_USER_PROCESS_PARAMETERS_DISABLE_HEAP_CHECKS);
813
814 /* Write the Parameter Block */
815 Status = NtWriteVirtualMemory(ProcessHandle,
816 RemoteParameters,
817 ProcessParameters,
818 ProcessParameters->Length,
819 NULL);
820 if (!NT_SUCCESS(Status)) goto FailPath;
821
822 /* Write the PEB Pointer */
823 Status = NtWriteVirtualMemory(ProcessHandle,
824 &RemotePeb->ProcessParameters,
825 &RemoteParameters,
826 sizeof(PVOID),
827 NULL);
828 if (!NT_SUCCESS(Status)) goto FailPath;
829
830 /* Check if there's any app compat data to write */
831 RemoteAppCompatData = NULL;
832 if (AppCompatData)
833 {
834 /* Allocate some space for the application compatibility data */
835 Size = AppCompatDataSize;
836 Status = NtAllocateVirtualMemory(ProcessHandle,
837 &RemoteAppCompatData,
838 0,
839 &Size,
840 MEM_COMMIT,
841 PAGE_READWRITE);
842 if (!NT_SUCCESS(Status)) goto FailPath;
843
844 /* Write the application compatibility data */
845 Status = NtWriteVirtualMemory(ProcessHandle,
846 RemoteAppCompatData,
847 AppCompatData,
848 AppCompatDataSize,
849 NULL);
850 if (!NT_SUCCESS(Status)) goto FailPath;
851 }
852
853 /* Write the PEB Pointer to the app compat data (might be NULL) */
854 Status = NtWriteVirtualMemory(ProcessHandle,
855 &RemotePeb->pShimData,
856 &RemoteAppCompatData,
857 sizeof(PVOID),
858 NULL);
859 if (!NT_SUCCESS(Status)) goto FailPath;
860
861 /* Now write Peb->ImageSubSystem */
862 if (ImageSubsystem)
863 {
864 NtWriteVirtualMemory(ProcessHandle,
865 &RemotePeb->ImageSubsystem,
866 &ImageSubsystem,
867 sizeof(ImageSubsystem),
868 NULL);
869 }
870
871 /* Success path */
872 Result = TRUE;
873
874 Quickie:
875 /* Cleanup */
876 if (HavePebLock) RtlReleasePebLock();
877 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath.Buffer);
878 if (ProcessParameters) RtlDestroyProcessParameters(ProcessParameters);
879 return Result;
880 FailPath:
881 DPRINT1("Failure to create process parameters: %lx\n", Status);
882 BaseSetLastNTError(Status);
883 Result = FALSE;
884 goto Quickie;
885 }
886
887 VOID
888 WINAPI
889 InitCommandLines(VOID)
890 {
891 NTSTATUS Status;
892
893 /* Read the UNICODE_STRING from the PEB */
894 BaseUnicodeCommandLine = NtCurrentPeb()->ProcessParameters->CommandLine;
895
896 /* Convert to ANSI_STRING for the *A callers */
897 Status = RtlUnicodeStringToAnsiString(&BaseAnsiCommandLine,
898 &BaseUnicodeCommandLine,
899 TRUE);
900 if (!NT_SUCCESS(Status)) RtlInitEmptyAnsiString(&BaseAnsiCommandLine, 0, 0);
901 }
902
903 /* PUBLIC FUNCTIONS ***********************************************************/
904
905 /*
906 * @implemented
907 */
908 BOOL
909 WINAPI
910 GetProcessAffinityMask(IN HANDLE hProcess,
911 OUT PDWORD_PTR lpProcessAffinityMask,
912 OUT PDWORD_PTR lpSystemAffinityMask)
913 {
914 PROCESS_BASIC_INFORMATION ProcessInfo;
915 NTSTATUS Status;
916
917 /* Query information on the process from the kernel */
918 Status = NtQueryInformationProcess(hProcess,
919 ProcessBasicInformation,
920 (PVOID)&ProcessInfo,
921 sizeof(PROCESS_BASIC_INFORMATION),
922 NULL);
923 if (!NT_SUCCESS(Status))
924 {
925 /* Fail */
926 BaseSetLastNTError(Status);
927 return FALSE;
928 }
929
930 /* Copy the affinity mask, and get the system one from our shared data */
931 *lpProcessAffinityMask = (DWORD)ProcessInfo.AffinityMask;
932 *lpSystemAffinityMask = (DWORD)BaseStaticServerData->SysInfo.ActiveProcessorsAffinityMask;
933 return TRUE;
934 }
935
936 /*
937 * @implemented
938 */
939 BOOL
940 WINAPI
941 SetProcessAffinityMask(IN HANDLE hProcess,
942 IN DWORD_PTR dwProcessAffinityMask)
943 {
944 NTSTATUS Status;
945
946 /* Directly set the affinity mask */
947 Status = NtSetInformationProcess(hProcess,
948 ProcessAffinityMask,
949 (PVOID)&dwProcessAffinityMask,
950 sizeof(DWORD));
951 if (!NT_SUCCESS(Status))
952 {
953 /* Handle failure */
954 BaseSetLastNTError(Status);
955 return FALSE;
956 }
957
958 /* Everything was ok */
959 return TRUE;
960 }
961
962 /*
963 * @implemented
964 */
965 BOOL
966 WINAPI
967 GetProcessShutdownParameters(OUT LPDWORD lpdwLevel,
968 OUT LPDWORD lpdwFlags)
969 {
970 NTSTATUS Status;
971 BASE_API_MESSAGE ApiMessage;
972 PBASE_GETSET_PROCESS_SHUTDOWN_PARAMS ShutdownParametersRequest = &ApiMessage.Data.ShutdownParametersRequest;
973
974 /* Ask CSRSS for shutdown information */
975 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
976 NULL,
977 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepGetProcessShutdownParam),
978 sizeof(BASE_GETSET_PROCESS_SHUTDOWN_PARAMS));
979 if (!NT_SUCCESS(Status))
980 {
981 /* Return the failure from CSRSS */
982 BaseSetLastNTError(Status);
983 return FALSE;
984 }
985
986 /* Get the data back */
987 *lpdwLevel = ShutdownParametersRequest->ShutdownLevel;
988 *lpdwFlags = ShutdownParametersRequest->ShutdownFlags;
989 return TRUE;
990 }
991
992 /*
993 * @implemented
994 */
995 BOOL
996 WINAPI
997 SetProcessShutdownParameters(IN DWORD dwLevel,
998 IN DWORD dwFlags)
999 {
1000 NTSTATUS Status;
1001 BASE_API_MESSAGE ApiMessage;
1002 PBASE_GETSET_PROCESS_SHUTDOWN_PARAMS ShutdownParametersRequest = &ApiMessage.Data.ShutdownParametersRequest;
1003
1004 /* Write the data into the CSRSS request and send it */
1005 ShutdownParametersRequest->ShutdownLevel = dwLevel;
1006 ShutdownParametersRequest->ShutdownFlags = dwFlags;
1007 Status = CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1008 NULL,
1009 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepSetProcessShutdownParam),
1010 sizeof(BASE_GETSET_PROCESS_SHUTDOWN_PARAMS));
1011 if (!NT_SUCCESS(Status))
1012 {
1013 /* Return the failure from CSRSS */
1014 BaseSetLastNTError(Status);
1015 return FALSE;
1016 }
1017
1018 /* All went well */
1019 return TRUE;
1020 }
1021
1022 /*
1023 * @implemented
1024 */
1025 BOOL
1026 WINAPI
1027 GetProcessWorkingSetSizeEx(IN HANDLE hProcess,
1028 OUT PSIZE_T lpMinimumWorkingSetSize,
1029 OUT PSIZE_T lpMaximumWorkingSetSize,
1030 OUT PDWORD Flags)
1031 {
1032 QUOTA_LIMITS_EX QuotaLimits;
1033 NTSTATUS Status;
1034
1035 /* Query the kernel about this */
1036 Status = NtQueryInformationProcess(hProcess,
1037 ProcessQuotaLimits,
1038 &QuotaLimits,
1039 sizeof(QUOTA_LIMITS_EX),
1040 NULL);
1041 if (!NT_SUCCESS(Status))
1042 {
1043 /* Return error */
1044 BaseSetLastNTError(Status);
1045 return FALSE;
1046 }
1047
1048 /* Copy the quota information out */
1049 *lpMinimumWorkingSetSize = QuotaLimits.MinimumWorkingSetSize;
1050 *lpMaximumWorkingSetSize = QuotaLimits.MaximumWorkingSetSize;
1051 *Flags = QuotaLimits.Flags;
1052 return TRUE;
1053 }
1054
1055 /*
1056 * @implemented
1057 */
1058 BOOL
1059 WINAPI
1060 GetProcessWorkingSetSize(IN HANDLE hProcess,
1061 OUT PSIZE_T lpMinimumWorkingSetSize,
1062 OUT PSIZE_T lpMaximumWorkingSetSize)
1063 {
1064 DWORD Dummy;
1065 return GetProcessWorkingSetSizeEx(hProcess,
1066 lpMinimumWorkingSetSize,
1067 lpMaximumWorkingSetSize,
1068 &Dummy);
1069 }
1070
1071 /*
1072 * @implemented
1073 */
1074 BOOL
1075 WINAPI
1076 SetProcessWorkingSetSizeEx(IN HANDLE hProcess,
1077 IN SIZE_T dwMinimumWorkingSetSize,
1078 IN SIZE_T dwMaximumWorkingSetSize,
1079 IN DWORD Flags)
1080 {
1081 QUOTA_LIMITS_EX QuotaLimits;
1082 NTSTATUS Status, ReturnStatus;
1083 BOOL Result;
1084 PVOID State;
1085 ULONG Privilege = SE_INC_BASE_PRIORITY_PRIVILEGE;
1086
1087 /* Zero out the input structure */
1088 RtlZeroMemory(&QuotaLimits, sizeof(QuotaLimits));
1089
1090 /* Check if the caller sent any limits */
1091 if ((dwMinimumWorkingSetSize) && (dwMaximumWorkingSetSize))
1092 {
1093 /* Write the quota information */
1094 QuotaLimits.MinimumWorkingSetSize = dwMinimumWorkingSetSize;
1095 QuotaLimits.MaximumWorkingSetSize = dwMaximumWorkingSetSize;
1096 QuotaLimits.Flags = Flags;
1097
1098 /* Acquire the required privilege */
1099 Status = RtlAcquirePrivilege(&Privilege, 1, 0, &State);
1100
1101 /* Request the new quotas */
1102 ReturnStatus = NtSetInformationProcess(hProcess,
1103 ProcessQuotaLimits,
1104 &QuotaLimits,
1105 sizeof(QuotaLimits));
1106 Result = NT_SUCCESS(ReturnStatus);
1107 if (NT_SUCCESS(Status))
1108 {
1109 /* Release the privilege and set succes code */
1110 ASSERT(State != NULL);
1111 RtlReleasePrivilege(State);
1112 State = NULL;
1113 }
1114 }
1115 else
1116 {
1117 /* No limits, fail the call */
1118 ReturnStatus = STATUS_INVALID_PARAMETER;
1119 Result = FALSE;
1120 }
1121
1122 /* Return result code, set error code if this was a failure */
1123 if (!Result) BaseSetLastNTError(ReturnStatus);
1124 return Result;
1125 }
1126
1127 /*
1128 * @implemented
1129 */
1130 BOOL
1131 WINAPI
1132 SetProcessWorkingSetSize(IN HANDLE hProcess,
1133 IN SIZE_T dwMinimumWorkingSetSize,
1134 IN SIZE_T dwMaximumWorkingSetSize)
1135 {
1136 /* Call the newer API */
1137 return SetProcessWorkingSetSizeEx(hProcess,
1138 dwMinimumWorkingSetSize,
1139 dwMaximumWorkingSetSize,
1140 0);
1141 }
1142
1143 /*
1144 * @implemented
1145 */
1146 BOOL
1147 WINAPI
1148 GetProcessTimes(IN HANDLE hProcess,
1149 IN LPFILETIME lpCreationTime,
1150 IN LPFILETIME lpExitTime,
1151 IN LPFILETIME lpKernelTime,
1152 IN LPFILETIME lpUserTime)
1153 {
1154 KERNEL_USER_TIMES Kut;
1155 NTSTATUS Status;
1156
1157 /* Query the times */
1158 Status = NtQueryInformationProcess(hProcess,
1159 ProcessTimes,
1160 &Kut,
1161 sizeof(Kut),
1162 NULL);
1163 if (!NT_SUCCESS(Status))
1164 {
1165 /* Handle failure */
1166 BaseSetLastNTError(Status);
1167 return FALSE;
1168 }
1169
1170 /* Copy all the times and return success */
1171 lpCreationTime->dwLowDateTime = Kut.CreateTime.u.LowPart;
1172 lpCreationTime->dwHighDateTime = Kut.CreateTime.u.HighPart;
1173 lpExitTime->dwLowDateTime = Kut.ExitTime.u.LowPart;
1174 lpExitTime->dwHighDateTime = Kut.ExitTime.u.HighPart;
1175 lpKernelTime->dwLowDateTime = Kut.KernelTime.u.LowPart;
1176 lpKernelTime->dwHighDateTime = Kut.KernelTime.u.HighPart;
1177 lpUserTime->dwLowDateTime = Kut.UserTime.u.LowPart;
1178 lpUserTime->dwHighDateTime = Kut.UserTime.u.HighPart;
1179 return TRUE;
1180 }
1181
1182 /*
1183 * @implemented
1184 */
1185 HANDLE
1186 WINAPI
1187 GetCurrentProcess(VOID)
1188 {
1189 return (HANDLE)NtCurrentProcess();
1190 }
1191
1192 /*
1193 * @implemented
1194 */
1195 HANDLE
1196 WINAPI
1197 GetCurrentThread(VOID)
1198 {
1199 return (HANDLE)NtCurrentThread();
1200 }
1201
1202 /*
1203 * @implemented
1204 */
1205 DWORD
1206 WINAPI
1207 GetCurrentProcessId(VOID)
1208 {
1209 return HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess);
1210 }
1211
1212 /*
1213 * @implemented
1214 */
1215 BOOL
1216 WINAPI
1217 GetExitCodeProcess(IN HANDLE hProcess,
1218 IN LPDWORD lpExitCode)
1219 {
1220 PROCESS_BASIC_INFORMATION ProcessBasic;
1221 NTSTATUS Status;
1222
1223 /* Ask the kernel */
1224 Status = NtQueryInformationProcess(hProcess,
1225 ProcessBasicInformation,
1226 &ProcessBasic,
1227 sizeof(PROCESS_BASIC_INFORMATION),
1228 NULL);
1229 if (!NT_SUCCESS(Status))
1230 {
1231 /* We failed, was this because this is a VDM process? */
1232 if (BaseCheckForVDM(hProcess, lpExitCode) == TRUE) return TRUE;
1233
1234 /* Not a VDM process, fail the call */
1235 BaseSetLastNTError(Status);
1236 return FALSE;
1237 }
1238
1239 /* Succes case, return the exit code */
1240 *lpExitCode = (DWORD)ProcessBasic.ExitStatus;
1241 return TRUE;
1242 }
1243
1244 /*
1245 * @implemented
1246 */
1247 DWORD
1248 WINAPI
1249 GetProcessId(IN HANDLE Process)
1250 {
1251 PROCESS_BASIC_INFORMATION ProcessBasic;
1252 NTSTATUS Status;
1253
1254 /* Query the kernel */
1255 Status = NtQueryInformationProcess(Process,
1256 ProcessBasicInformation,
1257 &ProcessBasic,
1258 sizeof(PROCESS_BASIC_INFORMATION),
1259 NULL);
1260 if (!NT_SUCCESS(Status))
1261 {
1262 /* Handle failure */
1263 BaseSetLastNTError(Status);
1264 return 0;
1265 }
1266
1267 /* Return the PID */
1268 return (DWORD)ProcessBasic.UniqueProcessId;
1269 }
1270
1271 /*
1272 * @implemented
1273 */
1274 HANDLE
1275 WINAPI
1276 OpenProcess(IN DWORD dwDesiredAccess,
1277 IN BOOL bInheritHandle,
1278 IN DWORD dwProcessId)
1279 {
1280 NTSTATUS Status;
1281 HANDLE ProcessHandle;
1282 OBJECT_ATTRIBUTES ObjectAttributes;
1283 CLIENT_ID ClientId;
1284
1285 /* Setup the input client ID structure */
1286 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
1287 ClientId.UniqueThread = 0;
1288
1289 /* This is needed just to define the inheritance flags */
1290 InitializeObjectAttributes(&ObjectAttributes,
1291 NULL,
1292 (bInheritHandle ? OBJ_INHERIT : 0),
1293 NULL,
1294 NULL);
1295
1296 /* Now try to open the process */
1297 Status = NtOpenProcess(&ProcessHandle,
1298 dwDesiredAccess,
1299 &ObjectAttributes,
1300 &ClientId);
1301 if (!NT_SUCCESS(Status))
1302 {
1303 /* Handle failure */
1304 BaseSetLastNTError(Status);
1305 return NULL;
1306 }
1307
1308 /* Otherwise return a handle to the process */
1309 return ProcessHandle;
1310 }
1311
1312 /*
1313 * @implemented
1314 */
1315 VOID
1316 WINAPI
1317 RegisterWaitForInputIdle(IN WaitForInputIdleType lpfnRegisterWaitForInputIdle)
1318 {
1319 /* Write the global function pointer */
1320 UserWaitForInputIdleRoutine = lpfnRegisterWaitForInputIdle;
1321 }
1322
1323 /*
1324 * @implemented
1325 */
1326 VOID
1327 WINAPI
1328 GetStartupInfoW(IN LPSTARTUPINFOW lpStartupInfo)
1329 {
1330 PRTL_USER_PROCESS_PARAMETERS Params;
1331
1332 /* Get the process parameters */
1333 Params = NtCurrentPeb()->ProcessParameters;
1334
1335 /* Copy the data out of there */
1336 lpStartupInfo->cb = sizeof(STARTUPINFOW);
1337 lpStartupInfo->lpReserved = Params->ShellInfo.Buffer;
1338 lpStartupInfo->lpDesktop = Params->DesktopInfo.Buffer;
1339 lpStartupInfo->lpTitle = Params->WindowTitle.Buffer;
1340 lpStartupInfo->dwX = Params->StartingX;
1341 lpStartupInfo->dwY = Params->StartingY;
1342 lpStartupInfo->dwXSize = Params->CountX;
1343 lpStartupInfo->dwYSize = Params->CountY;
1344 lpStartupInfo->dwXCountChars = Params->CountCharsX;
1345 lpStartupInfo->dwYCountChars = Params->CountCharsY;
1346 lpStartupInfo->dwFillAttribute = Params->FillAttribute;
1347 lpStartupInfo->dwFlags = Params->WindowFlags;
1348 lpStartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags;
1349 lpStartupInfo->cbReserved2 = Params->RuntimeData.Length;
1350 lpStartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer;
1351
1352 /* Check if the standard handles are being used for other features */
1353 if (lpStartupInfo->dwFlags & (STARTF_USESTDHANDLES |
1354 STARTF_USEHOTKEY |
1355 STARTF_SHELLPRIVATE))
1356 {
1357 /* These are, so copy the standard handles too */
1358 lpStartupInfo->hStdInput = Params->StandardInput;
1359 lpStartupInfo->hStdOutput = Params->StandardOutput;
1360 lpStartupInfo->hStdError = Params->StandardError;
1361 }
1362 }
1363
1364 /*
1365 * @implemented
1366 */
1367 VOID
1368 WINAPI
1369 GetStartupInfoA(IN LPSTARTUPINFOA lpStartupInfo)
1370 {
1371 PRTL_USER_PROCESS_PARAMETERS Params;
1372 ANSI_STRING TitleString, ShellString, DesktopString;
1373 LPSTARTUPINFOA StartupInfo;
1374 NTSTATUS Status;
1375
1376 /* Get the cached information as well as the PEB parameters */
1377 StartupInfo = BaseAnsiStartupInfo;
1378 Params = NtCurrentPeb()->ProcessParameters;
1379
1380 /* Check if this is the first time we have to get the cached version */
1381 while (!StartupInfo)
1382 {
1383 /* Create new ANSI startup info */
1384 StartupInfo = RtlAllocateHeap(RtlGetProcessHeap(),
1385 0,
1386 sizeof(*StartupInfo));
1387 if (StartupInfo)
1388 {
1389 /* Zero out string pointers in case we fail to create them */
1390 StartupInfo->lpReserved = 0;
1391 StartupInfo->lpDesktop = 0;
1392 StartupInfo->lpTitle = 0;
1393
1394 /* Set the size */
1395 StartupInfo->cb = sizeof(*StartupInfo);
1396
1397 /* Copy what's already stored in the PEB */
1398 StartupInfo->dwX = Params->StartingX;
1399 StartupInfo->dwY = Params->StartingY;
1400 StartupInfo->dwXSize = Params->CountX;
1401 StartupInfo->dwYSize = Params->CountY;
1402 StartupInfo->dwXCountChars = Params->CountCharsX;
1403 StartupInfo->dwYCountChars = Params->CountCharsY;
1404 StartupInfo->dwFillAttribute = Params->FillAttribute;
1405 StartupInfo->dwFlags = Params->WindowFlags;
1406 StartupInfo->wShowWindow = (WORD)Params->ShowWindowFlags;
1407 StartupInfo->cbReserved2 = Params->RuntimeData.Length;
1408 StartupInfo->lpReserved2 = (LPBYTE)Params->RuntimeData.Buffer;
1409 StartupInfo->hStdInput = Params->StandardInput;
1410 StartupInfo->hStdOutput = Params->StandardOutput;
1411 StartupInfo->hStdError = Params->StandardError;
1412
1413 /* Copy shell info string */
1414 Status = RtlUnicodeStringToAnsiString(&ShellString,
1415 &Params->ShellInfo,
1416 TRUE);
1417 if (NT_SUCCESS(Status))
1418 {
1419 /* Save it */
1420 StartupInfo->lpReserved = ShellString.Buffer;
1421
1422 /* Copy desktop info string */
1423 Status = RtlUnicodeStringToAnsiString(&DesktopString,
1424 &Params->DesktopInfo,
1425 TRUE);
1426 if (NT_SUCCESS(Status))
1427 {
1428 /* Save it */
1429 StartupInfo->lpDesktop = DesktopString.Buffer;
1430
1431 /* Copy window title string */
1432 Status = RtlUnicodeStringToAnsiString(&TitleString,
1433 &Params->WindowTitle,
1434 TRUE);
1435 if (NT_SUCCESS(Status))
1436 {
1437 /* Save it */
1438 StartupInfo->lpTitle = TitleString.Buffer;
1439
1440 /* We finished with the ANSI version, try to cache it */
1441 if (!InterlockedCompareExchangePointer(&BaseAnsiStartupInfo,
1442 StartupInfo,
1443 NULL))
1444 {
1445 /* We were the first thread through, use the data */
1446 break;
1447 }
1448
1449 /* Someone beat us to it, use their data instead */
1450 StartupInfo = BaseAnsiStartupInfo;
1451 Status = STATUS_SUCCESS;
1452
1453 /* We're going to free our own stuff, but not raise */
1454 RtlFreeAnsiString(&TitleString);
1455 }
1456 RtlFreeAnsiString(&DesktopString);
1457 }
1458 RtlFreeAnsiString(&ShellString);
1459 }
1460 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo);
1461 }
1462 else
1463 {
1464 /* No memory, fail */
1465 Status = STATUS_NO_MEMORY;
1466 }
1467
1468 /* Raise an error unless we got here due to the race condition */
1469 if (!NT_SUCCESS(Status)) RtlRaiseStatus(Status);
1470 }
1471
1472 /* Now copy from the cached ANSI version */
1473 lpStartupInfo->cb = StartupInfo->cb;
1474 lpStartupInfo->lpReserved = StartupInfo->lpReserved;
1475 lpStartupInfo->lpDesktop = StartupInfo->lpDesktop;
1476 lpStartupInfo->lpTitle = StartupInfo->lpTitle;
1477 lpStartupInfo->dwX = StartupInfo->dwX;
1478 lpStartupInfo->dwY = StartupInfo->dwY;
1479 lpStartupInfo->dwXSize = StartupInfo->dwXSize;
1480 lpStartupInfo->dwYSize = StartupInfo->dwYSize;
1481 lpStartupInfo->dwXCountChars = StartupInfo->dwXCountChars;
1482 lpStartupInfo->dwYCountChars = StartupInfo->dwYCountChars;
1483 lpStartupInfo->dwFillAttribute = StartupInfo->dwFillAttribute;
1484 lpStartupInfo->dwFlags = StartupInfo->dwFlags;
1485 lpStartupInfo->wShowWindow = StartupInfo->wShowWindow;
1486 lpStartupInfo->cbReserved2 = StartupInfo->cbReserved2;
1487 lpStartupInfo->lpReserved2 = StartupInfo->lpReserved2;
1488
1489 /* Check if the shell is hijacking the handles for other features */
1490 if (lpStartupInfo->dwFlags &
1491 (STARTF_USESTDHANDLES | STARTF_USEHOTKEY | STARTF_SHELLPRIVATE))
1492 {
1493 /* It isn't, so we can return the raw values */
1494 lpStartupInfo->hStdInput = StartupInfo->hStdInput;
1495 lpStartupInfo->hStdOutput = StartupInfo->hStdOutput;
1496 lpStartupInfo->hStdError = StartupInfo->hStdError;
1497 }
1498 else
1499 {
1500 /* It is, so make sure nobody uses these as console handles */
1501 lpStartupInfo->hStdInput = INVALID_HANDLE_VALUE;
1502 lpStartupInfo->hStdOutput = INVALID_HANDLE_VALUE;
1503 lpStartupInfo->hStdError = INVALID_HANDLE_VALUE;
1504 }
1505 }
1506
1507 /*
1508 * @implemented
1509 */
1510 BOOL
1511 WINAPI
1512 FlushInstructionCache(IN HANDLE hProcess,
1513 IN LPCVOID lpBaseAddress,
1514 IN SIZE_T dwSize)
1515 {
1516 NTSTATUS Status;
1517
1518 /* Call the native function */
1519 Status = NtFlushInstructionCache(hProcess, (PVOID)lpBaseAddress, dwSize);
1520 if (!NT_SUCCESS(Status))
1521 {
1522 /* Handle failure case */
1523 BaseSetLastNTError(Status);
1524 return FALSE;
1525 }
1526
1527 /* All good */
1528 return TRUE;
1529 }
1530
1531 /*
1532 * @implemented
1533 */
1534 VOID
1535 WINAPI
1536 ExitProcess(IN UINT uExitCode)
1537 {
1538 BASE_API_MESSAGE ApiMessage;
1539 PBASE_EXIT_PROCESS ExitProcessRequest = &ApiMessage.Data.ExitProcessRequest;
1540
1541 ASSERT(!BaseRunningInServerProcess);
1542
1543 _SEH2_TRY
1544 {
1545 /* Acquire the PEB lock */
1546 RtlAcquirePebLock();
1547
1548 /* Kill all the threads */
1549 NtTerminateProcess(NULL, 0);
1550
1551 /* Unload all DLLs */
1552 LdrShutdownProcess();
1553
1554 /* Notify Base Server of process termination */
1555 ExitProcessRequest->uExitCode = uExitCode;
1556 CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage,
1557 NULL,
1558 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepExitProcess),
1559 sizeof(BASE_EXIT_PROCESS));
1560
1561 /* Now do it again */
1562 NtTerminateProcess(NtCurrentProcess(), uExitCode);
1563 }
1564 _SEH2_FINALLY
1565 {
1566 /* Release the PEB lock */
1567 RtlReleasePebLock();
1568 }
1569 _SEH2_END;
1570
1571 /* should never get here */
1572 ASSERT(0);
1573 while(1);
1574 }
1575
1576 /*
1577 * @implemented
1578 */
1579 BOOL
1580 WINAPI
1581 TerminateProcess(IN HANDLE hProcess,
1582 IN UINT uExitCode)
1583 {
1584 NTSTATUS Status;
1585
1586 /* Check if no handle was passed in */
1587 if (!hProcess)
1588 {
1589 /* Set error code */
1590 SetLastError(ERROR_INVALID_HANDLE);
1591 }
1592 else
1593 {
1594 /* Otherwise, try to terminate the process */
1595 Status = NtTerminateProcess(hProcess, uExitCode);
1596 if (NT_SUCCESS(Status)) return TRUE;
1597
1598 /* It failed, convert error code */
1599 BaseSetLastNTError(Status);
1600 }
1601
1602 /* This is the failure path */
1603 return FALSE;
1604 }
1605
1606 /*
1607 * @implemented
1608 */
1609 VOID
1610 WINAPI
1611 FatalAppExitA(UINT uAction,
1612 LPCSTR lpMessageText)
1613 {
1614 PUNICODE_STRING MessageTextU;
1615 ANSI_STRING MessageText;
1616 NTSTATUS Status;
1617
1618 /* Initialize the string using the static TEB pointer */
1619 MessageTextU = &NtCurrentTeb()->StaticUnicodeString;
1620 RtlInitAnsiString(&MessageText, (LPSTR)lpMessageText);
1621
1622 /* Convert to unicode and just exit normally if this failed */
1623 Status = RtlAnsiStringToUnicodeString(MessageTextU, &MessageText, FALSE);
1624 if (!NT_SUCCESS(Status)) ExitProcess(0);
1625
1626 /* Call the Wide function */
1627 FatalAppExitW(uAction, MessageTextU->Buffer);
1628 }
1629
1630 /*
1631 * @implemented
1632 */
1633 VOID
1634 WINAPI
1635 FatalAppExitW(IN UINT uAction,
1636 IN LPCWSTR lpMessageText)
1637 {
1638 UNICODE_STRING UnicodeString;
1639 ULONG Response;
1640 NTSTATUS Status;
1641
1642 /* Setup the string to print out */
1643 RtlInitUnicodeString(&UnicodeString, lpMessageText);
1644
1645 /* Display the hard error no matter what */
1646 Status = NtRaiseHardError(STATUS_FATAL_APP_EXIT | HARDERROR_OVERRIDE_ERRORMODE,
1647 1,
1648 1,
1649 (PULONG_PTR)&UnicodeString,
1650 OptionOkCancel,
1651 &Response);
1652
1653 /* Give the user a chance to abort */
1654 if ((NT_SUCCESS(Status)) && (Response == ResponseCancel)) return;
1655
1656 /* Otherwise kill the process */
1657 ExitProcess(0);
1658 }
1659
1660 /*
1661 * @implemented
1662 */
1663 VOID
1664 WINAPI
1665 FatalExit(IN int ExitCode)
1666 {
1667 #if DBG
1668 /* On Checked builds, Windows gives you a nice little debugger UI */
1669 CHAR ch[2];
1670 DbgPrint("FatalExit...\n");
1671 DbgPrint("\n");
1672
1673 while (TRUE)
1674 {
1675 DbgPrompt( "A (Abort), B (Break), I (Ignore)? ", ch, sizeof(ch));
1676 switch (ch[0])
1677 {
1678 case 'B': case 'b':
1679 DbgBreakPoint();
1680 break;
1681
1682 case 'A': case 'a':
1683 ExitProcess(ExitCode);
1684
1685 case 'I': case 'i':
1686 return;
1687 }
1688 }
1689 #endif
1690 /* On other builds, just kill the process */
1691 ExitProcess(ExitCode);
1692 }
1693
1694 /*
1695 * @implemented
1696 */
1697 DWORD
1698 WINAPI
1699 GetPriorityClass(IN HANDLE hProcess)
1700 {
1701 NTSTATUS Status;
1702 PROCESS_PRIORITY_CLASS PriorityClass;
1703
1704 /* Query the kernel */
1705 Status = NtQueryInformationProcess(hProcess,
1706 ProcessPriorityClass,
1707 &PriorityClass,
1708 sizeof(PROCESS_PRIORITY_CLASS),
1709 NULL);
1710 if (NT_SUCCESS(Status))
1711 {
1712 /* Handle the conversion from NT to Win32 classes */
1713 switch (PriorityClass.PriorityClass)
1714 {
1715 case PROCESS_PRIORITY_CLASS_IDLE: return IDLE_PRIORITY_CLASS;
1716 case PROCESS_PRIORITY_CLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS;
1717 case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL: return ABOVE_NORMAL_PRIORITY_CLASS;
1718 case PROCESS_PRIORITY_CLASS_HIGH: return HIGH_PRIORITY_CLASS;
1719 case PROCESS_PRIORITY_CLASS_REALTIME: return REALTIME_PRIORITY_CLASS;
1720 case PROCESS_PRIORITY_CLASS_NORMAL: default: return NORMAL_PRIORITY_CLASS;
1721 }
1722 }
1723
1724 /* Failure path */
1725 BaseSetLastNTError(Status);
1726 return FALSE;
1727 }
1728
1729 /*
1730 * @implemented
1731 */
1732 BOOL
1733 WINAPI
1734 SetPriorityClass(IN HANDLE hProcess,
1735 IN DWORD dwPriorityClass)
1736 {
1737 NTSTATUS Status;
1738 PVOID State = NULL;
1739 PROCESS_PRIORITY_CLASS PriorityClass;
1740
1741 /* Handle conversion from Win32 to NT priority classes */
1742 switch (dwPriorityClass)
1743 {
1744 case IDLE_PRIORITY_CLASS:
1745 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
1746 break;
1747
1748 case BELOW_NORMAL_PRIORITY_CLASS:
1749 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
1750 break;
1751
1752 case NORMAL_PRIORITY_CLASS:
1753 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
1754 break;
1755
1756 case ABOVE_NORMAL_PRIORITY_CLASS:
1757 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
1758 break;
1759
1760 case HIGH_PRIORITY_CLASS:
1761 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
1762 break;
1763
1764 case REALTIME_PRIORITY_CLASS:
1765 /* Try to acquire the privilege. If it fails, just use HIGH */
1766 State = BasepIsRealtimeAllowed(TRUE);
1767 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
1768 PriorityClass.PriorityClass += (State != NULL);
1769 break;
1770
1771 default:
1772 /* Unrecognized priority classes don't make it to the kernel */
1773 SetLastError(ERROR_INVALID_PARAMETER);
1774 return FALSE;
1775 }
1776
1777 /* Send the request to the kernel, and don't touch the foreground flag */
1778 PriorityClass.Foreground = FALSE;
1779 Status = NtSetInformationProcess(hProcess,
1780 ProcessPriorityClass,
1781 &PriorityClass,
1782 sizeof(PROCESS_PRIORITY_CLASS));
1783
1784 /* Release the privilege if we had it */
1785 if (State) RtlReleasePrivilege(State);
1786 if (!NT_SUCCESS(Status))
1787 {
1788 /* Handle error path */
1789 BaseSetLastNTError(Status);
1790 return FALSE;
1791 }
1792
1793 /* All done */
1794 return TRUE;
1795 }
1796
1797 /*
1798 * @implemented
1799 */
1800 DWORD
1801 WINAPI
1802 GetProcessVersion(IN DWORD ProcessId)
1803 {
1804 DWORD Version = 0;
1805 PIMAGE_NT_HEADERS NtHeader;
1806 PIMAGE_DOS_HEADER DosHeader;
1807 PPEB Peb;
1808 PROCESS_BASIC_INFORMATION ProcessBasicInfo;
1809 PVOID BaseAddress;
1810 ULONG e_lfanew;
1811 HANDLE ProcessHandle = NULL;
1812 NTSTATUS Status;
1813 USHORT VersionData[2];
1814 BOOLEAN Result;
1815
1816 /* We'll be accessing stuff that can fault, so protect everything with SEH */
1817 _SEH2_TRY
1818 {
1819 /* It this an in-process or out-of-process request? */
1820 if (!(ProcessId) || (GetCurrentProcessId() == ProcessId))
1821 {
1822 /* It's in-process, so just read our own header */
1823 NtHeader = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
1824 if (!NtHeader)
1825 {
1826 /* Unable to read the NT header, something is wrong here... */
1827 Status = STATUS_INVALID_IMAGE_FORMAT;
1828 goto Error;
1829 }
1830
1831 /* Get the version straight out of the NT header */
1832 Version = MAKELONG(NtHeader->OptionalHeader.MinorSubsystemVersion,
1833 NtHeader->OptionalHeader.MajorSubsystemVersion);
1834 }
1835 else
1836 {
1837 /* Out-of-process, so open it */
1838 ProcessHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
1839 FALSE,
1840 ProcessId);
1841 if (!ProcessHandle) return 0;
1842
1843 /* Try to find out where its PEB lives */
1844 Status = NtQueryInformationProcess(ProcessHandle,
1845 ProcessBasicInformation,
1846 &ProcessBasicInfo,
1847 sizeof(ProcessBasicInfo),
1848 NULL);
1849
1850 if (!NT_SUCCESS(Status)) goto Error;
1851 Peb = ProcessBasicInfo.PebBaseAddress;
1852
1853 /* Now that we have the PEB, read the image base address out of it */
1854 Result = ReadProcessMemory(ProcessHandle,
1855 &Peb->ImageBaseAddress,
1856 &BaseAddress,
1857 sizeof(BaseAddress),
1858 NULL);
1859 if (!Result) goto Error;
1860
1861 /* Now read the e_lfanew (offset to NT header) from the base */
1862 DosHeader = BaseAddress;
1863 Result = ReadProcessMemory(ProcessHandle,
1864 &DosHeader->e_lfanew,
1865 &e_lfanew,
1866 sizeof(e_lfanew),
1867 NULL);
1868 if (!Result) goto Error;
1869
1870 /* And finally, read the NT header itself by adding the offset */
1871 NtHeader = (PVOID)((ULONG_PTR)BaseAddress + e_lfanew);
1872 Result = ReadProcessMemory(ProcessHandle,
1873 &NtHeader->OptionalHeader.MajorSubsystemVersion,
1874 &VersionData,
1875 sizeof(VersionData),
1876 NULL);
1877 if (!Result) goto Error;
1878
1879 /* Get the version straight out of the NT header */
1880 Version = MAKELONG(VersionData[0], VersionData[1]);
1881
1882 Error:
1883 /* If there was an error anywhere, set the last error */
1884 if (!NT_SUCCESS(Status)) BaseSetLastNTError(Status);
1885 }
1886 }
1887 _SEH2_FINALLY
1888 {
1889 /* Close the process handle */
1890 if (ProcessHandle) CloseHandle(ProcessHandle);
1891 }
1892 _SEH2_END;
1893
1894 /* And return the version data */
1895 return Version;
1896 }
1897
1898 /*
1899 * @implemented
1900 */
1901 BOOL
1902 WINAPI
1903 GetProcessIoCounters(IN HANDLE hProcess,
1904 OUT PIO_COUNTERS lpIoCounters)
1905 {
1906 NTSTATUS Status;
1907
1908 /* Query the kernel. Structures are identical, so let it do the copy too. */
1909 Status = NtQueryInformationProcess(hProcess,
1910 ProcessIoCounters,
1911 lpIoCounters,
1912 sizeof(IO_COUNTERS),
1913 NULL);
1914 if (!NT_SUCCESS(Status))
1915 {
1916 /* Handle error path */
1917 BaseSetLastNTError(Status);
1918 return FALSE;
1919 }
1920
1921 /* All done */
1922 return TRUE;
1923 }
1924
1925 /*
1926 * @implemented
1927 */
1928 BOOL
1929 WINAPI
1930 GetProcessPriorityBoost(IN HANDLE hProcess,
1931 OUT PBOOL pDisablePriorityBoost)
1932 {
1933 NTSTATUS Status;
1934 ULONG PriorityBoost;
1935
1936 /* Query the kernel */
1937 Status = NtQueryInformationProcess(hProcess,
1938 ProcessPriorityBoost,
1939 &PriorityBoost,
1940 sizeof(ULONG),
1941 NULL);
1942 if (NT_SUCCESS(Status))
1943 {
1944 /* Convert from ULONG to a BOOL */
1945 *pDisablePriorityBoost = PriorityBoost ? TRUE : FALSE;
1946 return TRUE;
1947 }
1948
1949 /* Handle error path */
1950 BaseSetLastNTError(Status);
1951 return FALSE;
1952 }
1953
1954 /*
1955 * @implemented
1956 */
1957 BOOL
1958 WINAPI
1959 SetProcessPriorityBoost(IN HANDLE hProcess,
1960 IN BOOL bDisablePriorityBoost)
1961 {
1962 NTSTATUS Status;
1963 ULONG PriorityBoost;
1964
1965 /* Enforce that this is a BOOL, and send it to the kernel as a ULONG */
1966 PriorityBoost = (bDisablePriorityBoost ? TRUE : FALSE);
1967 Status = NtSetInformationProcess(hProcess,
1968 ProcessPriorityBoost,
1969 &PriorityBoost,
1970 sizeof(ULONG));
1971 if (!NT_SUCCESS(Status))
1972 {
1973 /* Handle error path */
1974 BaseSetLastNTError(Status);
1975 return FALSE;
1976 }
1977
1978 /* All done */
1979 return TRUE;
1980 }
1981
1982 /*
1983 * @implemented
1984 */
1985 BOOL
1986 WINAPI
1987 GetProcessHandleCount(IN HANDLE hProcess,
1988 OUT PDWORD pdwHandleCount)
1989 {
1990 ULONG phc;
1991 NTSTATUS Status;
1992
1993 /* Query the kernel */
1994 Status = NtQueryInformationProcess(hProcess,
1995 ProcessHandleCount,
1996 &phc,
1997 sizeof(ULONG),
1998 NULL);
1999 if (NT_SUCCESS(Status))
2000 {
2001 /* Copy the count and return sucecss */
2002 *pdwHandleCount = phc;
2003 return TRUE;
2004 }
2005
2006 /* Handle error path */
2007 BaseSetLastNTError(Status);
2008 return FALSE;
2009 }
2010
2011 /*
2012 * @implemented
2013 */
2014 BOOL
2015 WINAPI
2016 IsWow64Process(IN HANDLE hProcess,
2017 OUT PBOOL Wow64Process)
2018 {
2019 ULONG_PTR pbi;
2020 NTSTATUS Status;
2021
2022 /* Query the kernel */
2023 Status = NtQueryInformationProcess(hProcess,
2024 ProcessWow64Information,
2025 &pbi,
2026 sizeof(pbi),
2027 NULL);
2028 if (!NT_SUCCESS(Status))
2029 {
2030 /* Handle error path */
2031 BaseSetLastNTError(Status);
2032 return FALSE;
2033 }
2034
2035 /* Enforce this is a BOOL, and return success */
2036 *Wow64Process = (pbi != 0);
2037 return TRUE;
2038 }
2039
2040 /*
2041 * @implemented
2042 */
2043 LPSTR
2044 WINAPI
2045 GetCommandLineA(VOID)
2046 {
2047 return BaseAnsiCommandLine.Buffer;
2048 }
2049
2050 /*
2051 * @implemented
2052 */
2053 LPWSTR
2054 WINAPI
2055 GetCommandLineW(VOID)
2056 {
2057 return BaseUnicodeCommandLine.Buffer;
2058 }
2059
2060 /*
2061 * @implemented
2062 */
2063 BOOL
2064 NTAPI
2065 ReadProcessMemory(IN HANDLE hProcess,
2066 IN LPCVOID lpBaseAddress,
2067 IN LPVOID lpBuffer,
2068 IN SIZE_T nSize,
2069 OUT SIZE_T* lpNumberOfBytesRead)
2070 {
2071 NTSTATUS Status;
2072
2073 /* Do the read */
2074 Status = NtReadVirtualMemory(hProcess,
2075 (PVOID)lpBaseAddress,
2076 lpBuffer,
2077 nSize,
2078 &nSize);
2079
2080 /* In user-mode, this parameter is optional */
2081 if (lpNumberOfBytesRead) *lpNumberOfBytesRead = nSize;
2082 if (!NT_SUCCESS(Status))
2083 {
2084 /* We failed */
2085 BaseSetLastNTError(Status);
2086 return FALSE;
2087 }
2088
2089 /* Return success */
2090 return TRUE;
2091 }
2092
2093 /*
2094 * @implemented
2095 */
2096 BOOL
2097 NTAPI
2098 WriteProcessMemory(IN HANDLE hProcess,
2099 IN LPVOID lpBaseAddress,
2100 IN LPCVOID lpBuffer,
2101 IN SIZE_T nSize,
2102 OUT SIZE_T *lpNumberOfBytesWritten)
2103 {
2104 NTSTATUS Status;
2105 ULONG OldValue;
2106 SIZE_T RegionSize;
2107 PVOID Base;
2108 BOOLEAN UnProtect;
2109
2110 /* Set parameters for protect call */
2111 RegionSize = nSize;
2112 Base = lpBaseAddress;
2113
2114 /* Check the current status */
2115 Status = NtProtectVirtualMemory(hProcess,
2116 &Base,
2117 &RegionSize,
2118 PAGE_EXECUTE_READWRITE,
2119 &OldValue);
2120 if (NT_SUCCESS(Status))
2121 {
2122 /* Check if we are unprotecting */
2123 UnProtect = OldValue & (PAGE_READWRITE |
2124 PAGE_WRITECOPY |
2125 PAGE_EXECUTE_READWRITE |
2126 PAGE_EXECUTE_WRITECOPY) ? FALSE : TRUE;
2127 if (!UnProtect)
2128 {
2129 /* Set the new protection */
2130 Status = NtProtectVirtualMemory(hProcess,
2131 &Base,
2132 &RegionSize,
2133 OldValue,
2134 &OldValue);
2135
2136 /* Write the memory */
2137 Status = NtWriteVirtualMemory(hProcess,
2138 lpBaseAddress,
2139 (LPVOID)lpBuffer,
2140 nSize,
2141 &nSize);
2142
2143 /* In Win32, the parameter is optional, so handle this case */
2144 if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
2145
2146 if (!NT_SUCCESS(Status))
2147 {
2148 /* We failed */
2149 BaseSetLastNTError(Status);
2150 return FALSE;
2151 }
2152
2153 /* Flush the ITLB */
2154 NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
2155 return TRUE;
2156 }
2157 else
2158 {
2159 /* Check if we were read only */
2160 if (OldValue & (PAGE_NOACCESS | PAGE_READONLY))
2161 {
2162 /* Restore protection and fail */
2163 NtProtectVirtualMemory(hProcess,
2164 &Base,
2165 &RegionSize,
2166 OldValue,
2167 &OldValue);
2168 BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
2169
2170 /* Note: This is what Windows returns and code depends on it */
2171 return STATUS_ACCESS_VIOLATION;
2172 }
2173
2174 /* Otherwise, do the write */
2175 Status = NtWriteVirtualMemory(hProcess,
2176 lpBaseAddress,
2177 (LPVOID)lpBuffer,
2178 nSize,
2179 &nSize);
2180
2181 /* In Win32, the parameter is optional, so handle this case */
2182 if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
2183
2184 /* And restore the protection */
2185 NtProtectVirtualMemory(hProcess,
2186 &Base,
2187 &RegionSize,
2188 OldValue,
2189 &OldValue);
2190 if (!NT_SUCCESS(Status))
2191 {
2192 /* We failed */
2193 BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
2194
2195 /* Note: This is what Windows returns and code depends on it */
2196 return STATUS_ACCESS_VIOLATION;
2197 }
2198
2199 /* Flush the ITLB */
2200 NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
2201 return TRUE;
2202 }
2203 }
2204 else
2205 {
2206 /* We failed */
2207 BaseSetLastNTError(Status);
2208 return FALSE;
2209 }
2210 }
2211
2212 /*
2213 * @implemented
2214 */
2215 BOOL
2216 WINAPI
2217 ProcessIdToSessionId(IN DWORD dwProcessId,
2218 OUT PDWORD pSessionId)
2219 {
2220 PROCESS_SESSION_INFORMATION SessionInformation;
2221 OBJECT_ATTRIBUTES ObjectAttributes;
2222 CLIENT_ID ClientId;
2223 HANDLE ProcessHandle;
2224 NTSTATUS Status;
2225
2226 /* Do a quick check if the pointer is not writable */
2227 if (IsBadWritePtr(pSessionId, sizeof(DWORD)))
2228 {
2229 /* Fail fast */
2230 SetLastError(ERROR_INVALID_PARAMETER);
2231 return FALSE;
2232 }
2233
2234 /* Open the process passed in by ID */
2235 ClientId.UniqueProcess = UlongToHandle(dwProcessId);
2236 ClientId.UniqueThread = 0;
2237 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
2238 Status = NtOpenProcess(&ProcessHandle,
2239 PROCESS_QUERY_INFORMATION,
2240 &ObjectAttributes,
2241 &ClientId);
2242 if (NT_SUCCESS(Status))
2243 {
2244 /* Query the session ID from the kernel */
2245 Status = NtQueryInformationProcess(ProcessHandle,
2246 ProcessSessionInformation,
2247 &SessionInformation,
2248 sizeof(SessionInformation),
2249 NULL);
2250
2251 /* Close the handle and check if we suceeded */
2252 NtClose(ProcessHandle);
2253 if (NT_SUCCESS(Status))
2254 {
2255 /* Return the session ID */
2256 *pSessionId = SessionInformation.SessionId;
2257 return TRUE;
2258 }
2259 }
2260
2261 /* Set error code and fail */
2262 BaseSetLastNTError(Status);
2263 return FALSE;
2264 }
2265
2266
2267 #define AddToHandle(x,y) (x) = (HANDLE)((ULONG_PTR)(x) | (y));
2268 #define RemoveFromHandle(x,y) (x) = (HANDLE)((ULONG_PTR)(x) & ~(y));
2269 C_ASSERT(PROCESS_PRIORITY_CLASS_REALTIME == (PROCESS_PRIORITY_CLASS_HIGH + 1));
2270
2271 /*
2272 * @implemented
2273 */
2274 BOOL
2275 WINAPI
2276 CreateProcessInternalW(IN HANDLE hUserToken,
2277 IN LPCWSTR lpApplicationName,
2278 IN LPWSTR lpCommandLine,
2279 IN LPSECURITY_ATTRIBUTES lpProcessAttributes,
2280 IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
2281 IN BOOL bInheritHandles,
2282 IN DWORD dwCreationFlags,
2283 IN LPVOID lpEnvironment,
2284 IN LPCWSTR lpCurrentDirectory,
2285 IN LPSTARTUPINFOW lpStartupInfo,
2286 IN LPPROCESS_INFORMATION lpProcessInformation,
2287 OUT PHANDLE hNewToken)
2288 {
2289 //
2290 // Core variables used for creating the initial process and thread
2291 //
2292 SECURITY_ATTRIBUTES LocalThreadAttributes, LocalProcessAttributes;
2293 OBJECT_ATTRIBUTES LocalObjectAttributes;
2294 POBJECT_ATTRIBUTES ObjectAttributes;
2295 SECTION_IMAGE_INFORMATION ImageInformation;
2296 IO_STATUS_BLOCK IoStatusBlock;
2297 CLIENT_ID ClientId;
2298 ULONG NoWindow, RegionSize, StackSize, ImageMachine, ErrorCode, Flags;
2299 ULONG ParameterFlags, PrivilegeValue, HardErrorMode, ErrorResponse;
2300 ULONG_PTR ErrorParameters[2];
2301 BOOLEAN InJob, SaferNeeded, UseLargePages, HavePrivilege;
2302 BOOLEAN QuerySection, SkipSaferAndAppCompat;
2303 CONTEXT Context;
2304 BASE_API_MESSAGE CsrMsg;
2305 PBASE_CREATE_PROCESS CreateProcessMsg;
2306 PCSR_CAPTURE_BUFFER CaptureBuffer;
2307 PVOID BaseAddress, PrivilegeState, RealTimePrivilegeState;
2308 HANDLE DebugHandle, TokenHandle, JobHandle, KeyHandle, ThreadHandle;
2309 HANDLE FileHandle, SectionHandle, ProcessHandle;
2310 ULONG ResumeCount;
2311 PROCESS_PRIORITY_CLASS PriorityClass;
2312 NTSTATUS Status, Status1, ImageDbgStatus;
2313 PPEB Peb, RemotePeb;
2314 PTEB Teb;
2315 INITIAL_TEB InitialTeb;
2316 PVOID TibValue;
2317 PIMAGE_NT_HEADERS NtHeaders;
2318 STARTUPINFOW StartupInfo;
2319 PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
2320 UNICODE_STRING DebuggerString;
2321 BOOL Result;
2322 //
2323 // Variables used for command-line and argument parsing
2324 //
2325 PCHAR pcScan;
2326 SIZE_T n;
2327 WCHAR SaveChar;
2328 ULONG Length, CurdirLength, CmdQuoteLength;
2329 ULONG CmdLineLength, ResultSize;
2330 PWCHAR QuotedCmdLine, AnsiCmdCommand, ExtBuffer, CurrentDirectory;
2331 PWCHAR NullBuffer, ScanString, NameBuffer, SearchPath, DebuggerCmdLine;
2332 ANSI_STRING AnsiEnv;
2333 UNICODE_STRING UnicodeEnv, PathName;
2334 BOOLEAN SearchRetry, QuotesNeeded, CmdLineIsAppName, HasQuotes;
2335
2336 //
2337 // Variables used for Fusion/SxS (Side-by-Side Assemblies)
2338 //
2339 RTL_PATH_TYPE SxsPathType, PathType;
2340 #if _SXS_SUPPORT_ENABLED_
2341 PRTL_BUFFER ByteBuffer;
2342 PRTL_UNICODE_STRING_BUFFER ThisBuffer, Buffer, SxsStaticBuffers[5];
2343 PRTL_UNICODE_STRING_BUFFER* BufferHead, SxsStringBuffer;
2344 RTL_UNICODE_STRING_BUFFER SxsWin32ManifestPath, SxsNtManifestPath;
2345 RTL_UNICODE_STRING_BUFFER SxsWin32PolicyPath, SxsNtPolicyPath;
2346 RTL_UNICODE_STRING_BUFFER SxsWin32AssemblyDirectory;
2347 BASE_MSG_SXS_HANDLES MappedHandles, Handles, FileHandles;
2348 PVOID CapturedStrings[3];
2349 SXS_WIN32_NT_PATH_PAIR ExePathPair, ManifestPathPair, PolicyPathPair;
2350 SXS_OVERRIDE_MANIFEST OverrideMannifest;
2351 UNICODE_STRING FreeString, SxsNtExePath;
2352 PWCHAR SxsConglomeratedBuffer, StaticBuffer;
2353 ULONG ConglomeratedBufferSizeBytes, StaticBufferSize, i;
2354 #endif
2355 ULONG FusionFlags;
2356
2357 //
2358 // Variables used for path conversion (and partially Fusion/SxS)
2359 //
2360 PWCHAR FilePart, PathBuffer, FreeBuffer;
2361 BOOLEAN TranslationStatus;
2362 RTL_RELATIVE_NAME_U SxsWin32RelativePath;
2363 UNICODE_STRING PathBufferString, SxsWin32ExePath;
2364
2365 //
2366 // Variables used by Application Compatibility (and partially Fusion/SxS)
2367 //
2368 PVOID AppCompatSxsData, AppCompatData;
2369 ULONG AppCompatSxsDataSize, AppCompatDataSize;
2370 //
2371 // Variables used by VDM (Virtual Dos Machine) and WOW32 (16-bit Support)
2372 //
2373 ULONG BinarySubType, VdmBinaryType, VdmTask, VdmReserve;
2374 ULONG VdmUndoLevel;
2375 BOOLEAN UseVdmReserve;
2376 HANDLE VdmWaitObject;
2377 ANSI_STRING VdmAnsiEnv;
2378 UNICODE_STRING VdmString, VdmUnicodeEnv;
2379 BOOLEAN IsWowApp;
2380 PBASE_CHECK_VDM VdmMsg;
2381
2382 /* Zero out the initial core variables and handles */
2383 QuerySection = FALSE;
2384 InJob = FALSE;
2385 SkipSaferAndAppCompat = TRUE; // HACK for making .bat/.cmd launch working again.
2386 ParameterFlags = 0;
2387 Flags = 0;
2388 DebugHandle = NULL;
2389 JobHandle = NULL;
2390 TokenHandle = NULL;
2391 FileHandle = NULL;
2392 SectionHandle = NULL;
2393 ProcessHandle = NULL;
2394 ThreadHandle = NULL;
2395 BaseAddress = (PVOID)1;
2396
2397 /* Zero out initial SxS and Application Compatibility state */
2398 AppCompatData = NULL;
2399 AppCompatDataSize = 0;
2400 AppCompatSxsData = NULL;
2401 AppCompatSxsDataSize = 0;
2402 CaptureBuffer = NULL;
2403 #if _SXS_SUPPORT_ENABLED_
2404 SxsConglomeratedBuffer = NULL;
2405 #endif
2406 FusionFlags = 0;
2407
2408 /* Zero out initial parsing variables -- others are initialized later */
2409 DebuggerCmdLine = NULL;
2410 PathBuffer = NULL;
2411 SearchPath = NULL;
2412 NullBuffer = 0;
2413 FreeBuffer = NULL;
2414 NameBuffer = NULL;
2415 CurrentDirectory = NULL;
2416 FilePart = NULL;
2417 DebuggerString.Buffer = NULL;
2418 HasQuotes = FALSE;
2419 QuotedCmdLine = NULL;
2420
2421 /* Zero out initial VDM state */
2422 VdmAnsiEnv.Buffer = NULL;
2423 VdmUnicodeEnv.Buffer = NULL;
2424 VdmString.Buffer = NULL;
2425 VdmTask = 0;
2426 VdmUndoLevel = 0;
2427 VdmBinaryType = 0;
2428 VdmReserve = 0;
2429 VdmWaitObject = NULL;
2430 UseVdmReserve = FALSE;
2431 IsWowApp = FALSE;
2432
2433 /* Set message structures */
2434 CreateProcessMsg = &CsrMsg.Data.CreateProcessRequest;
2435 VdmMsg = &CsrMsg.Data.CheckVDMRequest;
2436
2437 /* Clear the more complex structures by zeroing out their entire memory */
2438 RtlZeroMemory(&Context, sizeof(Context));
2439 #if _SXS_SUPPORT_ENABLED_
2440 RtlZeroMemory(&FileHandles, sizeof(FileHandles));
2441 RtlZeroMemory(&MappedHandles, sizeof(MappedHandles));
2442 RtlZeroMemory(&Handles, sizeof(Handles));
2443 #endif
2444 RtlZeroMemory(&CreateProcessMsg->Sxs, sizeof(CreateProcessMsg->Sxs));
2445 RtlZeroMemory(&LocalProcessAttributes, sizeof(LocalProcessAttributes));
2446 RtlZeroMemory(&LocalThreadAttributes, sizeof(LocalThreadAttributes));
2447
2448 /* Zero out output arguments as well */
2449 RtlZeroMemory(lpProcessInformation, sizeof(*lpProcessInformation));
2450 if (hNewToken) *hNewToken = NULL;
2451
2452 /* Capture the special window flag */
2453 NoWindow = dwCreationFlags & CREATE_NO_WINDOW;
2454 dwCreationFlags &= ~CREATE_NO_WINDOW;
2455
2456 #if _SXS_SUPPORT_ENABLED_
2457 /* Setup the SxS static string arrays and buffers */
2458 SxsStaticBuffers[0] = &SxsWin32ManifestPath;
2459 SxsStaticBuffers[1] = &SxsWin32PolicyPath;
2460 SxsStaticBuffers[2] = &SxsWin32AssemblyDirectory;
2461 SxsStaticBuffers[3] = &SxsNtManifestPath;
2462 SxsStaticBuffers[4] = &SxsNtPolicyPath;
2463 ExePathPair.Win32 = &SxsWin32ExePath;
2464 ExePathPair.Nt = &SxsNtExePath;
2465 ManifestPathPair.Win32 = &SxsWin32ManifestPath.String;
2466 ManifestPathPair.Nt = &SxsNtManifestPath.String;
2467 PolicyPathPair.Win32 = &SxsWin32PolicyPath.String;
2468 PolicyPathPair.Nt = &SxsNtPolicyPath.String;
2469 #endif
2470
2471 DPRINT1("CreateProcessInternalW: %S %S %lx\n", lpApplicationName, lpCommandLine, dwCreationFlags);
2472
2473 /* Finally, set our TEB and PEB */
2474 Teb = NtCurrentTeb();
2475 Peb = NtCurrentPeb();
2476
2477 /* This combination is illegal (see MSDN) */
2478 if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
2479 (DETACHED_PROCESS | CREATE_NEW_CONSOLE))
2480 {
2481 DPRINT1("Invalid flag combo used\n");
2482 SetLastError(ERROR_INVALID_PARAMETER);
2483 return FALSE;
2484 }
2485
2486 /* Convert the priority class */
2487 if (dwCreationFlags & IDLE_PRIORITY_CLASS)
2488 {
2489 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
2490 }
2491 else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS)
2492 {
2493 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
2494 }
2495 else if (dwCreationFlags & NORMAL_PRIORITY_CLASS)
2496 {
2497 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
2498 }
2499 else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS)
2500 {
2501 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
2502 }
2503 else if (dwCreationFlags & HIGH_PRIORITY_CLASS)
2504 {
2505 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
2506 }
2507 else if (dwCreationFlags & REALTIME_PRIORITY_CLASS)
2508 {
2509 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
2510 PriorityClass.PriorityClass += (BasepIsRealtimeAllowed(FALSE) != NULL);
2511 }
2512 else
2513 {
2514 PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_INVALID;
2515 }
2516
2517 /* Done with the priority masks, so get rid of them */
2518 PriorityClass.Foreground = FALSE;
2519 dwCreationFlags &= ~(NORMAL_PRIORITY_CLASS |
2520 IDLE_PRIORITY_CLASS |
2521 HIGH_PRIORITY_CLASS |
2522 REALTIME_PRIORITY_CLASS |
2523 BELOW_NORMAL_PRIORITY_CLASS |
2524 ABOVE_NORMAL_PRIORITY_CLASS);
2525
2526 /* You cannot request both a shared and a separate WoW VDM */
2527 if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
2528 (dwCreationFlags & CREATE_SHARED_WOW_VDM))
2529 {
2530 /* Fail such nonsensical attempts */
2531 DPRINT1("Invalid WOW flags\n");
2532 SetLastError(ERROR_INVALID_PARAMETER);
2533 return FALSE;
2534 }
2535 else if (!(dwCreationFlags & CREATE_SHARED_WOW_VDM) &&
2536 (BaseStaticServerData->DefaultSeparateVDM))
2537 {
2538 /* A shared WoW VDM was not requested but system enforces separation */
2539 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
2540 }
2541
2542 /* If a shared WoW VDM is used, make sure the process isn't in a job */
2543 if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
2544 (NtIsProcessInJob(NtCurrentProcess(), NULL)))
2545 {
2546 /* Remove the shared flag and add the separate flag */
2547 dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) |
2548 CREATE_SEPARATE_WOW_VDM;
2549 }
2550
2551 /* Convert the environment */
2552 if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
2553 {
2554 /* Scan the environment to calculate its Unicode size */
2555 AnsiEnv.Buffer = pcScan = (PCHAR)lpEnvironment;
2556 while ((*pcScan) || (*(pcScan + 1))) ++pcScan;
2557
2558 /* Create our ANSI String */
2559 AnsiEnv.Length = pcScan - (PCHAR)lpEnvironment + sizeof(ANSI_NULL);
2560 AnsiEnv.MaximumLength = AnsiEnv.Length + sizeof(ANSI_NULL);
2561
2562 /* Allocate memory for the Unicode Environment */
2563 UnicodeEnv.Buffer = NULL;
2564 RegionSize = AnsiEnv.MaximumLength * sizeof(WCHAR);
2565 Status = NtAllocateVirtualMemory(NtCurrentProcess(),
2566 (PVOID)&UnicodeEnv.Buffer,
2567 0,
2568 &RegionSize,
2569 MEM_COMMIT,
2570 PAGE_READWRITE);
2571 if (!NT_SUCCESS(Status))
2572 {
2573 /* Fail */
2574 BaseSetLastNTError(Status);
2575 return FALSE;
2576 }
2577
2578 /* Use the allocated size and convert */
2579 UnicodeEnv.MaximumLength = RegionSize;
2580 Status = RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE);
2581 if (!NT_SUCCESS(Status))
2582 {
2583 /* Fail */
2584 NtFreeVirtualMemory(NtCurrentProcess(),
2585 (PVOID)&UnicodeEnv.Buffer,
2586 &RegionSize,
2587 MEM_RELEASE);
2588 BaseSetLastNTError(Status);
2589 return FALSE;
2590 }
2591
2592 /* Now set the Unicode environment as the environment string pointer */
2593 lpEnvironment = UnicodeEnv.Buffer;
2594 }
2595
2596 /* Make a copy of the caller's startup info since we'll modify it */
2597 StartupInfo = *lpStartupInfo;
2598
2599 /* Check if private data is being sent on the same channel as std handles */
2600 if ((StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
2601 (StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
2602 {
2603 /* Cannot use the std handles since we have monitor/hotkey values */
2604 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
2605 }
2606
2607 /* If there's a debugger, or we have to launch cmd.exe, we go back here */
2608 AppNameRetry:
2609 /* New iteration -- free any existing name buffer */
2610 if (NameBuffer)
2611 {
2612 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
2613 NameBuffer = NULL;
2614 }
2615
2616 /* New iteration -- free any existing free buffer */
2617 if (FreeBuffer)
2618 {
2619 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
2620 FreeBuffer = NULL;
2621 }
2622
2623 /* New iteration -- close any existing file handle */
2624 if (FileHandle)
2625 {
2626 NtClose(FileHandle);
2627 FileHandle = NULL;
2628 }
2629
2630 /* Set the initial parsing state. This code can loop -- don't move this! */
2631 ErrorCode = 0;
2632 SearchRetry = TRUE;
2633 QuotesNeeded = FALSE;
2634 CmdLineIsAppName = FALSE;
2635
2636 /* First check if we don't have an application name */
2637 if (!lpApplicationName)
2638 {
2639 /* This should be the first time we attempt creating one */
2640 ASSERT(NameBuffer == NULL);
2641
2642 /* Allocate a buffer to hold it */
2643 NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
2644 0,
2645 MAX_PATH * sizeof(WCHAR));
2646 if (!NameBuffer)
2647 {
2648 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2649 Result = FALSE;
2650 goto Quickie;
2651 }
2652
2653 /* Initialize the application name and our parsing parameters */
2654 lpApplicationName = NullBuffer = ScanString = lpCommandLine;
2655
2656 /* Check for an initial quote*/
2657 if (*lpCommandLine == L'\"')
2658 {
2659 /* We found a quote, keep searching for another one */
2660 SearchRetry = FALSE;
2661 ScanString++;
2662 lpApplicationName = ScanString;
2663 while (*ScanString)
2664 {
2665 /* Have we found the terminating quote? */
2666 if (*ScanString == L'\"')
2667 {
2668 /* We're done, get out of here */
2669 NullBuffer = ScanString;
2670 HasQuotes = TRUE;
2671 break;
2672 }
2673
2674 /* Keep searching for the quote */
2675 ScanString++;
2676 NullBuffer = ScanString;
2677 }
2678 }
2679 else
2680 {
2681 StartScan:
2682 /* We simply make the application name be the command line*/
2683 lpApplicationName = lpCommandLine;
2684 while (*ScanString)
2685 {
2686 /* Check if it starts with a space or tab */
2687 if ((*ScanString == L' ') || (*ScanString == L'\t'))
2688 {
2689 /* Break out of the search loop */
2690 NullBuffer = ScanString;
2691 break;
2692 }
2693
2694 /* Keep searching for a space or tab */
2695 ScanString++;
2696 NullBuffer = ScanString;
2697 }
2698 }
2699
2700 /* We have found the end of the application name, terminate it */
2701 SaveChar = *NullBuffer;
2702 *NullBuffer = UNICODE_NULL;
2703
2704 /* New iteration -- free any existing saved path */
2705 if (SearchPath)
2706 {
2707 RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
2708 SearchPath = NULL;
2709 }
2710
2711 /* Now compute the final EXE path based on the name */
2712 SearchPath = BaseComputeProcessExePath((LPWSTR)lpApplicationName);
2713 DPRINT1("Search Path: %S\n", SearchPath);
2714 if (!SearchPath)
2715 {
2716 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2717 Result = FALSE;
2718 goto Quickie;
2719 }
2720
2721 /* And search for the executable in the search path */
2722 Length = SearchPathW(SearchPath,
2723 lpApplicationName,
2724 L".exe",
2725 MAX_PATH,
2726 NameBuffer,
2727 NULL);
2728
2729 /* Did we find it? */
2730 if ((Length) && (Length < MAX_PATH))
2731 {
2732 /* Get file attributes */
2733 CurdirLength = GetFileAttributesW(NameBuffer);
2734 if ((CurdirLength != 0xFFFFFFFF) &&
2735 (CurdirLength & FILE_ATTRIBUTE_DIRECTORY))
2736 {
2737 /* This was a directory, fail later on */
2738 Length = 0;
2739 }
2740 else
2741 {
2742 /* It's a file! */
2743 Length++;
2744 }
2745 }
2746
2747 DPRINT1("Length: %lu Buffer: %S\n", Length, NameBuffer);
2748
2749 /* Check if there was a failure in SearchPathW */
2750 if ((Length) && (Length < MAX_PATH))
2751 {
2752 /* Everything looks good, restore the name */
2753 *NullBuffer = SaveChar;
2754 lpApplicationName = NameBuffer;
2755 }
2756 else
2757 {
2758 /* Check if this was a relative path, which would explain it */
2759 PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2760 if (PathType != RtlPathTypeRelative)
2761 {
2762 /* This should fail, and give us a detailed LastError */
2763 FileHandle = CreateFileW(lpApplicationName,
2764 GENERIC_READ,
2765 FILE_SHARE_READ |
2766 FILE_SHARE_WRITE,
2767 NULL,
2768 OPEN_EXISTING,
2769 FILE_ATTRIBUTE_NORMAL,
2770 NULL);
2771 if (FileHandle != INVALID_HANDLE_VALUE)
2772 {
2773 /* It worked? Return a generic error */
2774 CloseHandle(FileHandle);
2775 FileHandle = NULL;
2776 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2777 }
2778 }
2779 else
2780 {
2781 /* Path was absolute, which means it doesn't exist */
2782 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2783 }
2784
2785 /* Did we already fail once? */
2786 if (ErrorCode)
2787 {
2788 /* Set the error code */
2789 SetLastError(ErrorCode);
2790 }
2791 else
2792 {
2793 /* Not yet, cache it */
2794 ErrorCode = GetLastError();
2795 }
2796
2797 /* Put back the command line */
2798 *NullBuffer = SaveChar;
2799 lpApplicationName = NameBuffer;
2800
2801 /* It's possible there's whitespace in the directory name */
2802 if (!(*ScanString) || !(SearchRetry))
2803 {
2804 /* Not the case, give up completely */
2805 Result = FALSE;
2806 goto Quickie;
2807 }
2808
2809 /* There are spaces, so keep trying the next possibility */
2810 ScanString++;
2811 NullBuffer = ScanString;
2812
2813 /* We will have to add a quote, since there is a space */
2814 QuotesNeeded = TRUE;
2815 HasQuotes = TRUE;
2816 goto StartScan;
2817 }
2818 }
2819 else if (!(lpCommandLine) || !(*lpCommandLine))
2820 {
2821 /* We don't have a command line, so just use the application name */
2822 CmdLineIsAppName = TRUE;
2823 lpCommandLine = (LPWSTR)lpApplicationName;
2824 }
2825
2826 /* Convert the application name to its NT path */
2827 TranslationStatus = RtlDosPathNameToRelativeNtPathName_U(lpApplicationName,
2828 &PathName,
2829 NULL,
2830 &SxsWin32RelativePath);
2831 if (!TranslationStatus)
2832 {
2833 /* Path must be invaild somehow, bail out */
2834 DPRINT1("Path translation for SxS failed\n");
2835 SetLastError(ERROR_PATH_NOT_FOUND);
2836 Result = FALSE;
2837 goto Quickie;
2838 }
2839
2840 /* Setup the buffer that needs to be freed at the end */
2841 ASSERT(FreeBuffer == NULL);
2842 FreeBuffer = PathName.Buffer;
2843
2844 /* Check what kind of path the application is, for SxS (Fusion) purposes */
2845 RtlInitUnicodeString(&SxsWin32ExePath, lpApplicationName);
2846 SxsPathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2847 if ((SxsPathType != RtlPathTypeDriveAbsolute) &&
2848 (SxsPathType != RtlPathTypeLocalDevice) &&
2849 (SxsPathType != RtlPathTypeRootLocalDevice) &&
2850 (SxsPathType != RtlPathTypeUncAbsolute))
2851 {
2852 /* Relative-type path, get the full path */
2853 RtlInitEmptyUnicodeString(&PathBufferString, NULL, 0);
2854 Status = RtlGetFullPathName_UstrEx(&SxsWin32ExePath,
2855 NULL,
2856 &PathBufferString,
2857 NULL,
2858 NULL,
2859 NULL,
2860 &SxsPathType,
2861 NULL);
2862 if (!NT_SUCCESS(Status))
2863 {
2864 /* Fail the rest of the create */
2865 RtlReleaseRelativeName(&SxsWin32RelativePath);
2866 BaseSetLastNTError(Status);
2867 Result = FALSE;
2868 goto Quickie;
2869 }
2870
2871 /* Use this full path as the SxS path */
2872 SxsWin32ExePath = PathBufferString;
2873 PathBuffer = PathBufferString.Buffer;
2874 PathBufferString.Buffer = NULL;
2875 DPRINT1("SxS Path: %S\n", PathBuffer);
2876 }
2877
2878 /* Also set the .EXE path based on the path name */
2879 #if _SXS_SUPPORT_ENABLED_
2880 SxsNtExePath = PathName;
2881 #endif
2882 if (SxsWin32RelativePath.RelativeName.Length)
2883 {
2884 /* If it's relative, capture the relative name */
2885 PathName = SxsWin32RelativePath.RelativeName;
2886 }
2887 else
2888 {
2889 /* Otherwise, it's absolute, make sure no relative dir is used */
2890 SxsWin32RelativePath.ContainingDirectory = NULL;
2891 }
2892
2893 /* Now use the path name, and the root path, to try opening the app */
2894 DPRINT1("Path: %wZ. Dir: %p\n", &PathName, SxsWin32RelativePath.ContainingDirectory);
2895 InitializeObjectAttributes(&LocalObjectAttributes,
2896 &PathName,
2897 OBJ_CASE_INSENSITIVE,
2898 SxsWin32RelativePath.ContainingDirectory,
2899 NULL);
2900 Status = NtOpenFile(&FileHandle,
2901 SYNCHRONIZE |
2902 FILE_READ_ATTRIBUTES |
2903 FILE_READ_DATA |
2904 FILE_EXECUTE,
2905 &LocalObjectAttributes,
2906 &IoStatusBlock,
2907 FILE_SHARE_READ | FILE_SHARE_DELETE,
2908 FILE_SYNCHRONOUS_IO_NONALERT |
2909 FILE_NON_DIRECTORY_FILE);
2910 if (!NT_SUCCESS(Status))
2911 {
2912 /* Try to open the app just for execute purposes instead */
2913 Status = NtOpenFile(&FileHandle,
2914 SYNCHRONIZE | FILE_EXECUTE,
2915 &LocalObjectAttributes,
2916 &IoStatusBlock,
2917 FILE_SHARE_READ | FILE_SHARE_DELETE,
2918 FILE_SYNCHRONOUS_IO_NONALERT |
2919 FILE_NON_DIRECTORY_FILE);
2920 }
2921
2922 /* Cleanup in preparation for failure or success */
2923 RtlReleaseRelativeName(&SxsWin32RelativePath);
2924 if (!NT_SUCCESS(Status))
2925 {
2926 /* Failure path, try to understand why */
2927 DPRINT1("Open file failed: %lx\n", Status);
2928 if (RtlIsDosDeviceName_U(lpApplicationName))
2929 {
2930 /* If a device is being executed, return this special error code */
2931 SetLastError(ERROR_BAD_DEVICE);
2932 Result = FALSE;
2933 goto Quickie;
2934 }
2935 else
2936 {
2937 /* Otherwise return the converted NT error code */
2938 BaseSetLastNTError(Status);
2939 Result = FALSE;
2940 goto Quickie;
2941 }
2942 }
2943
2944 /* Did the caller specify a desktop? */
2945 if (!StartupInfo.lpDesktop)
2946 {
2947 /* Use the one from the current process */
2948 StartupInfo.lpDesktop = Peb->ProcessParameters->DesktopInfo.Buffer;
2949 }
2950
2951 /* Create a section for this file */
2952 Status = NtCreateSection(&SectionHandle,
2953 SECTION_ALL_ACCESS,
2954 NULL,
2955 NULL,
2956 PAGE_EXECUTE,
2957 SEC_IMAGE,
2958 FileHandle);
2959 DPRINT1("Section status: %lx\n", Status);
2960 if (NT_SUCCESS(Status))
2961 {
2962 /* Are we running on Windows Embedded, Datacenter, Blade or Starter? */
2963 if (SharedUserData->SuiteMask & (VER_SUITE_EMBEDDEDNT |
2964 VER_SUITE_DATACENTER |
2965 VER_SUITE_PERSONAL |
2966 VER_SUITE_BLADE))
2967 {
2968 /* These SKUs do not allow running certain applications */
2969 Status = BasepCheckWebBladeHashes(FileHandle);
2970 if (Status == STATUS_ACCESS_DENIED)
2971 {
2972 /* And this is one of them! */
2973 DPRINT1("Invalid Blade hashes!\n");
2974 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE);
2975 Result = FALSE;
2976 goto Quickie;
2977 }
2978
2979 /* Did we get some other failure? */
2980 if (!NT_SUCCESS(Status))
2981 {
2982 /* If we couldn't check the hashes, assume nefariousness */
2983 DPRINT1("Tampered Blade hashes!\n");
2984 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER);
2985 Result = FALSE;
2986 goto Quickie;
2987 }
2988 }
2989
2990 /* Now do Winsafer, etc, checks */
2991 Status = BasepIsProcessAllowed((LPWSTR)lpApplicationName);
2992 if (!NT_SUCCESS(Status))
2993 {
2994 /* Fail if we're not allowed to launch the process */
2995 DPRINT1("Process not allowed to launch: %lx\n", Status);
2996 BaseSetLastNTError(Status);
2997 if (SectionHandle)
2998 {
2999 NtClose(SectionHandle);
3000 SectionHandle = NULL;
3001 }
3002 Result = FALSE;
3003 goto Quickie;
3004 }
3005
3006 /* Is a DOS VDM being forced, but we already have a WOW32 instance ready? */
3007 if ((dwCreationFlags & CREATE_FORCEDOS) &&
3008 (BaseStaticServerData->IsWowTaskReady))
3009 {
3010 /* This request can't be satisfied, instead, a separate VDM is needed */
3011 dwCreationFlags &= ~(CREATE_FORCEDOS | CREATE_SHARED_WOW_VDM);
3012 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3013
3014 /* Set a failure code, ask for VDM reservation */
3015 Status = STATUS_INVALID_IMAGE_WIN_16;
3016 UseVdmReserve = TRUE;
3017
3018 /* Close the current handle */
3019 NtClose(SectionHandle);
3020 SectionHandle = NULL;
3021
3022 /* Don't query the section later */
3023 QuerySection = FALSE;
3024 }
3025 }
3026
3027 /* Did we already do these checks? */
3028 if (!SkipSaferAndAppCompat)
3029 {
3030 /* Is everything OK so far, OR do we have an non-MZ, non-DOS app? */
3031 if ((NT_SUCCESS(Status)) ||
3032 ((Status == STATUS_INVALID_IMAGE_NOT_MZ) &&
3033 !(BaseIsDosApplication(&PathName, Status))))
3034 {
3035 /* Clear the machine type in case of failure */
3036 ImageMachine = 0;
3037
3038 /* Clean any app compat data that may have accumulated */
3039 BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
3040 AppCompatData = NULL;
3041 AppCompatSxsData = NULL;
3042
3043 /* Do we have a section? */
3044 if (SectionHandle)
3045 {
3046 /* Have we already queried it? */
3047 if (QuerySection)
3048 {
3049 /* Nothing to do */
3050 Status = STATUS_SUCCESS;
3051 }
3052 else
3053 {
3054 /* Get some information about the executable */
3055 Status = NtQuerySection(SectionHandle,
3056 SectionImageInformation,
3057 &ImageInformation,
3058 sizeof(ImageInformation),
3059 NULL);
3060 }
3061
3062 /* Do we have section information now? */
3063 if (NT_SUCCESS(Status))
3064 {
3065 /* Don't ask for it again, save the machine type */
3066 QuerySection = TRUE;
3067 ImageMachine = ImageInformation.Machine;
3068 }
3069 }
3070
3071 /* Is there a reason/Shim we shouldn't run this application? */
3072 Status = BasepCheckBadapp(FileHandle,
3073 FreeBuffer,
3074 lpEnvironment,
3075 ImageMachine,
3076 &AppCompatData,
3077 &AppCompatDataSize,
3078 &AppCompatSxsData,
3079 &AppCompatSxsDataSize,
3080 &FusionFlags);
3081 if (!NT_SUCCESS(Status))
3082 {
3083 /* This is usually the status we get back */
3084 DPRINT1("App compat launch failure: %lx\n", Status);
3085 if (Status == STATUS_ACCESS_DENIED)
3086 {
3087 /* Convert it to something more Win32-specific */
3088 SetLastError(ERROR_CANCELLED);
3089 }
3090 else
3091 {
3092 /* Some other error */
3093 BaseSetLastNTError(Status);
3094 }
3095
3096 /* Did we have a section? */
3097 if (SectionHandle)
3098 {
3099 /* Clean it up */
3100 NtClose(SectionHandle);
3101 SectionHandle = NULL;
3102 }
3103
3104 /* Fail the call */
3105 Result = FALSE;
3106 goto Quickie;
3107 }
3108 }
3109 }
3110
3111 //ASSERT((dwFusionFlags & ~SXS_APPCOMPACT_FLAG_APP_RUNNING_SAFEMODE) == 0);
3112
3113 /* Have we already done, and do we need to do, SRP (WinSafer) checks? */
3114 if (!(SkipSaferAndAppCompat) &&
3115 ~(dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL))
3116 {
3117 /* Assume yes */
3118 SaferNeeded = TRUE;
3119 switch (Status)
3120 {
3121 case STATUS_INVALID_IMAGE_NE_FORMAT:
3122 case STATUS_INVALID_IMAGE_PROTECT:
3123 case STATUS_INVALID_IMAGE_WIN_16:
3124 case STATUS_FILE_IS_OFFLINE:
3125 /* For all DOS, 16-bit, OS/2 images, we do*/
3126 break;
3127
3128 case STATUS_INVALID_IMAGE_NOT_MZ:
3129 /* For invalid files, we don't, unless it's a .BAT file */
3130 if (BaseIsDosApplication(&PathName, Status)) break;
3131
3132 default:
3133 /* Any other error codes we also don't */
3134 if (!NT_SUCCESS(Status))
3135 {
3136 SaferNeeded = FALSE;
3137 }
3138
3139 /* But for success, we do */
3140 break;
3141 }
3142
3143 /* Okay, so what did the checks above result in? */
3144 if (SaferNeeded)
3145 {
3146 /* We have to call into the WinSafer library and actually check */
3147 Status = BasepCheckWinSaferRestrictions(hUserToken,
3148 (LPWSTR)lpApplicationName,
3149 FileHandle,
3150 &InJob,
3151 &TokenHandle,
3152 &JobHandle);
3153 if (Status == 0xFFFFFFFF)
3154 {
3155 /* Back in 2003, they didn't have an NTSTATUS for this... */
3156 DPRINT1("WinSafer blocking process launch\n");
3157 SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY);
3158 Result = FALSE;
3159 goto Quickie;
3160 }
3161
3162 /* Other status codes are not-Safer related, just convert them */
3163 if (!NT_SUCCESS(Status))
3164 {
3165 DPRINT1("Error checking WinSafer: %lx\n", Status);
3166 BaseSetLastNTError(Status);
3167 Result = FALSE;
3168 goto Quickie;
3169 }
3170 }
3171 }
3172
3173 /* The last step is to figure out why the section object was not created */
3174 switch (Status)
3175 {
3176 case STATUS_INVALID_IMAGE_WIN_16:
3177 {
3178 /* 16-bit binary. Should we use WOW or does the caller force VDM? */
3179 if (!(dwCreationFlags & CREATE_FORCEDOS))
3180 {
3181 /* Remember that we're launching WOW */
3182 IsWowApp = TRUE;
3183
3184 /* Create the VDM environment, it's valid for WOW too */
3185 Result = BaseCreateVDMEnvironment(lpEnvironment,
3186 &VdmAnsiEnv,
3187 &VdmUnicodeEnv);
3188 if (!Result)
3189 {
3190 DPRINT1("VDM environment for WOW app failed\n");
3191 goto Quickie;
3192 }
3193
3194 /* We're going to try this twice, so do a loop */
3195 while (TRUE)
3196 {
3197 /* Pick which kind of WOW mode we want to run in */
3198 VdmBinaryType = (dwCreationFlags &
3199 CREATE_SEPARATE_WOW_VDM) ?
3200 BINARY_TYPE_WOW : BINARY_TYPE_SEPARATE_WOW;
3201
3202 /* Get all the VDM settings and current status */
3203 Status = BaseCheckVDM(VdmBinaryType,
3204 lpApplicationName,
3205 lpCommandLine,
3206 lpCurrentDirectory,
3207 &VdmAnsiEnv,
3208 (PCSR_API_MESSAGE)VdmMsg,
3209 &VdmTask,
3210 dwCreationFlags,
3211 &StartupInfo,
3212 hUserToken);
3213
3214 /* If it worked, no need to try again */
3215 if (NT_SUCCESS(Status)) break;
3216
3217 /* Check if it's disallowed or if it's our second time */
3218 BaseSetLastNTError(Status);
3219 if ((Status == STATUS_VDM_DISALLOWED) ||
3220 (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW) ||
3221 (GetLastError() == ERROR_ACCESS_DENIED))
3222 {
3223 /* Fail the call -- we won't try again */
3224 DPRINT1("VDM message failure for WOW: %lx\n", Status);
3225 Result = FALSE;
3226 goto Quickie;
3227 }
3228
3229 /* Try one more time, but with a separate WOW instance */
3230 dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3231 }
3232
3233 /* Check which VDM state we're currently in */
3234 switch (VdmMsg->VDMState & (VDM_NOT_LOADED |
3235 VDM_NOT_READY |
3236 VDM_READY))
3237 {
3238 case VDM_NOT_LOADED:
3239 /* VDM is not fully loaded, so not that much to undo */
3240 VdmUndoLevel = VDM_UNDO_PARTIAL;
3241
3242 /* Reset VDM reserve if needed */
3243 if (UseVdmReserve) VdmReserve = 1;
3244
3245 /* Get the required parameters and names for launch */
3246 Result = BaseGetVdmConfigInfo(lpCommandLine,
3247 VdmTask,
3248 VdmBinaryType,
3249 &VdmString,
3250 &VdmReserve);
3251 if (!Result)
3252 {
3253 DPRINT1("VDM Configuration failed for WOW\n");
3254 BaseSetLastNTError(Status);
3255 goto Quickie;
3256 }
3257
3258 /* Update the command-line with the VDM one instead */
3259 lpCommandLine = VdmString.Buffer;
3260 lpApplicationName = NULL;
3261
3262 /* We don't want a console, detachment, nor a window */
3263 dwCreationFlags |= CREATE_NO_WINDOW;
3264 dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);
3265
3266 /* Force feedback on */
3267 StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK;
3268 break;
3269
3270
3271 case VDM_READY:
3272 /* VDM is ready, so we have to undo everything */
3273 VdmUndoLevel = VDM_UNDO_REUSE;
3274
3275 /* Check if CSRSS wants us to wait on VDM */
3276 VdmWaitObject = VdmMsg->WaitObjectForParent;
3277 break;
3278
3279 case VDM_NOT_READY:
3280 /* Something is wrong with VDM, we'll fail the call */
3281 DPRINT1("VDM is not ready for WOW\n");
3282 SetLastError(ERROR_NOT_READY);
3283 Result = FALSE;
3284 goto Quickie;
3285
3286 default:
3287 break;
3288 }
3289
3290 /* Since to get NULL, we allocate from 0x1, account for this */
3291 VdmReserve--;
3292
3293 /* This implies VDM is ready, so skip everything else */
3294 if (VdmWaitObject) goto VdmShortCircuit;
3295
3296 /* Don't inherit handles since we're doing VDM now */
3297 bInheritHandles = FALSE;
3298
3299 /* Had the user passed in environment? If so, destroy it */
3300 if ((lpEnvironment) &&
3301 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3302 {
3303 RtlDestroyEnvironment(lpEnvironment);
3304 }
3305
3306 /* We've already done all these checks, don't do them again */
3307 SkipSaferAndAppCompat = TRUE;
3308 goto AppNameRetry;
3309 }
3310
3311 // There is no break here on purpose, so FORCEDOS drops down!
3312 }
3313
3314 case STATUS_INVALID_IMAGE_PROTECT:
3315 case STATUS_INVALID_IMAGE_NOT_MZ:
3316 case STATUS_INVALID_IMAGE_NE_FORMAT:
3317 {
3318 /* We're launching an executable application */
3319 BinarySubType = BINARY_TYPE_EXE;
3320
3321 /* We can drop here from other "cases" above too, so check */
3322 if ((Status == STATUS_INVALID_IMAGE_PROTECT) ||
3323 (Status == STATUS_INVALID_IMAGE_NE_FORMAT) ||
3324 (BinarySubType = BaseIsDosApplication(&PathName, Status)))
3325 {
3326 /* We're launching a DOS application */
3327 VdmBinaryType = BINARY_TYPE_DOS;
3328
3329 /* Based on the caller environment, create a VDM one */
3330 Result = BaseCreateVDMEnvironment(lpEnvironment,
3331 &VdmAnsiEnv,
3332 &VdmUnicodeEnv);
3333 if (!Result)
3334 {
3335 DPRINT1("VDM environment for DOS failed\n");
3336 goto Quickie;
3337 }
3338
3339 /* Check the current state of the VDM subsystem */
3340 Status = BaseCheckVDM(VdmBinaryType | BinarySubType,
3341 lpApplicationName,
3342 lpCommandLine,
3343 lpCurrentDirectory,
3344 &VdmAnsiEnv,
3345 (PCSR_API_MESSAGE)VdmMsg,
3346 &VdmTask,
3347 dwCreationFlags,
3348 &StartupInfo,
3349 NULL);
3350 if (!NT_SUCCESS(Status))
3351 {
3352 /* Failed to inquire about VDM, fail the call */
3353 DPRINT1("VDM message failure for DOS: %lx\n", Status);
3354 BaseSetLastNTError(Status);
3355 Result = FALSE;
3356 goto Quickie;
3357 };
3358
3359 /* Handle possible VDM states */
3360 switch (VdmMsg->VDMState & (VDM_NOT_LOADED |
3361 VDM_NOT_READY |
3362 VDM_READY))
3363 {
3364 case VDM_NOT_LOADED:
3365 /* If VDM is not loaded, we'll do a partial undo */
3366 VdmUndoLevel = VDM_UNDO_PARTIAL;
3367
3368 /* A VDM process can't also be detached, so fail */
3369 if (dwCreationFlags & DETACHED_PROCESS)
3370 {
3371 DPRINT1("Detached process but no VDM, not allowed\n");
3372 SetLastError(ERROR_ACCESS_DENIED);
3373 return FALSE;
3374 }
3375
3376 /* Get the required parameters and names for launch */
3377 Result = BaseGetVdmConfigInfo(lpCommandLine,
3378 VdmTask,
3379 VdmBinaryType,
3380 &VdmString,
3381 &VdmReserve);
3382 if (!Result)
3383 {
3384 DPRINT1("VDM Configuration failed for DOS\n");
3385 BaseSetLastNTError(Status);
3386 goto Quickie;
3387 }
3388
3389 /* Update the command-line to launch VDM instead */
3390 lpCommandLine = VdmString.Buffer;
3391 lpApplicationName = NULL;
3392 break;
3393
3394 case VDM_READY:
3395 /* VDM is ready, so we have to undo everything */
3396 VdmUndoLevel = VDM_UNDO_REUSE;
3397
3398 /* Check if CSRSS wants us to wait on VDM */
3399 VdmWaitObject = VdmMsg->WaitObjectForParent;
3400 break;
3401
3402 case VDM_NOT_READY:
3403 /* Something is wrong with VDM, we'll fail the call */
3404 DPRINT1("VDM is not ready for DOS\n");
3405 SetLastError(ERROR_NOT_READY);
3406 Result = FALSE;
3407 goto Quickie;
3408
3409 default:
3410 break;
3411 }
3412
3413 /* Since to get NULL, we allocate from 0x1, account for this */
3414 VdmReserve--;
3415
3416 /* This implies VDM is ready, so skip everything else */
3417 if (VdmWaitObject) goto VdmShortCircuit;
3418
3419 /* Don't inherit handles since we're doing VDM now */
3420 bInheritHandles = FALSE;
3421
3422 /* Had the user passed in environment? If so, destroy it */
3423 if ((lpEnvironment) &&
3424 !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3425 {
3426 RtlDestroyEnvironment(lpEnvironment);
3427 }
3428
3429 /* Use our VDM Unicode environment instead */
3430 lpEnvironment = VdmUnicodeEnv.Buffer;
3431 }
3432 else
3433 {
3434 /* It's a batch file, get the extension */
3435 ExtBuffer = &PathName.Buffer[PathName.Length / sizeof(WCHAR) - 4];
3436
3437 /* Make sure the extensions are correct */
3438 if ((PathName.Length < (4 * sizeof(WCHAR))) ||
3439 ((_wcsnicmp(ExtBuffer, L".bat", 4)) &&
3440 (_wcsnicmp(ExtBuffer, L".cmd", 4))))
3441 {
3442 DPRINT1("Invalid EXE, and not a batch or script file\n");
3443 SetLastError(ERROR_BAD_EXE_FORMAT);
3444 Result = FALSE;
3445 goto Quickie;
3446 }
3447
3448 /* Check if we need to account for quotes around the path */
3449 CmdQuoteLength = CmdLineIsAppName || HasQuotes;
3450 if (!CmdLineIsAppName)
3451 {
3452 if (HasQuotes) CmdQuoteLength++;
3453 }
3454 else
3455 {
3456 CmdQuoteLength++;
3457 }
3458
3459 /* Calculate the length of the command line */
3460 CmdLineLength = wcslen(lpCommandLine);
3461 CmdLineLength += wcslen(CMD_STRING);
3462 CmdLineLength += CmdQuoteLength + sizeof(ANSI_NULL);
3463 CmdLineLength *= sizeof(WCHAR);
3464
3465 /* Allocate space for the new command line */
3466 AnsiCmdCommand = RtlAllocateHeap(RtlGetProcessHeap(),
3467 0,
3468 CmdLineLength);
3469 if (!AnsiCmdCommand)
3470 {
3471 BaseSetLastNTError(STATUS_NO_MEMORY);
3472 Result = FALSE;
3473 goto Quickie;
3474 }
3475
3476 /* Build it */
3477 wcscpy(AnsiCmdCommand, CMD_STRING);
3478 if ((CmdLineIsAppName) || (HasQuotes))
3479 {
3480 wcscat(AnsiCmdCommand, L"\"");
3481 }
3482 wcscat(AnsiCmdCommand, lpCommandLine);
3483 if ((CmdLineIsAppName) || (HasQuotes))
3484 {
3485 wcscat(AnsiCmdCommand, L"\"");
3486 }
3487
3488 /* Create it as a Unicode String */
3489 RtlInitUnicodeString(&DebuggerString, AnsiCmdCommand);
3490
3491 /* Set the command line to this */
3492 lpCommandLine = DebuggerString.Buffer;
3493 lpApplicationName = NULL;
3494 DPRINT1("Retrying with: %S\n", lpCommandLine);
3495 }
3496
3497 /* We've already done all these checks, don't do them again */
3498 SkipSaferAndAppCompat = TRUE;
3499 goto AppNameRetry;
3500 }
3501
3502 case STATUS_INVALID_IMAGE_WIN_64:
3503 {
3504 /* 64-bit binaries are not allowed to run on 32-bit ReactOS */
3505 DPRINT1("64-bit binary, failing\n");
3506 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3507 Result = FALSE;
3508 goto Quickie;
3509 }
3510
3511 case STATUS_FILE_IS_OFFLINE:
3512 {
3513 /* Set the correct last error for this */
3514 DPRINT1("File is offline, failing\n");
3515 SetLastError(ERROR_FILE_OFFLINE);
3516 break;
3517 }
3518
3519 default:
3520 {
3521 /* Any other error, convert it to a generic Win32 error */
3522 if (!NT_SUCCESS(Status))
3523 {
3524 DPRINT1("Failed to create section: %lx\n", Status);
3525 SetLastError(ERROR_BAD_EXE_FORMAT);
3526 Result = FALSE;
3527 goto Quickie;
3528 }
3529
3530 /* Otherwise, this must be success */
3531 ASSERT(Status == STATUS_SUCCESS);
3532 break;
3533 }
3534 }
3535
3536 /* Is this not a WOW application, but a WOW32 VDM was requested for it? */
3537 if (!(IsWowApp) && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM))
3538 {
3539 /* Ignore the nonsensical request */
3540 dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
3541 }
3542
3543 /* Did we already check information for the section? */
3544 if (!QuerySection)
3545 {
3546 /* Get some information about the executable */
3547 Status = NtQuerySection(SectionHandle,
3548 SectionImageInformation,
3549 &ImageInformation,
3550 sizeof(ImageInformation),
3551 NULL);
3552 if (!NT_SUCCESS(Status))
3553 {
3554 /* We failed, bail out */
3555 DPRINT1("Section query failed\n");
3556 BaseSetLastNTError(Status);
3557 Result = FALSE;
3558 goto Quickie;
3559 }
3560
3561 /* Don't check this later */
3562 QuerySection = TRUE;
3563 }
3564
3565 /* Check if this was linked as a DLL */
3566 if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3567 {
3568 /* These aren't valid images to try to execute! */
3569 DPRINT1("Trying to launch a DLL, failing\n");
3570 SetLastError(ERROR_BAD_EXE_FORMAT);
3571 Result = FALSE;
3572 goto Quickie;
3573 }
3574
3575 /* Don't let callers pass in this flag -- we'll only get it from IFRO */
3576 Flags &= ~PROCESS_CREATE_FLAGS_LARGE_PAGES;
3577
3578 /* Clear the IFEO-missing flag, before we know for sure... */
3579 ParameterFlags &= ~2;
3580
3581 /* If the process is being debugged, only read IFEO if the PEB says so */
3582 if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
3583 (NtCurrentPeb()->ReadImageFileExecOptions))
3584 {
3585 /* Let's do this! Attempt to open IFEO */
3586 Status1 = LdrOpenImageFileOptionsKey(&PathName, 0, &KeyHandle);
3587 if (!NT_SUCCESS(Status1))
3588 {
3589 /* We failed, set the flag so we store this in the parameters */
3590 if (Status1 == STATUS_OBJECT_NAME_NOT_FOUND) ParameterFlags |= 2;
3591 }
3592 else
3593 {
3594 /* Was this our first time going through this path? */
3595 if (!DebuggerCmdLine)
3596 {
3597 /* Allocate a buffer for the debugger path */
3598 DebuggerCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
3599 0,
3600 MAX_PATH * sizeof(WCHAR));
3601 if (!DebuggerCmdLine)
3602 {
3603 /* Close IFEO on failure */
3604 Status1 = NtClose(KeyHandle);
3605 ASSERT(NT_SUCCESS(Status1));
3606
3607 /* Fail the call */
3608 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3609 Result = FALSE;
3610 goto Quickie;
3611 }
3612 }
3613
3614 /* Now query for the debugger */
3615 Status1 = LdrQueryImageFileKeyOption(KeyHandle,
3616 L"Debugger",
3617 REG_SZ,
3618 DebuggerCmdLine,
3619 MAX_PATH * sizeof(WCHAR),
3620 &ResultSize);
3621 if (!(NT_SUCCESS(Status1)) ||
3622 (ResultSize < sizeof(WCHAR)) ||
3623 (DebuggerCmdLine[0] == UNICODE_NULL))
3624 {
3625 /* If it's not there, or too small, or invalid, ignore it */
3626 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3627 DebuggerCmdLine = NULL;
3628 }
3629
3630 /* Also query if we should map with large pages */
3631 Status1 = LdrQueryImageFileKeyOption(KeyHandle,
3632 L"UseLargePages",
3633 REG_DWORD,
3634 &UseLargePages,
3635 sizeof(UseLargePages),
3636 NULL);
3637 if ((NT_SUCCESS(Status1)) && (UseLargePages))
3638 {
3639 /* Do it! This is the only way this flag can be set */
3640 Flags |= PROCESS_CREATE_FLAGS_LARGE_PAGES;
3641 }
3642
3643 /* We're done with IFEO, can close it now */
3644 Status1 = NtClose(KeyHandle);
3645 ASSERT(NT_SUCCESS(Status1));
3646 }
3647 }
3648
3649 /* Make sure the image was compiled for this processor */
3650 if ((ImageInformation.Machine < SharedUserData->ImageNumberLow) ||
3651 (ImageInformation.Machine > SharedUserData->ImageNumberHigh))
3652 {
3653 /* It was not -- raise a hard error */
3654 ErrorResponse = ResponseOk;
3655 ErrorParameters[0] = (ULONG_PTR)&PathName;
3656 NtRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
3657 1,
3658 1,
3659 ErrorParameters,
3660 OptionOk,
3661 &ErrorResponse);
3662 if (Peb->ImageSubsystemMajorVersion <= 3)
3663 {
3664 /* If it's really old, return this error */
3665 SetLastError(ERROR_BAD_EXE_FORMAT);
3666 }
3667 else
3668 {
3669 /* Otherwise, return a more modern error */
3670 SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3671 }
3672
3673 /* Go to the failure path */
3674 DPRINT1("Invalid image architecture: %lx\n", ImageInformation.Machine);
3675 Result = FALSE;
3676 goto Quickie;
3677 }
3678
3679 /* Check if this isn't a Windows image */
3680 if ((ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) &&
3681 (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI))
3682 {
3683 /* Get rid of section-related information since we'll retry */
3684 NtClose(SectionHandle);
3685 SectionHandle = NULL;
3686 QuerySection = FALSE;
3687
3688 /* The only other non-Windows image type we support here is POSIX */
3689 if (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_POSIX_CUI)
3690 {
3691 /* Bail out if it's something else */
3692 SetLastError(ERROR_CHILD_NOT_COMPLETE);
3693 Result = FALSE;
3694 goto Quickie;
3695 }
3696
3697 /* Now build the command-line to have posix launch this image */
3698 Result = BuildSubSysCommandLine(L"POSIX /P ",
3699 lpApplicationName,
3700 lpCommandLine,
3701 &DebuggerString);
3702 if (!Result)
3703 {
3704 /* Bail out if that failed */
3705 DPRINT1("Subsystem command line failed\n");
3706 goto Quickie;
3707 }
3708
3709 /* And re-try launching the process, with the new command-line now */
3710 lpCommandLine = DebuggerString.Buffer;
3711 lpApplicationName = NULL;
3712
3713 /* We've already done all these checks, don't do them again */
3714 SkipSaferAndAppCompat = TRUE;
3715 DPRINT1("Retrying with: %S\n", lpCommandLine);
3716 goto AppNameRetry;
3717 }
3718
3719 /* Was this image built for a version of Windows whose images we can run? */
3720 Result = BasepIsImageVersionOk(ImageInformation.SubSystemMajorVersion,
3721 ImageInformation.SubSystemMinorVersion);
3722 if (!Result)
3723 {
3724 /* It was not, bail out */
3725 DPRINT1("Invalid subsystem version: %hu.%hu\n",
3726 ImageInformation.SubSystemMajorVersion,
3727 ImageInformation.SubSystemMinorVersion);
3728 SetLastError(ERROR_BAD_EXE_FORMAT);
3729 goto Quickie;
3730 }
3731
3732 /* Check if there is a debugger associated with the application */
3733 if (DebuggerCmdLine)
3734 {
3735 /* Get the length of the command line */
3736 n = wcslen(lpCommandLine);
3737 if (!n)
3738 {
3739 /* There's no command line, use the application name instead */
3740 lpCommandLine = (LPWSTR)lpApplicationName;
3741 n = wcslen(lpCommandLine);
3742 }
3743
3744 /* Protect against overflow */
3745 if (n > UNICODE_STRING_MAX_CHARS)
3746 {
3747 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3748 Result = FALSE;
3749 goto Quickie;
3750 }
3751
3752 /* Now add the length of the debugger command-line */
3753 n += wcslen(DebuggerCmdLine);
3754
3755 /* Again make sure we don't overflow */
3756 if (n > UNICODE_STRING_MAX_CHARS)
3757 {
3758 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3759 Result = FALSE;
3760 goto Quickie;
3761 }
3762
3763 /* Account for the quotes and space between the two */
3764 n += ((sizeof('""') * 2) + sizeof(' '));
3765
3766 /* Convert to bytes, and make sure we don't overflow */
3767 n *= sizeof(WCHAR);
3768 if (n > UNICODE_STRING_MAX_BYTES)
3769 {
3770 BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3771 Result = FALSE;
3772 goto Quickie;
3773 }
3774
3775 /* Allocate space for the string */
3776 DebuggerString.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, n);
3777 if (!DebuggerString.Buffer)
3778 {
3779 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3780 Result = FALSE;
3781 goto Quickie;
3782 }
3783
3784 /* Set the length */
3785 RtlInitEmptyUnicodeString(&DebuggerString,
3786 DebuggerString.Buffer,
3787 n);
3788
3789 /* Now perform the command line creation */
3790 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString,
3791 DebuggerCmdLine);
3792 ASSERT(NT_SUCCESS(ImageDbgStatus));
3793 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, L" ");
3794 ASSERT(NT_SUCCESS(ImageDbgStatus));
3795 ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, lpCommandLine);
3796 ASSERT(NT_SUCCESS(ImageDbgStatus));
3797
3798 /* Make sure it all looks nice */
3799 DbgPrint("BASE: Calling debugger with '%wZ'\n", &DebuggerString);
3800
3801 /* Update the command line and application name */
3802 lpCommandLine = DebuggerString.Buffer;
3803 lpApplicationName = NULL;
3804
3805 /* Close all temporary state */
3806 NtClose(SectionHandle);
3807 SectionHandle = NULL;
3808 QuerySection = FALSE;
3809
3810 /* Free all temporary memory */
3811 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
3812 NameBuffer = NULL;
3813 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
3814 FreeBuffer = NULL;
3815 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3816 DebuggerCmdLine = NULL;
3817 DPRINT1("Retrying with: %S\n", lpCommandLine);
3818 goto AppNameRetry;
3819 }
3820
3821 /* Initialize the process object attributes */
3822 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3823 lpProcessAttributes,
3824 NULL);
3825 if ((hUserToken) && (lpProcessAttributes))
3826 {
3827 /* Auggment them with information from the user */
3828
3829 LocalProcessAttributes = *lpProcessAttributes;
3830 LocalProcessAttributes.lpSecurityDescriptor = NULL;
3831 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3832 &LocalProcessAttributes,
3833 NULL);
3834 }
3835
3836 /* Check if we're going to be debugged */
3837 if (dwCreationFlags & DEBUG_PROCESS)
3838 {
3839 /* Set process flag */
3840 Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
3841 }
3842
3843 /* Check if we're going to be debugged */
3844 if (dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
3845 {
3846 /* Connect to DbgUi */
3847 Status = DbgUiConnectToDbg();
3848 if (!NT_SUCCESS(Status))
3849 {
3850 DPRINT1("Failed to connect to DbgUI!\n");
3851 BaseSetLastNTError(Status);
3852 Result = FALSE;
3853 goto Quickie;
3854 }
3855
3856 /* Get the debug object */
3857 DebugHandle = DbgUiGetThreadDebugObject();
3858
3859 /* Check if only this process will be debugged */
3860 if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS)
3861 {
3862 /* Set process flag */
3863 Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
3864 }
3865 }
3866
3867 /* Set inherit flag */
3868 if (bInheritHandles) Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
3869
3870 /* Check if the process should be created with large pages */
3871 HavePrivilege = FALSE;
3872 PrivilegeState = NULL;
3873 if (Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES)
3874 {
3875 /* Acquire the required privilege so that the kernel won't fail the call */
3876 PrivilegeValue = SE_LOCK_MEMORY_PRIVILEGE;
3877 Status = RtlAcquirePrivilege(&PrivilegeValue, TRUE, FALSE, &PrivilegeState);
3878 if (NT_SUCCESS(Status))
3879 {
3880 /* Remember to release it later */
3881 HavePrivilege = TRUE;
3882 }
3883 }
3884
3885 /* Save the current TIB value since kernel overwrites it to store PEB */
3886 TibValue = Teb->NtTib.ArbitraryUserPointer;
3887
3888 /* Tell the kernel to create the process */
3889 Status = NtCreateProcessEx(&ProcessHandle,
3890 PROCESS_ALL_ACCESS,
3891 ObjectAttributes,
3892 NtCurrentProcess(),
3893 Flags,
3894 SectionHandle,
3895 DebugHandle,
3896 NULL,
3897 InJob);
3898
3899 /* Load the PEB address from the hacky location where the kernel stores it */
3900 RemotePeb = Teb->NtTib.ArbitraryUserPointer;
3901
3902 /* And restore the old TIB value */
3903 Teb->NtTib.ArbitraryUserPointer = TibValue;
3904
3905 /* Release the large page privilege if we had acquired it */
3906 if (HavePrivilege) RtlReleasePrivilege(PrivilegeState);
3907
3908 /* And now check if the kernel failed to create the process */
3909 if (!NT_SUCCESS(Status))
3910 {
3911 /* Go to failure path */
3912 DPRINT1("Failed to create process: %lx\n", Status);
3913 BaseSetLastNTError(Status);
3914 Result = FALSE;
3915 goto Quickie;
3916 }
3917
3918 /* Check if there is a priority class to set */
3919 if (PriorityClass.PriorityClass)
3920 {
3921 /* Reset current privilege state */
3922 RealTimePrivilegeState = NULL;
3923
3924 /* Is realtime priority being requested? */
3925 if (PriorityClass.PriorityClass == REALTIME_PRIORITY_CLASS)
3926 {
3927 /* Check if the caller has real-time access, and enable it if so */
3928 RealTimePrivilegeState = BasepIsRealtimeAllowed(TRUE);
3929 }
3930
3931 /* Set the new priority class and release the privilege */
3932 Status = NtSetInformationProcess(ProcessHandle,
3933 ProcessPriorityClass,
3934 &PriorityClass,
3935 sizeof(PROCESS_PRIORITY_CLASS));
3936 if (RealTimePrivilegeState) RtlReleasePrivilege(RealTimePrivilegeState);
3937
3938 /* Check if we failed to set the priority class */
3939 if (!NT_SUCCESS(Status))
3940 {
3941 /* Bail out on failure */
3942 DPRINT1("Failed to set priority class: %lx\n", Status);
3943 BaseSetLastNTError(Status);
3944 Result = FALSE;
3945 goto Quickie;
3946 }
3947 }
3948
3949 /* Check if the caller wants the default error mode */
3950 if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE)
3951 {
3952 /* Set Error Mode to only fail on critical errors */
3953 HardErrorMode = SEM_FAILCRITICALERRORS;
3954 NtSetInformationProcess(ProcessHandle,
3955 ProcessDefaultHardErrorMode,
3956 &HardErrorMode,
3957 sizeof(ULONG));
3958 }
3959
3960 /* Check if this was a VDM binary */
3961 if (VdmBinaryType)
3962 {
3963 /* Update VDM by telling it the process has now been created */
3964 VdmWaitObject = ProcessHandle;
3965 Result = BaseUpdateVDMEntry(VdmEntryUpdateProcess,
3966 &VdmWaitObject,
3967 VdmTask,
3968 VdmBinaryType);
3969 {
3970 /* Bail out on failure */
3971 DPRINT1("Failed to update VDM with wait object\n");
3972 VdmWaitObject = NULL;
3973 goto Quickie;
3974 }
3975
3976 /* At this point, a failure means VDM has to undo all the state */
3977 VdmUndoLevel |= VDM_UNDO_FULL;
3978 }
3979
3980 /* Check if VDM needed reserved low-memory */
3981 if (VdmReserve)
3982 {
3983 /* Reserve the requested allocation */
3984 Status = NtAllocateVirtualMemory(ProcessHandle,
3985 &BaseAddress,
3986 0,
3987 &VdmReserve,
3988 MEM_RESERVE,
3989 PAGE_EXECUTE_READWRITE);
3990 if (!NT_SUCCESS(Status))
3991 {
3992 /* Bail out on failure */
3993 DPRINT1("Failed to reserved memory for VDM: %lx\n", Status);
3994 BaseSetLastNTError(Status);
3995 Result = FALSE;
3996 goto Quickie;
3997 }
3998 }
3999
4000 /* Check if we've already queried information on the section */
4001 if (!QuerySection)
4002 {
4003 /* We haven't, so get some information about the executable */
4004 Status = NtQuerySection(SectionHandle,
4005 SectionImageInformation,
4006 &ImageInformation,
4007 sizeof(ImageInformation),
4008 NULL);
4009 if (!NT_SUCCESS(Status))
4010 {
4011 /* Bail out on failure */
4012 DPRINT1("Failed to query section: %lx\n", Status);
4013 BaseSetLastNTError(Status);
4014 Result = FALSE;
4015 goto Quickie;
4016 }
4017
4018 /* If we encounter a restart, don't re-query this information again */
4019 QuerySection = TRUE;
4020 }
4021
4022 /* Do we need to apply SxS to this image? */
4023 if (!(ImageInformation.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_ISOLATION))
4024 {
4025 /* Too bad, we don't support this yet */
4026 DPRINT1("Image should receive SxS Fusion Isolation\n");
4027 }
4028
4029 /* There's some SxS flag that we need to set if fusion flags have 1 set */
4030 if (FusionFlags & 1) CreateProcessMsg->Sxs.Flags |= 0x10;
4031
4032 /* Check if we have a current directory */
4033 if (lpCurrentDirectory)
4034 {
4035 /* Allocate a buffer so we can keep a Unicode copy */
4036 DPRINT1("Current directory: %S\n", lpCurrentDirectory);
4037 CurrentDirectory = RtlAllocateHeap(RtlGetProcessHeap(),
4038 0,
4039 (MAX_PATH * sizeof(WCHAR)) +
4040 sizeof(UNICODE_NULL));
4041 if (!CurrentDirectory)
4042 {
4043 /* Bail out if this failed */
4044 BaseSetLastNTError(STATUS_NO_MEMORY);
4045 Result = FALSE;
4046 goto Quickie;
4047 }
4048
4049 /* Get the length in Unicode */
4050 Length = GetFullPathNameW(lpCurrentDirectory,
4051 MAX_PATH,
4052 CurrentDirectory,
4053 &FilePart);
4054 if (Length > MAX_PATH)
4055 {
4056 /* The directory is too long, so bail out */
4057 SetLastError(ERROR_DIRECTORY);
4058 Result = FALSE;
4059 goto Quickie;
4060 }
4061
4062 /* Make sure the directory is actually valid */
4063 CurdirLength = GetFileAttributesW(CurrentDirectory);
4064 if ((CurdirLength == 0xffffffff) ||
4065 !(CurdirLength & FILE_ATTRIBUTE_DIRECTORY))
4066 {
4067 /* It isn't, so bail out */
4068 DPRINT1("Current directory is invalid\n");
4069 SetLastError(ERROR_DIRECTORY);
4070 Result = FALSE;
4071 goto Quickie;
4072 }
4073 }
4074
4075 /* Insert quotes if needed */
4076 if ((QuotesNeeded) || (CmdLineIsAppName))
4077 {
4078 /* Allocate our buffer, plus enough space for quotes and a NULL */
4079 QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
4080 0,
4081 (wcslen(lpCommandLine) * sizeof(WCHAR)) +
4082 (2 * sizeof(L'\"') + sizeof(UNICODE_NULL)));
4083 if (QuotedCmdLine)
4084 {
4085 /* Copy the first quote */
4086 wcscpy(QuotedCmdLine, L"\"");
4087
4088 /* Save the current null-character */
4089 if (QuotesNeeded)
4090 {
4091 SaveChar = *NullBuffer;
4092 *NullBuffer = UNICODE_NULL;
4093 }
4094
4095 /* Copy the command line and the final quote */
4096 wcscat(QuotedCmdLine, lpCommandLine);
4097 wcscat(QuotedCmdLine, L"\"");
4098
4099 /* Copy the null-char back */
4100 if (QuotesNeeded)
4101 {
4102 *NullBuffer = SaveChar;
4103 wcscat(QuotedCmdLine, NullBuffer);
4104 }
4105 }
4106 else
4107 {
4108 /* We can't put quotes around the thing, so try it anyway */
4109 if (QuotesNeeded) QuotesNeeded = FALSE;
4110 if (CmdLineIsAppName) CmdLineIsAppName = FALSE;
4111 }
4112 }
4113
4114 /* Use isolation if needed */
4115 if (CreateProcessMsg->Sxs.Flags & 1) ParameterFlags |= 1;
4116
4117 /* Set the new command-line if needed */
4118 if ((QuotesNeeded) || (CmdLineIsAppName)) lpCommandLine = QuotedCmdLine;
4119
4120 /* Call the helper function in charge of RTL_USER_PROCESS_PARAMETERS */
4121 Result = BasePushProcessParameters(ParameterFlags,
4122 ProcessHandle,
4123 RemotePeb,
4124 lpApplicationName,
4125 CurrentDirectory,
4126 lpCommandLine,
4127 lpEnvironment,
4128 &StartupInfo,
4129 dwCreationFlags | NoWindow,
4130 bInheritHandles,
4131 IsWowApp ? IMAGE_SUBSYSTEM_WINDOWS_GUI: 0,
4132 AppCompatData,
4133 AppCompatDataSize);
4134 if (!Result)
4135 {
4136 /* The remote process would have an undefined state, so fail the call */
4137 DPRINT1("BasePushProcessParameters failed\n");
4138 goto Quickie;
4139 }
4140
4141 /* Free the VDM command line string as it's no longer needed */
4142 RtlFreeUnicodeString(&VdmString);
4143 VdmString.Buffer = NULL;
4144
4145 /* Non-VDM console applications usually inherit handles unless specified */
4146 if (!(VdmBinaryType) &&
4147 !(bInheritHandles) &&
4148 !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
4149 !(dwCreationFlags & (CREATE_NO_WINDOW |
4150 CREATE_NEW_CONSOLE |
4151 DETACHED_PROCESS)) &&
4152 (ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI))
4153 {
4154 /* Get the remote parameters */
4155 Status = NtReadVirtualMemory(ProcessHandle,
4156 &RemotePeb->ProcessParameters,
4157 &ProcessParameters,
4158 sizeof(PRTL_USER_PROCESS_PARAMETERS),
4159 NULL);
4160 if (NT_SUCCESS(Status))
4161 {
4162 /* Duplicate standard input unless it's a console handle */
4163 if (!IsConsoleHandle(Peb->ProcessParameters->StandardInput))
4164 {
4165 StuffStdHandle(ProcessHandle,
4166 Peb->ProcessParameters->StandardInput,
4167 &ProcessParameters->StandardInput);
4168 }
4169
4170 /* Duplicate standard output unless it's a console handle */
4171 if (!IsConsoleHandle(Peb->ProcessParameters->StandardOutput))
4172 {
4173 StuffStdHandle(ProcessHandle,
4174 Peb->ProcessParameters->StandardOutput,
4175 &ProcessParameters->StandardOutput);
4176 }
4177
4178 /* Duplicate standard error unless it's a console handle */
4179 if (!IsConsoleHandle(Peb->ProcessParameters->StandardError))
4180 {
4181 StuffStdHandle(ProcessHandle,
4182 Peb->ProcessParameters->StandardError,
4183 &ProcessParameters->StandardError);
4184 }
4185 }
4186 }
4187
4188 /* Create the Thread's Stack */
4189 StackSize = max(256 * 1024, ImageInformation.MaximumStackSize);
4190 Status = BaseCreateStack(ProcessHandle,
4191 ImageInformation.CommittedStackSize,
4192 StackSize,
4193 &InitialTeb);
4194 if (!NT_SUCCESS(Status))
4195 {
4196 DPRINT1("Creating the thread stack failed: %lx\n", Status);
4197 BaseSetLastNTError(Status);
4198 Result = FALSE;
4199 goto Quickie;
4200 }
4201
4202 /* Create the Thread's Context */
4203 BaseInitializeContext(&Context,
4204 Peb,
4205 ImageInformation.TransferAddress,
4206 InitialTeb.StackBase,
4207 0);
4208
4209 /* Convert the thread attributes */
4210 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
4211 lpThreadAttributes,
4212 NULL);
4213 if ((hUserToken) && (lpThreadAttributes))
4214 {
4215 /* If the caller specified a user token, zero the security descriptor */
4216 LocalThreadAttributes = *lpThreadAttributes;
4217 LocalThreadAttributes.lpSecurityDescriptor = NULL;
4218 ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
4219 &LocalThreadAttributes,
4220 NULL);
4221 }
4222
4223 /* Create the Kernel Thread Object */
4224 Status = NtCreateThread(&ThreadHandle,
4225 THREAD_ALL_ACCESS,
4226 ObjectAttributes,
4227 ProcessHandle,
4228 &ClientId,
4229 &Context,
4230 &InitialTeb,
4231 TRUE);
4232 if (!NT_SUCCESS(Status))
4233 {
4234 /* A process is not allowed to exist without a main thread, so fail */
4235 DPRINT1("Creating the main thread failed: %lx\n", Status);
4236 BaseSetLastNTError(Status);
4237 Result = FALSE;
4238 goto Quickie;
4239 }
4240
4241 /* Begin filling out the CSRSS message, first with our IDs and handles */
4242 CreateProcessMsg->ProcessHandle = ProcessHandle;
4243 CreateProcessMsg->ThreadHandle = ThreadHandle;
4244 CreateProcessMsg->ClientId = ClientId;
4245
4246 /* Write the remote PEB address and clear it locally, we no longer use it */
4247 CreateProcessMsg->PebAddressNative = RemotePeb;
4248 CreateProcessMsg->PebAddressWow64 = (ULONG)RemotePeb;
4249 RemotePeb = NULL;
4250
4251 /* Now check what kind of architecture this image was made for */
4252 switch (ImageInformation.Machine)
4253 {
4254 /* IA32, IA64 and AMD64 are supported in Server 2003 */
4255 case IMAGE_FILE_MACHINE_I386:
4256 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
4257 break;
4258 case IMAGE_FILE_MACHINE_IA64:
4259 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64;
4260 break;
4261 case IMAGE_FILE_MACHINE_AMD64:
4262 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
4263 break;
4264
4265 /* Anything else results in image unknown -- but no failure */
4266 default:
4267 DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n",
4268 ImageInformation.Machine);
4269 CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
4270 break;
4271 }
4272
4273 /* Write the input creation flags except any debugger-related flags */
4274 CreateProcessMsg->CreationFlags = dwCreationFlags &
4275 ~(DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS);
4276
4277 /* CSRSS needs to know if this is a GUI app or not */
4278 if ((ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI) ||
4279 (IsWowApp))
4280 {
4281 /*
4282 * For GUI apps we turn on the 2nd bit. This allow CSRSS server dlls
4283 * (basesrv in particular) to know whether or not this is a GUI or a
4284 * TUI application.
4285 */
4286 AddToHandle(CreateProcessMsg->ProcessHandle, 2);
4287
4288 /* Also check if the parent is also a GUI process */
4289 NtHeaders = RtlImageNtHeader(GetModuleHandle(NULL));
4290 if ((NtHeaders) &&
4291 (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI))
4292 {
4293 /* Let it know that it should display the hourglass mouse cursor */
4294 AddToHandle(CreateProcessMsg->ProcessHandle, 1);
4295 }
4296 }
4297
4298 /* For all apps, if this flag is on, the hourglass mouse cursor is shown */
4299 if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK)
4300 {
4301 AddToHandle(CreateProcessMsg->ProcessHandle, 1);
4302 }
4303
4304 /* Likewise, the opposite holds as well */
4305 if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK)
4306 {
4307 RemoveFromHandle(CreateProcessMsg->ProcessHandle, 1);
4308 }
4309
4310 /* Also store which kind of VDM app (if any) this is */
4311 CreateProcessMsg->VdmBinaryType = VdmBinaryType;
4312
4313 /* And if it really is a VDM app... */
4314 if (VdmBinaryType)
4315 {
4316 /* Store the task ID and VDM console handle */
4317 CreateProcessMsg->hVDM = VdmTask ? 0 : Peb->ProcessParameters->ConsoleHandle;
4318 CreateProcessMsg->VdmTask = VdmTask;
4319 }
4320 else if (VdmReserve)
4321 {
4322 /* Extended VDM, set a flag */
4323 CreateProcessMsg->VdmBinaryType |= BINARY_TYPE_WOW_EX;
4324 }
4325
4326 /* Check if there's side-by-side assembly data associated with the process */
4327 if (CreateProcessMsg->Sxs.Flags)
4328 {
4329 /* This should not happen in ReactOS yet */
4330 DPRINT1("This is an SxS Message -- should not happen yet\n");
4331 BaseSetLastNTError(STATUS_NOT_IMPLEMENTED);
4332 NtTerminateProcess(ProcessHandle, STATUS_NOT_IMPLEMENTED);
4333 Result = FALSE;
4334 goto Quickie;
4335 }
4336
4337 /* We are finally ready to call CSRSS to tell it about our new process! */
4338 CsrClientCallServer((PCSR_API_MESSAGE)&CsrMsg,
4339 CaptureBuffer,
4340 CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
4341 BasepCreateProcess),
4342 sizeof(*CreateProcessMsg));
4343
4344 /* CSRSS has returned, free the capture buffer now if we had one */
4345 if (CaptureBuffer)
4346 {
4347 CsrFreeCaptureBuffer(CaptureBuffer);
4348 CaptureBuffer = NULL;
4349 }
4350
4351 /* Check if CSRSS failed to accept ownership of the new Windows process */
4352 if (!NT_SUCCESS(CsrMsg.Status))
4353 {
4354 /* Terminate the process and enter failure path with the CSRSS status */
4355 DPRINT1("Failed to tell csrss about new process\n");
4356 BaseSetLastNTError(CsrMsg.Status);
4357 NtTerminateProcess(ProcessHandle, CsrMsg.Status);
4358 Result = FALSE;
4359 goto Quickie;
4360 }
4361
4362 /* Check if we have a token due to Authz/Safer, not passed by the user */
4363 if ((TokenHandle) && !(hUserToken))
4364 {
4365 /* Replace the process and/or thread token with the one from Safer */
4366 Status = BasepReplaceProcessThreadTokens(TokenHandle,
4367 ProcessHandle,
4368 ThreadHandle);
4369 if (!NT_SUCCESS(Status))
4370 {
4371 /* If this failed, kill the process and enter the failure path */
4372 DPRINT1("Failed to update process token: %lx\n", Status);
4373 NtTerminateProcess(ProcessHandle, Status);
4374 BaseSetLastNTError(Status);
4375 Result = FALSE;
4376 goto Quickie;
4377 }
4378 }
4379
4380 /* Check if a job was associated with this process */
4381 if (JobHandle)
4382 {
4383 /* Bind the process and job together now */
4384 Status = NtAssignProcessToJobObject(JobHandle, ProcessHandle);
4385 if (!NT_SUCCESS(Status))
4386 {
4387 /* Kill the process and enter the failure path if binding failed */
4388 DPRINT1("Failed to assign process to job: %lx\n", Status);
4389 NtTerminateProcess(ProcessHandle, STATUS_ACCESS_DENIED);
4390 BaseSetLastNTError(Status);
4391 Result = FALSE;
4392 goto Quickie;
4393 }
4394 }
4395
4396 /* Finally, resume the thread to actually get the process started */
4397 if (!(dwCreationFlags & CREATE_SUSPENDED))
4398 {
4399 NtResumeThread(ThreadHandle, &ResumeCount);
4400 }
4401
4402 VdmShortCircuit:
4403 /* We made it this far, meaning we have a fully created process and thread */
4404 Result = TRUE;
4405
4406 /* Anyone doing a VDM undo should now undo everything, since we are done */
4407 if (VdmUndoLevel) VdmUndoLevel |= VDM_UNDO_COMPLETED;
4408
4409 /* Having a VDM wait object implies this must be a VDM process */
4410 if (VdmWaitObject)
4411 {
4412 /* Check if it's a 16-bit separate WOW process */
4413 if (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW)
4414 {
4415 /* OR-in the special flag to indicate this, and return to caller */
4416 AddToHandle(VdmWaitObject, 2);
4417 lpProcessInformation->hProcess = VdmWaitObject;
4418
4419 /* Check if this was a re-used VDM */
4420 if (VdmUndoLevel & VDM_UNDO_REUSE)
4421 {
4422 /* No Client ID should be returned in this case */
4423 ClientId.UniqueProcess = 0;
4424 ClientId.UniqueThread = 0;
4425 }
4426 }
4427 else
4428 {
4429 /* OR-in the special flag to indicate this is not a separate VDM */
4430 AddToHandle(VdmWaitObject, 1);
4431
4432 /* Return handle to the caller */
4433 lpProcessInformation->hProcess = VdmWaitObject;
4434 }
4435
4436 /* Close the original process handle, since it's not needed for VDM */
4437 if (ProcessHandle) NtClose(ProcessHandle);
4438 }
4439 else
4440 {
4441 /* This is a regular process, so return the real process handle */
4442 lpProcessInformation->hProcess = ProcessHandle;
4443 }
4444
4445 /* Return the rest of the process information based on what we have so far */
4446 lpProcessInformation->hThread = ThreadHandle;
4447 lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess);
4448 lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread);
4449
4450 /* NULL these out here so we know to treat this as a success scenario */
4451 ProcessHandle = NULL;
4452 ThreadHandle = NULL;
4453
4454 Quickie:
4455 /* Free the debugger command line if one was allocated */
4456 if (DebuggerCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
4457
4458 /* Check if an SxS full path as queried */
4459 if (PathBuffer)
4460 {
4461 /* Reinitialize the executable path */
4462 RtlInitEmptyUnicodeString(&SxsWin32ExePath, NULL, 0);
4463 SxsWin32ExePath.Length = 0;
4464
4465 /* Free the path buffer */
4466 RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
4467 }
4468
4469 #if _SXS_SUPPORT_ENABLED_
4470 /* Check if this was a non-VDM process */
4471 if (!VdmBinaryType)
4472 {
4473 /* Then it must've had SxS data, so close the handles used for it */
4474 BasepSxsCloseHandles(&Handles);
4475 BasepSxsCloseHandles(&FileHandles);
4476
4477 /* Check if we built SxS byte buffers for this create process request */
4478 if (SxsConglomeratedBuffer)
4479 {
4480 /* Loop all of them */
4481 for (i = 0; i < 5; i++)
4482 {
4483 /* Check if this one was allocated */
4484 ThisBuffer = SxsStaticBuffers[i];
4485 if (ThisBuffer)
4486 {
4487 /* Get the underlying RTL_BUFFER structure */
4488 ByteBuffer = &ThisBuffer->ByteBuffer;
4489 if ((ThisBuffer != (PVOID)-8) && (ByteBuffer->Buffer))
4490 {
4491 /* Check if it was dynamic */
4492 if (ByteBuffer->Buffer != ByteBuffer->StaticBuffer)
4493 {
4494 /* Free it from the heap */
4495 FreeString.Buffer = (PWCHAR)ByteBuffer->Buffer;
4496 RtlFreeUnicodeString(&FreeString);
4497 }
4498
4499 /* Reset the buffer to its static data */
4500 ByteBuffer->Buffer = ByteBuffer->StaticBuffer;
4501 ByteBuffer->Size = ByteBuffer->StaticSize;
4502 }
4503
4504 /* Reset the string to the static buffer */
4505 RtlInitEmptyUnicodeString(&ThisBuffer->String,
4506 (PWCHAR)ByteBuffer->StaticBuffer,
4507 ByteBuffer->StaticSize);
4508 if (ThisBuffer->String.Buffer)
4509 {
4510 /* Also NULL-terminate it */
4511 *ThisBuffer->String.Buffer = UNICODE_NULL;
4512 }
4513 }
4514 }
4515 }
4516 }
4517 #endif
4518 /* Check if an environment was passed in */
4519 if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
4520 {
4521 /* Destroy it */
4522 RtlDestroyEnvironment(lpEnvironment);
4523
4524 /* If this was the VDM environment too, clear that as well */
4525 if (VdmUnicodeEnv.Buffer == lpEnvironment) VdmUnicodeEnv.Buffer = NULL;
4526 lpEnvironment = NULL;
4527 }
4528
4529 /* Unconditionally free all the name parsing buffers we always allocate */
4530 RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine);
4531 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
4532 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory);
4533 RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
4534
4535 /* Close open file/section handles */
4536 if (FileHandle) NtClose(FileHandle);
4537 if (SectionHandle) NtClose(SectionHandle);
4538
4539 /* If we have a thread handle, this was a failure path */
4540 if (ThreadHandle)
4541 {
4542 /* So kill the process and close the thread handle */
4543 NtTerminateProcess(ProcessHandle, 0);
4544 NtClose(ThreadHandle);
4545 }
4546
4547 /* If we have a process handle, this was a failure path, so close it */
4548 if (ProcessHandle) NtClose(ProcessHandle);
4549
4550 /* Thread/process handles, if any, are now processed. Now close this one. */
4551 if (JobHandle) NtClose(JobHandle);
4552
4553 /* Check if we had created a token */
4554 if (TokenHandle)
4555 {
4556 /* And if the user asked for one */
4557 if (hUserToken)
4558 {
4559 /* Then return it */
4560 *hNewToken = TokenHandle;
4561 }
4562 else
4563 {
4564 /* User didn't want it, so we used it temporarily -- close it */
4565 NtClose(TokenHandle);
4566 }
4567 }
4568
4569 /* Free any temporary app compatibility data, it's no longer needed */
4570 BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
4571
4572 /* Free a few strings. The API takes care of these possibly being NULL */
4573 RtlFreeUnicodeString(&VdmString);
4574 RtlFreeUnicodeString(&DebuggerString);
4575
4576 /* Check if we had built any sort of VDM environment */
4577 if ((VdmAnsiEnv.Buffer) || (VdmUnicodeEnv.Buffer))
4578 {
4579 /* Free it */
4580 BaseDestroyVDMEnvironment(&VdmAnsiEnv, &VdmUnicodeEnv);
4581 }
4582
4583 /* Check if this was any kind of VDM application that we ended up creating */
4584 if ((VdmUndoLevel) && (!(VdmUndoLevel & VDM_UNDO_COMPLETED)))
4585 {
4586 /* Send an undo */
4587 BaseUpdateVDMEntry(VdmEntryUndo,
4588 (PHANDLE)&VdmTask,
4589 VdmUndoLevel,
4590 VdmBinaryType);
4591
4592 /* And close whatever VDM handle we were using for notifications */
4593 if (VdmWaitObject) NtClose(VdmWaitObject);
4594 }
4595
4596 /* Check if we ended up here with an allocated search path, and free it */
4597 if (SearchPath) RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
4598
4599 /* Finally, return the API's result */
4600 return Result;
4601 }
4602
4603 /*
4604 * @implemented
4605 */
4606 BOOL
4607 WINAPI
4608 CreateProcessW(LPCWSTR lpApplicationName,
4609 LPWSTR lpCommandLine,
4610 LPSECURITY_ATTRIBUTES lpProcessAttributes,
4611 LPSECURITY_ATTRIBUTES lpThreadAttributes,
4612 BOOL bInheritHandles,
4613 DWORD dwCreationFlags,
4614 LPVOID lpEnvironment,
4615 LPCWSTR lpCurrentDirectory,
4616 LPSTARTUPINFOW lpStartupInfo,
4617 LPPROCESS_INFORMATION lpProcessInformation)
4618 {
4619 /* Call the internal (but exported) version */
4620 return CreateProcessInternalW(NULL,
4621 lpApplicationName,
4622 lpCommandLine,
4623 lpProcessAttributes,
4624 lpThreadAttributes,
4625 bInheritHandles,
4626 dwCreationFlags,
4627 lpEnvironment,
4628 lpCurrentDirectory,
4629 lpStartupInfo,
4630 lpProcessInformation,
4631 NULL);
4632 }
4633
4634 /*
4635 * @implemented
4636 */
4637 BOOL
4638 WINAPI
4639 CreateProcessInternalA(HANDLE hToken,
4640 LPCSTR lpApplicationName,
4641 LPSTR lpCommandLine,
4642 LPSECURITY_ATTRIBUTES lpProcessAttributes,
4643 LPSECURITY_ATTRIBUTES lpThreadAttributes,
4644 BOOL bInheritHandles,
4645 DWORD dwCreationFlags,
4646 LPVOID lpEnvironment,
4647 LPCSTR lpCurrentDirectory,
4648 LPSTARTUPINFOA lpStartupInfo,
4649 LPPROCESS_INFORMATION lpProcessInformation,
4650 PHANDLE hNewToken)
4651 {
4652 PUNICODE_STRING CommandLine = NULL;
4653 UNICODE_STRING DummyString;
4654 UNICODE_STRING LiveCommandLine;
4655 UNICODE_STRING ApplicationName;
4656 UNICODE_STRING CurrentDirectory;
4657 BOOL bRetVal;
4658 STARTUPINFOW StartupInfo;
4659
4660 DPRINT("dwCreationFlags %x, lpEnvironment %p, lpCurrentDirectory %p, "
4661 "lpStartupInfo %p, lpProcessInformation %p\n",
4662 dwCreationFlags, lpEnvironment, lpCurrentDirectory,
4663 lpStartupInfo, lpProcessInformation);
4664
4665 /* Copy Startup Info */
4666 RtlMoveMemory(&StartupInfo, lpStartupInfo, sizeof(*lpStartupInfo));
4667
4668 /* Initialize all strings to nothing */
4669 LiveCommandLine.Buffer = NULL;
4670 DummyString.Buffer = NULL;
4671 ApplicationName.Buffer = NULL;
4672 CurrentDirectory.Buffer = NULL;
4673 StartupInfo.lpDesktop = NULL;
4674 StartupInfo.lpReserved = NULL;
4675 StartupInfo.lpTitle = NULL;
4676
4677 /* Convert the Command line */
4678 if (lpCommandLine)
4679 {
4680 /* If it's too long, then we'll have a problem */
4681 if ((strlen(lpCommandLine) + 1) * sizeof(WCHAR) <
4682 NtCurrentTeb()->StaticUnicodeString.MaximumLength)
4683 {
4684 /* Cache it in the TEB */
4685 CommandLine = Basep8BitStringToStaticUnicodeString(lpCommandLine);
4686 }
4687 else
4688 {
4689 /* Use a dynamic version */
4690 Basep8BitStringToDynamicUnicodeString(&LiveCommandLine,
4691 lpCommandLine);
4692 }
4693 }
4694 else
4695 {
4696 /* The logic below will use CommandLine, so we must make it valid */
4697 CommandLine = &DummyString;
4698 }
4699
4700 /* Convert the Name and Directory */
4701 if (lpApplicationName)
4702 {
4703 Basep8BitStringToDynamicUnicodeString(&ApplicationName,
4704 lpApplicationName);
4705 }
4706 if (lpCurrentDirectory)
4707 {
4708 Basep8BitStringToDynamicUnicodeString(&CurrentDirectory,
4709 lpCurrentDirectory);
4710 }
4711
4712 /* Now convert Startup Strings */
4713 if (lpStartupInfo->lpReserved)
4714 {
4715 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpReserved,
4716 &StartupInfo.lpReserved);
4717 }
4718 if (lpStartupInfo->lpDesktop)
4719 {
4720 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpDesktop,
4721 &StartupInfo.lpDesktop);
4722 }
4723 if (lpStartupInfo->lpTitle)
4724 {
4725 BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpTitle,
4726 &StartupInfo.lpTitle);
4727 }
4728
4729 /* Call the Unicode function */
4730 bRetVal = CreateProcessInternalW(hToken,
4731 ApplicationName.Buffer,
4732 LiveCommandLine.Buffer ?
4733 LiveCommandLine.Buffer : CommandLine->Buffer,
4734 lpProcessAttributes,
4735 lpThreadAttributes,
4736 bInheritHandles,
4737 dwCreationFlags,
4738 lpEnvironment,
4739 CurrentDirectory.Buffer,
4740 &StartupInfo,
4741 lpProcessInformation,
4742 hNewToken);
4743
4744 /* Clean up */
4745 RtlFreeUnicodeString(&ApplicationName);
4746 RtlFreeUnicodeString(&LiveCommandLine);
4747 RtlFreeUnicodeString(&CurrentDirectory);
4748 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpDesktop);
4749 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpReserved);
4750 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpTitle);
4751
4752 /* Return what Unicode did */
4753 return bRetVal;
4754 }
4755
4756 /*
4757 * FUNCTION: The CreateProcess function creates a new process and its
4758 * primary thread. The new process executes the specified executable file
4759 * ARGUMENTS:
4760 *
4761 * lpApplicationName = Pointer to name of executable module
4762 * lpCommandLine = Pointer to command line string
4763 * lpProcessAttributes = Process security attributes
4764 * lpThreadAttributes = Thread security attributes
4765 * bInheritHandles = Handle inheritance flag
4766 * dwCreationFlags = Creation flags
4767 * lpEnvironment = Pointer to new environment block
4768 * lpCurrentDirectory = Pointer to current directory name
4769 * lpStartupInfo = Pointer to startup info
4770 * lpProcessInformation = Pointer to process information
4771 *
4772 * @implemented
4773 */
4774 BOOL
4775 WINAPI
4776 CreateProcessA(LPCSTR lpApplicationName,
4777 LPSTR lpCommandLine,
4778 LPSECURITY_ATTRIBUTES lpProcessAttributes,
4779 LPSECURITY_ATTRIBUTES lpThreadAttributes,
4780 BOOL bInheritHandles,
4781 DWORD dwCreationFlags,
4782 LPVOID lpEnvironment,
4783 LPCSTR lpCurrentDirectory,
4784 LPSTARTUPINFOA lpStartupInfo,
4785 LPPROCESS_INFORMATION lpProcessInformation)
4786 {
4787 /* Call the internal (but exported) version */
4788 return CreateProcessInternalA(NULL,
4789 lpApplicationName,
4790 lpCommandLine,
4791 lpProcessAttributes,
4792 lpThreadAttributes,
4793 bInheritHandles,
4794 dwCreationFlags,
4795 lpEnvironment,
4796 lpCurrentDirectory,
4797 lpStartupInfo,
4798 lpProcessInformation,
4799 NULL);
4800 }
4801
4802 /*
4803 * @implemented
4804 */
4805 UINT
4806 WINAPI
4807 WinExec(LPCSTR lpCmdLine,
4808 UINT uCmdShow)
4809 {
4810 STARTUPINFOA StartupInfo;
4811 PROCESS_INFORMATION ProcessInformation;
4812 DWORD dosErr;
4813
4814 RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
4815 StartupInfo.cb = sizeof(STARTUPINFOA);
4816 StartupInfo.wShowWindow = (WORD)uCmdShow;
4817 StartupInfo.dwFlags = 0;
4818
4819 if (!CreateProcessA(NULL,
4820 (PVOID)lpCmdLine,
4821 NULL,
4822 NULL,
4823 FALSE,
4824 0,
4825 NULL,
4826 NULL,
4827 &StartupInfo,
4828 &ProcessInformation))
4829 {
4830 dosErr = GetLastError();
4831 return dosErr < 32 ? dosErr : ERROR_BAD_FORMAT;
4832 }
4833
4834 if (NULL != UserWaitForInputIdleRoutine)
4835 {
4836 UserWaitForInputIdleRoutine(ProcessInformation.hProcess,
4837 10000);
4838 }
4839
4840 NtClose(ProcessInformation.hProcess);
4841 NtClose(ProcessInformation.hThread);
4842
4843 return 33; /* Something bigger than 31 means success. */
4844 }
4845
4846 /* EOF */