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