xref: /reactos/dll/win32/kernel32/client/proc.c (revision 5c7ce447)
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 VOID
449 WINAPI
450 BaseProcessStartup(PPROCESS_START_ROUTINE lpStartAddress)
451 {
452     DPRINT("BaseProcessStartup(..) - setting up exception frame.\n");
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(DWORD));
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 #if DBG
1610     /* Give the user a chance to abort */
1611     if ((NT_SUCCESS(Status)) && (Response == ResponseCancel)) return;
1612 #else
1613     UNREFERENCED_LOCAL_VARIABLE(Status);
1614 #endif
1615 
1616     /* Otherwise kill the process */
1617     ExitProcess(0);
1618 }
1619 
1620 /*
1621  * @implemented
1622  */
1623 VOID
1624 WINAPI
1625 FatalExit(IN int ExitCode)
1626 {
1627 #if DBG
1628     /* On Checked builds, Windows gives the user a nice little debugger UI */
1629     CHAR ch[2];
1630     DbgPrint("FatalExit...\n");
1631     DbgPrint("\n");
1632 
1633     while (TRUE)
1634     {
1635         DbgPrompt( "A (Abort), B (Break), I (Ignore)? ", ch, sizeof(ch));
1636         switch (ch[0])
1637         {
1638             case 'B': case 'b':
1639                  DbgBreakPoint();
1640                  break;
1641 
1642             case 'A': case 'a':
1643                 ExitProcess(ExitCode);
1644 
1645             case 'I': case 'i':
1646                 return;
1647         }
1648     }
1649 #endif
1650     /* On other builds, just kill the process */
1651     ExitProcess(ExitCode);
1652 }
1653 
1654 /*
1655  * @implemented
1656  */
1657 DWORD
1658 WINAPI
1659 GetPriorityClass(IN HANDLE hProcess)
1660 {
1661     NTSTATUS Status;
1662     PROCESS_PRIORITY_CLASS PriorityClass;
1663 
1664     /* Query the kernel */
1665     Status = NtQueryInformationProcess(hProcess,
1666                                        ProcessPriorityClass,
1667                                        &PriorityClass,
1668                                        sizeof(PriorityClass),
1669                                        NULL);
1670     if (NT_SUCCESS(Status))
1671     {
1672         /* Handle the conversion from NT to Win32 classes */
1673         switch (PriorityClass.PriorityClass)
1674         {
1675             case PROCESS_PRIORITY_CLASS_IDLE: return IDLE_PRIORITY_CLASS;
1676             case PROCESS_PRIORITY_CLASS_BELOW_NORMAL: return BELOW_NORMAL_PRIORITY_CLASS;
1677             case PROCESS_PRIORITY_CLASS_ABOVE_NORMAL: return ABOVE_NORMAL_PRIORITY_CLASS;
1678             case PROCESS_PRIORITY_CLASS_HIGH: return HIGH_PRIORITY_CLASS;
1679             case PROCESS_PRIORITY_CLASS_REALTIME: return REALTIME_PRIORITY_CLASS;
1680             case PROCESS_PRIORITY_CLASS_NORMAL: default: return NORMAL_PRIORITY_CLASS;
1681         }
1682     }
1683 
1684     /* Failure path */
1685     BaseSetLastNTError(Status);
1686     return 0;
1687 }
1688 
1689 /*
1690  * @implemented
1691  */
1692 BOOL
1693 WINAPI
1694 SetPriorityClass(IN HANDLE hProcess,
1695                  IN DWORD dwPriorityClass)
1696 {
1697     NTSTATUS Status;
1698     PVOID State = NULL;
1699     PROCESS_PRIORITY_CLASS PriorityClass;
1700 
1701     /* Handle conversion from Win32 to NT priority classes */
1702     switch (dwPriorityClass)
1703     {
1704         case IDLE_PRIORITY_CLASS:
1705             PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
1706             break;
1707 
1708         case BELOW_NORMAL_PRIORITY_CLASS:
1709             PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
1710             break;
1711 
1712         case NORMAL_PRIORITY_CLASS:
1713             PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
1714             break;
1715 
1716         case ABOVE_NORMAL_PRIORITY_CLASS:
1717             PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
1718             break;
1719 
1720         case HIGH_PRIORITY_CLASS:
1721             PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
1722             break;
1723 
1724         case REALTIME_PRIORITY_CLASS:
1725             /* Try to acquire the privilege. If it fails, just use HIGH */
1726             State = BasepIsRealtimeAllowed(TRUE);
1727             PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
1728             PriorityClass.PriorityClass += (State != NULL);
1729             break;
1730 
1731         default:
1732             /* Unrecognized priority classes don't make it to the kernel */
1733             SetLastError(ERROR_INVALID_PARAMETER);
1734             return FALSE;
1735     }
1736 
1737     /* Send the request to the kernel, and don't touch the foreground flag */
1738     PriorityClass.Foreground = FALSE;
1739     Status = NtSetInformationProcess(hProcess,
1740                                      ProcessPriorityClass,
1741                                      &PriorityClass,
1742                                      sizeof(PROCESS_PRIORITY_CLASS));
1743 
1744     /* Release the privilege if we had it */
1745     if (State) RtlReleasePrivilege(State);
1746     if (!NT_SUCCESS(Status))
1747     {
1748         /* Handle error path */
1749         BaseSetLastNTError(Status);
1750         return FALSE;
1751     }
1752 
1753     /* All done */
1754     return TRUE;
1755 }
1756 
1757 /*
1758  * @implemented
1759  */
1760 DWORD
1761 WINAPI
1762 GetProcessVersion(IN DWORD ProcessId)
1763 {
1764     DWORD Version = 0;
1765     PIMAGE_NT_HEADERS NtHeader;
1766     PIMAGE_DOS_HEADER DosHeader;
1767     PPEB Peb;
1768     PROCESS_BASIC_INFORMATION ProcessBasicInfo;
1769     PVOID BaseAddress;
1770     ULONG e_lfanew;
1771     HANDLE ProcessHandle = NULL;
1772     NTSTATUS Status;
1773     USHORT VersionData[2];
1774     BOOLEAN Result;
1775 
1776     /* We'll be accessing stuff that can fault, so protect everything with SEH */
1777     _SEH2_TRY
1778     {
1779         /* It this an in-process or out-of-process request? */
1780         if (!(ProcessId) || (GetCurrentProcessId() == ProcessId))
1781         {
1782             /* It's in-process, so just read our own header */
1783             NtHeader = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
1784             if (!NtHeader)
1785             {
1786                 /* Unable to read the NT header, something is wrong here... */
1787                 Status = STATUS_INVALID_IMAGE_FORMAT;
1788                 goto Error;
1789             }
1790 
1791             /* Get the version straight out of the NT header */
1792             Version = MAKELONG(NtHeader->OptionalHeader.MinorSubsystemVersion,
1793                                NtHeader->OptionalHeader.MajorSubsystemVersion);
1794         }
1795         else
1796         {
1797             /* Out-of-process, so open it */
1798             ProcessHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
1799                                         FALSE,
1800                                         ProcessId);
1801             if (!ProcessHandle) _SEH2_YIELD(return 0);
1802 
1803             /* Try to find out where its PEB lives */
1804             Status = NtQueryInformationProcess(ProcessHandle,
1805                                                ProcessBasicInformation,
1806                                                &ProcessBasicInfo,
1807                                                sizeof(ProcessBasicInfo),
1808                                                NULL);
1809 
1810             if (!NT_SUCCESS(Status)) goto Error;
1811             Peb = ProcessBasicInfo.PebBaseAddress;
1812 
1813             /* Now that we have the PEB, read the image base address out of it */
1814             Result = ReadProcessMemory(ProcessHandle,
1815                                        &Peb->ImageBaseAddress,
1816                                        &BaseAddress,
1817                                        sizeof(BaseAddress),
1818                                        NULL);
1819             if (!Result) goto Error;
1820 
1821             /* Now read the e_lfanew (offset to NT header) from the base */
1822             DosHeader = BaseAddress;
1823             Result = ReadProcessMemory(ProcessHandle,
1824                                        &DosHeader->e_lfanew,
1825                                        &e_lfanew,
1826                                        sizeof(e_lfanew),
1827                                        NULL);
1828             if (!Result) goto Error;
1829 
1830             /* And finally, read the NT header itself by adding the offset */
1831             NtHeader = (PVOID)((ULONG_PTR)BaseAddress + e_lfanew);
1832             Result = ReadProcessMemory(ProcessHandle,
1833                                        &NtHeader->OptionalHeader.MajorSubsystemVersion,
1834                                        &VersionData,
1835                                        sizeof(VersionData),
1836                                        NULL);
1837             if (!Result) goto Error;
1838 
1839             /* Get the version straight out of the NT header */
1840             Version = MAKELONG(VersionData[0], VersionData[1]);
1841 
1842 Error:
1843             /* If there was an error anywhere, set the last error */
1844             if (!NT_SUCCESS(Status)) BaseSetLastNTError(Status);
1845         }
1846     }
1847     _SEH2_FINALLY
1848     {
1849         /* Close the process handle */
1850         if (ProcessHandle) CloseHandle(ProcessHandle);
1851     }
1852     _SEH2_END;
1853 
1854     /* And return the version data */
1855     return Version;
1856 }
1857 
1858 /*
1859  * @implemented
1860  */
1861 BOOL
1862 WINAPI
1863 GetProcessIoCounters(IN HANDLE hProcess,
1864                      OUT PIO_COUNTERS lpIoCounters)
1865 {
1866     NTSTATUS Status;
1867 
1868     /* Query the kernel. Structures are identical, so let it do the copy too. */
1869     Status = NtQueryInformationProcess(hProcess,
1870                                        ProcessIoCounters,
1871                                        lpIoCounters,
1872                                        sizeof(IO_COUNTERS),
1873                                        NULL);
1874     if (!NT_SUCCESS(Status))
1875     {
1876         /* Handle error path */
1877         BaseSetLastNTError(Status);
1878         return FALSE;
1879     }
1880 
1881     /* All done */
1882     return TRUE;
1883 }
1884 
1885 /*
1886  * @implemented
1887  */
1888 BOOL
1889 WINAPI
1890 GetProcessPriorityBoost(IN HANDLE hProcess,
1891                         OUT PBOOL pDisablePriorityBoost)
1892 {
1893     NTSTATUS Status;
1894     ULONG PriorityBoost;
1895 
1896     /* Query the kernel */
1897     Status = NtQueryInformationProcess(hProcess,
1898                                        ProcessPriorityBoost,
1899                                        &PriorityBoost,
1900                                        sizeof(PriorityBoost),
1901                                        NULL);
1902     if (NT_SUCCESS(Status))
1903     {
1904         /* Convert from ULONG to a BOOL */
1905         *pDisablePriorityBoost = PriorityBoost ? TRUE : FALSE;
1906         return TRUE;
1907     }
1908 
1909     /* Handle error path */
1910     BaseSetLastNTError(Status);
1911     return FALSE;
1912 }
1913 
1914 /*
1915  * @implemented
1916  */
1917 BOOL
1918 WINAPI
1919 SetProcessPriorityBoost(IN HANDLE hProcess,
1920                         IN BOOL bDisablePriorityBoost)
1921 {
1922     NTSTATUS Status;
1923     ULONG PriorityBoost;
1924 
1925     /* Enforce that this is a BOOL, and send it to the kernel as a ULONG */
1926     PriorityBoost = (bDisablePriorityBoost ? TRUE : FALSE);
1927     Status = NtSetInformationProcess(hProcess,
1928                                      ProcessPriorityBoost,
1929                                      &PriorityBoost,
1930                                      sizeof(ULONG));
1931     if (!NT_SUCCESS(Status))
1932     {
1933         /* Handle error path */
1934         BaseSetLastNTError(Status);
1935         return FALSE;
1936     }
1937 
1938     /* All done */
1939     return TRUE;
1940 }
1941 
1942 /*
1943  * @implemented
1944  */
1945 BOOL
1946 WINAPI
1947 GetProcessHandleCount(IN HANDLE hProcess,
1948                       OUT PDWORD pdwHandleCount)
1949 {
1950     ULONG phc;
1951     NTSTATUS Status;
1952 
1953     /* Query the kernel */
1954     Status = NtQueryInformationProcess(hProcess,
1955                                        ProcessHandleCount,
1956                                        &phc,
1957                                        sizeof(phc),
1958                                        NULL);
1959     if (NT_SUCCESS(Status))
1960     {
1961         /* Copy the count and return success */
1962         *pdwHandleCount = phc;
1963         return TRUE;
1964     }
1965 
1966     /* Handle error path */
1967     BaseSetLastNTError(Status);
1968     return FALSE;
1969 }
1970 
1971 /*
1972  * @implemented
1973  */
1974 BOOL
1975 WINAPI
1976 IsWow64Process(IN HANDLE hProcess,
1977                OUT PBOOL Wow64Process)
1978 {
1979     ULONG_PTR pbi;
1980     NTSTATUS Status;
1981 
1982     /* Query the kernel */
1983     Status = NtQueryInformationProcess(hProcess,
1984                                        ProcessWow64Information,
1985                                        &pbi,
1986                                        sizeof(pbi),
1987                                        NULL);
1988     if (!NT_SUCCESS(Status))
1989     {
1990         /* Handle error path */
1991         BaseSetLastNTError(Status);
1992         return FALSE;
1993     }
1994 
1995     /* Enforce this is a BOOL, and return success */
1996     *Wow64Process = (pbi != 0);
1997     return TRUE;
1998 }
1999 
2000 /*
2001  * @implemented
2002  */
2003 LPSTR
2004 WINAPI
2005 GetCommandLineA(VOID)
2006 {
2007     return BaseAnsiCommandLine.Buffer;
2008 }
2009 
2010 /*
2011  * @implemented
2012  */
2013 LPWSTR
2014 WINAPI
2015 GetCommandLineW(VOID)
2016 {
2017     return BaseUnicodeCommandLine.Buffer;
2018 }
2019 
2020 /*
2021  * @implemented
2022  */
2023 BOOL
2024 NTAPI
2025 ReadProcessMemory(IN HANDLE hProcess,
2026                   IN LPCVOID lpBaseAddress,
2027                   IN LPVOID lpBuffer,
2028                   IN SIZE_T nSize,
2029                   OUT SIZE_T* lpNumberOfBytesRead)
2030 {
2031     NTSTATUS Status;
2032 
2033     /* Do the read */
2034     Status = NtReadVirtualMemory(hProcess,
2035                                  (PVOID)lpBaseAddress,
2036                                  lpBuffer,
2037                                  nSize,
2038                                  &nSize);
2039 
2040     /* In user-mode, this parameter is optional */
2041     if (lpNumberOfBytesRead) *lpNumberOfBytesRead = nSize;
2042     if (!NT_SUCCESS(Status))
2043     {
2044         /* We failed */
2045         BaseSetLastNTError(Status);
2046         return FALSE;
2047     }
2048 
2049     /* Return success */
2050     return TRUE;
2051 }
2052 
2053 /*
2054  * @implemented
2055  */
2056 BOOL
2057 NTAPI
2058 WriteProcessMemory(IN HANDLE hProcess,
2059                    IN LPVOID lpBaseAddress,
2060                    IN LPCVOID lpBuffer,
2061                    IN SIZE_T nSize,
2062                    OUT SIZE_T *lpNumberOfBytesWritten)
2063 {
2064     NTSTATUS Status;
2065     ULONG OldValue;
2066     SIZE_T RegionSize;
2067     PVOID Base;
2068     BOOLEAN UnProtect;
2069 
2070     /* Set parameters for protect call */
2071     RegionSize = nSize;
2072     Base = lpBaseAddress;
2073 
2074     /* Check the current status */
2075     Status = NtProtectVirtualMemory(hProcess,
2076                                     &Base,
2077                                     &RegionSize,
2078                                     PAGE_EXECUTE_READWRITE,
2079                                     &OldValue);
2080     if (NT_SUCCESS(Status))
2081     {
2082         /* Check if we are unprotecting */
2083         UnProtect = OldValue & (PAGE_READWRITE |
2084                                 PAGE_WRITECOPY |
2085                                 PAGE_EXECUTE_READWRITE |
2086                                 PAGE_EXECUTE_WRITECOPY) ? FALSE : TRUE;
2087         if (!UnProtect)
2088         {
2089             /* Set the new protection */
2090             Status = NtProtectVirtualMemory(hProcess,
2091                                             &Base,
2092                                             &RegionSize,
2093                                             OldValue,
2094                                             &OldValue);
2095 
2096             /* Write the memory */
2097             Status = NtWriteVirtualMemory(hProcess,
2098                                           lpBaseAddress,
2099                                           (LPVOID)lpBuffer,
2100                                           nSize,
2101                                           &nSize);
2102 
2103             /* In Win32, the parameter is optional, so handle this case */
2104             if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
2105 
2106             if (!NT_SUCCESS(Status))
2107             {
2108                 /* We failed */
2109                 BaseSetLastNTError(Status);
2110                 return FALSE;
2111             }
2112 
2113             /* Flush the ITLB */
2114             NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
2115             return TRUE;
2116         }
2117         else
2118         {
2119             /* Check if we were read only */
2120             if (OldValue & (PAGE_NOACCESS | PAGE_READONLY))
2121             {
2122                 /* Restore protection and fail */
2123                 NtProtectVirtualMemory(hProcess,
2124                                        &Base,
2125                                        &RegionSize,
2126                                        OldValue,
2127                                        &OldValue);
2128                 BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
2129 
2130                 /* Note: This is what Windows returns and code depends on it */
2131                 return STATUS_ACCESS_VIOLATION;
2132             }
2133 
2134             /* Otherwise, do the write */
2135             Status = NtWriteVirtualMemory(hProcess,
2136                                           lpBaseAddress,
2137                                           (LPVOID)lpBuffer,
2138                                           nSize,
2139                                           &nSize);
2140 
2141             /* In Win32, the parameter is optional, so handle this case */
2142             if (lpNumberOfBytesWritten) *lpNumberOfBytesWritten = nSize;
2143 
2144             /* And restore the protection */
2145             NtProtectVirtualMemory(hProcess,
2146                                    &Base,
2147                                    &RegionSize,
2148                                    OldValue,
2149                                    &OldValue);
2150             if (!NT_SUCCESS(Status))
2151             {
2152                 /* We failed */
2153                 BaseSetLastNTError(STATUS_ACCESS_VIOLATION);
2154 
2155                 /* Note: This is what Windows returns and code depends on it */
2156                 return STATUS_ACCESS_VIOLATION;
2157             }
2158 
2159             /* Flush the ITLB */
2160             NtFlushInstructionCache(hProcess, lpBaseAddress, nSize);
2161             return TRUE;
2162         }
2163     }
2164     else
2165     {
2166         /* We failed */
2167         BaseSetLastNTError(Status);
2168         return FALSE;
2169     }
2170 }
2171 
2172 /*
2173  * @implemented
2174  */
2175 BOOL
2176 WINAPI
2177 ProcessIdToSessionId(IN DWORD dwProcessId,
2178                      OUT PDWORD pSessionId)
2179 {
2180     PROCESS_SESSION_INFORMATION SessionInformation;
2181     OBJECT_ATTRIBUTES ObjectAttributes;
2182     CLIENT_ID ClientId;
2183     HANDLE ProcessHandle;
2184     NTSTATUS Status;
2185 
2186     /* Do a quick check if the pointer is not writable */
2187     if (IsBadWritePtr(pSessionId, sizeof(DWORD)))
2188     {
2189         /* Fail fast */
2190         SetLastError(ERROR_INVALID_PARAMETER);
2191         return FALSE;
2192     }
2193 
2194     /* Open the process passed in by ID */
2195     ClientId.UniqueProcess = UlongToHandle(dwProcessId);
2196     ClientId.UniqueThread = 0;
2197     InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
2198     Status = NtOpenProcess(&ProcessHandle,
2199                            PROCESS_QUERY_INFORMATION,
2200                            &ObjectAttributes,
2201                            &ClientId);
2202     if (NT_SUCCESS(Status))
2203     {
2204         /* Query the session ID from the kernel */
2205         Status = NtQueryInformationProcess(ProcessHandle,
2206                                            ProcessSessionInformation,
2207                                            &SessionInformation,
2208                                            sizeof(SessionInformation),
2209                                            NULL);
2210 
2211         /* Close the handle and check if we succeeded */
2212         NtClose(ProcessHandle);
2213         if (NT_SUCCESS(Status))
2214         {
2215             /* Return the session ID */
2216             *pSessionId = SessionInformation.SessionId;
2217             return TRUE;
2218         }
2219     }
2220 
2221     /* Set error code and fail */
2222     BaseSetLastNTError(Status);
2223     return FALSE;
2224 }
2225 
2226 
2227 #define AddToHandle(x,y)  (x) = (HANDLE)((ULONG_PTR)(x) | (y));
2228 #define RemoveFromHandle(x,y)  (x) = (HANDLE)((ULONG_PTR)(x) & ~(y));
2229 C_ASSERT(PROCESS_PRIORITY_CLASS_REALTIME == (PROCESS_PRIORITY_CLASS_HIGH + 1));
2230 
2231 /*
2232  * @implemented
2233  */
2234 BOOL
2235 WINAPI
2236 CreateProcessInternalW(IN HANDLE hUserToken,
2237                        IN LPCWSTR lpApplicationName,
2238                        IN LPWSTR lpCommandLine,
2239                        IN LPSECURITY_ATTRIBUTES lpProcessAttributes,
2240                        IN LPSECURITY_ATTRIBUTES lpThreadAttributes,
2241                        IN BOOL bInheritHandles,
2242                        IN DWORD dwCreationFlags,
2243                        IN LPVOID lpEnvironment,
2244                        IN LPCWSTR lpCurrentDirectory,
2245                        IN LPSTARTUPINFOW lpStartupInfo,
2246                        IN LPPROCESS_INFORMATION lpProcessInformation,
2247                        OUT PHANDLE hNewToken)
2248 {
2249     //
2250     // Core variables used for creating the initial process and thread
2251     //
2252     SECURITY_ATTRIBUTES LocalThreadAttributes, LocalProcessAttributes;
2253     OBJECT_ATTRIBUTES LocalObjectAttributes;
2254     POBJECT_ATTRIBUTES ObjectAttributes;
2255     SECTION_IMAGE_INFORMATION ImageInformation;
2256     IO_STATUS_BLOCK IoStatusBlock;
2257     CLIENT_ID ClientId;
2258     ULONG NoWindow, StackSize, ErrorCode, Flags;
2259     SIZE_T RegionSize;
2260     USHORT ImageMachine;
2261     ULONG ParameterFlags, PrivilegeValue, HardErrorMode, ErrorResponse;
2262     ULONG_PTR ErrorParameters[2];
2263     BOOLEAN InJob, SaferNeeded, UseLargePages, HavePrivilege;
2264     BOOLEAN QuerySection, SkipSaferAndAppCompat;
2265     CONTEXT Context;
2266     BASE_API_MESSAGE CsrMsg[2];
2267     PBASE_CREATE_PROCESS CreateProcessMsg;
2268     PCSR_CAPTURE_BUFFER CaptureBuffer;
2269     PVOID BaseAddress, PrivilegeState, RealTimePrivilegeState;
2270     HANDLE DebugHandle, TokenHandle, JobHandle, KeyHandle, ThreadHandle;
2271     HANDLE FileHandle, SectionHandle, ProcessHandle;
2272     ULONG ResumeCount;
2273     PROCESS_PRIORITY_CLASS PriorityClass;
2274     NTSTATUS Status, AppCompatStatus, SaferStatus, IFEOStatus, ImageDbgStatus;
2275     PPEB Peb, RemotePeb;
2276     PTEB Teb;
2277     INITIAL_TEB InitialTeb;
2278     PVOID TibValue;
2279     PIMAGE_NT_HEADERS NtHeaders;
2280     STARTUPINFOW StartupInfo;
2281     PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
2282     UNICODE_STRING DebuggerString;
2283     BOOL Result;
2284     //
2285     // Variables used for command-line and argument parsing
2286     //
2287     PCHAR pcScan;
2288     SIZE_T n;
2289     WCHAR SaveChar;
2290     ULONG Length, FileAttribs, CmdQuoteLength;
2291     ULONG ResultSize;
2292     SIZE_T EnvironmentLength, CmdLineLength;
2293     PWCHAR QuotedCmdLine, AnsiCmdCommand, ExtBuffer, CurrentDirectory;
2294     PWCHAR NullBuffer, ScanString, NameBuffer, SearchPath, DebuggerCmdLine;
2295     ANSI_STRING AnsiEnv;
2296     UNICODE_STRING UnicodeEnv, PathName;
2297     BOOLEAN SearchRetry, QuotesNeeded, CmdLineIsAppName, HasQuotes;
2298 
2299     //
2300     // Variables used for Fusion/SxS (Side-by-Side Assemblies)
2301     //
2302     RTL_PATH_TYPE SxsPathType, PathType;
2303 #if _SXS_SUPPORT_ENABLED_
2304     PRTL_BUFFER ByteBuffer;
2305     PRTL_UNICODE_STRING_BUFFER ThisBuffer, Buffer, SxsStaticBuffers[5];
2306     PRTL_UNICODE_STRING_BUFFER* BufferHead, SxsStringBuffer;
2307     RTL_UNICODE_STRING_BUFFER SxsWin32ManifestPath, SxsNtManifestPath;
2308     RTL_UNICODE_STRING_BUFFER SxsWin32PolicyPath, SxsNtPolicyPath;
2309     RTL_UNICODE_STRING_BUFFER SxsWin32AssemblyDirectory;
2310     BASE_MSG_SXS_HANDLES MappedHandles, Handles, FileHandles;
2311     PVOID CapturedStrings[3];
2312     SXS_WIN32_NT_PATH_PAIR ExePathPair, ManifestPathPair, PolicyPathPair;
2313     SXS_OVERRIDE_MANIFEST OverrideManifest;
2314     UNICODE_STRING FreeString, SxsNtExePath;
2315     PWCHAR SxsConglomeratedBuffer, StaticBuffer;
2316     ULONG ConglomeratedBufferSizeBytes, StaticBufferSize, i;
2317 #endif
2318     ULONG FusionFlags;
2319 
2320     //
2321     // Variables used for path conversion (and partially Fusion/SxS)
2322     //
2323     PWCHAR FilePart, PathBuffer, FreeBuffer;
2324     BOOLEAN TranslationStatus;
2325     RTL_RELATIVE_NAME_U SxsWin32RelativePath;
2326     UNICODE_STRING PathBufferString, SxsWin32ExePath;
2327 
2328     //
2329     // Variables used by Application Compatibility (and partially Fusion/SxS)
2330     //
2331     PVOID AppCompatSxsData, AppCompatData;
2332     ULONG AppCompatSxsDataSize, AppCompatDataSize;
2333     //
2334     // Variables used by VDM (Virtual Dos Machine) and WOW32 (16-bit Support)
2335     //
2336     ULONG BinarySubType, VdmBinaryType, VdmTask, VdmReserve;
2337     ULONG VdmUndoLevel;
2338     BOOLEAN UseVdmReserve;
2339     HANDLE VdmWaitObject;
2340     ANSI_STRING VdmAnsiEnv;
2341     UNICODE_STRING VdmString, VdmUnicodeEnv;
2342     BOOLEAN IsWowApp;
2343     PBASE_CHECK_VDM CheckVdmMsg;
2344 
2345     /* Zero out the initial core variables and handles */
2346     QuerySection = FALSE;
2347     InJob = FALSE;
2348     SkipSaferAndAppCompat = FALSE;
2349     ParameterFlags = 0;
2350     Flags = 0;
2351     DebugHandle = NULL;
2352     JobHandle = NULL;
2353     TokenHandle = NULL;
2354     FileHandle = NULL;
2355     SectionHandle = NULL;
2356     ProcessHandle = NULL;
2357     ThreadHandle = NULL;
2358     BaseAddress = (PVOID)1;
2359 
2360     /* Zero out initial SxS and Application Compatibility state */
2361     AppCompatData = NULL;
2362     AppCompatDataSize = 0;
2363     AppCompatSxsData = NULL;
2364     AppCompatSxsDataSize = 0;
2365     CaptureBuffer = NULL;
2366 #if _SXS_SUPPORT_ENABLED_
2367     SxsConglomeratedBuffer = NULL;
2368 #endif
2369     FusionFlags = 0;
2370 
2371     /* Zero out initial parsing variables -- others are initialized later */
2372     DebuggerCmdLine = NULL;
2373     PathBuffer = NULL;
2374     SearchPath = NULL;
2375     NullBuffer = NULL;
2376     FreeBuffer = NULL;
2377     NameBuffer = NULL;
2378     CurrentDirectory = NULL;
2379     FilePart = NULL;
2380     DebuggerString.Buffer = NULL;
2381     HasQuotes = FALSE;
2382     QuotedCmdLine = NULL;
2383 
2384     /* Zero out initial VDM state */
2385     VdmAnsiEnv.Buffer = NULL;
2386     VdmUnicodeEnv.Buffer = NULL;
2387     VdmString.Buffer = NULL;
2388     VdmTask = 0;
2389     VdmUndoLevel = 0;
2390     VdmBinaryType = 0;
2391     VdmReserve = 0;
2392     VdmWaitObject = NULL;
2393     UseVdmReserve = FALSE;
2394     IsWowApp = FALSE;
2395 
2396     /* Set message structures */
2397     CreateProcessMsg = &CsrMsg[0].Data.CreateProcessRequest;
2398     CheckVdmMsg = &CsrMsg[1].Data.CheckVDMRequest;
2399 
2400     /* Clear the more complex structures by zeroing out their entire memory */
2401     RtlZeroMemory(&Context, sizeof(Context));
2402 #if _SXS_SUPPORT_ENABLED_
2403     RtlZeroMemory(&FileHandles, sizeof(FileHandles));
2404     RtlZeroMemory(&MappedHandles, sizeof(MappedHandles));
2405     RtlZeroMemory(&Handles, sizeof(Handles));
2406 #endif
2407     RtlZeroMemory(&CreateProcessMsg->Sxs, sizeof(CreateProcessMsg->Sxs));
2408     RtlZeroMemory(&LocalProcessAttributes, sizeof(LocalProcessAttributes));
2409     RtlZeroMemory(&LocalThreadAttributes, sizeof(LocalThreadAttributes));
2410 
2411     /* Zero out output arguments as well */
2412     RtlZeroMemory(lpProcessInformation, sizeof(*lpProcessInformation));
2413     if (hNewToken) *hNewToken = NULL;
2414 
2415     /* Capture the special window flag */
2416     NoWindow = dwCreationFlags & CREATE_NO_WINDOW;
2417     dwCreationFlags &= ~CREATE_NO_WINDOW;
2418 
2419 #if _SXS_SUPPORT_ENABLED_
2420     /* Setup the SxS static string arrays and buffers */
2421     SxsStaticBuffers[0] = &SxsWin32ManifestPath;
2422     SxsStaticBuffers[1] = &SxsWin32PolicyPath;
2423     SxsStaticBuffers[2] = &SxsWin32AssemblyDirectory;
2424     SxsStaticBuffers[3] = &SxsNtManifestPath;
2425     SxsStaticBuffers[4] = &SxsNtPolicyPath;
2426     ExePathPair.Win32 = &SxsWin32ExePath;
2427     ExePathPair.Nt = &SxsNtExePath;
2428     ManifestPathPair.Win32 = &SxsWin32ManifestPath.String;
2429     ManifestPathPair.Nt = &SxsNtManifestPath.String;
2430     PolicyPathPair.Win32 = &SxsWin32PolicyPath.String;
2431     PolicyPathPair.Nt = &SxsNtPolicyPath.String;
2432 #endif
2433 
2434     DPRINT("CreateProcessInternalW: '%S' '%S' %lx\n", lpApplicationName, lpCommandLine, dwCreationFlags);
2435 
2436     /* Finally, set our TEB and PEB */
2437     Teb = NtCurrentTeb();
2438     Peb = NtCurrentPeb();
2439 
2440     /* This combination is illegal (see MSDN) */
2441     if ((dwCreationFlags & (DETACHED_PROCESS | CREATE_NEW_CONSOLE)) ==
2442         (DETACHED_PROCESS | CREATE_NEW_CONSOLE))
2443     {
2444         DPRINT1("Invalid flag combo used\n");
2445         SetLastError(ERROR_INVALID_PARAMETER);
2446         return FALSE;
2447     }
2448 
2449     /* Convert the priority class */
2450     if (dwCreationFlags & IDLE_PRIORITY_CLASS)
2451     {
2452         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_IDLE;
2453     }
2454     else if (dwCreationFlags & BELOW_NORMAL_PRIORITY_CLASS)
2455     {
2456         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_BELOW_NORMAL;
2457     }
2458     else if (dwCreationFlags & NORMAL_PRIORITY_CLASS)
2459     {
2460         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_NORMAL;
2461     }
2462     else if (dwCreationFlags & ABOVE_NORMAL_PRIORITY_CLASS)
2463     {
2464         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_ABOVE_NORMAL;
2465     }
2466     else if (dwCreationFlags & HIGH_PRIORITY_CLASS)
2467     {
2468         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
2469     }
2470     else if (dwCreationFlags & REALTIME_PRIORITY_CLASS)
2471     {
2472         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_HIGH;
2473         PriorityClass.PriorityClass += (BasepIsRealtimeAllowed(FALSE) != NULL);
2474     }
2475     else
2476     {
2477         PriorityClass.PriorityClass = PROCESS_PRIORITY_CLASS_INVALID;
2478     }
2479 
2480     /* Done with the priority masks, so get rid of them */
2481     PriorityClass.Foreground = FALSE;
2482     dwCreationFlags &= ~(NORMAL_PRIORITY_CLASS |
2483                          IDLE_PRIORITY_CLASS |
2484                          HIGH_PRIORITY_CLASS |
2485                          REALTIME_PRIORITY_CLASS |
2486                          BELOW_NORMAL_PRIORITY_CLASS |
2487                          ABOVE_NORMAL_PRIORITY_CLASS);
2488 
2489     /* You cannot request both a shared and a separate WoW VDM */
2490     if ((dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
2491         (dwCreationFlags & CREATE_SHARED_WOW_VDM))
2492     {
2493         /* Fail such nonsensical attempts */
2494         DPRINT1("Invalid WOW flags\n");
2495         SetLastError(ERROR_INVALID_PARAMETER);
2496         return FALSE;
2497     }
2498     else if (!(dwCreationFlags & CREATE_SHARED_WOW_VDM) &&
2499              (BaseStaticServerData->DefaultSeparateVDM))
2500     {
2501         /* A shared WoW VDM was not requested but system enforces separation */
2502         dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
2503     }
2504 
2505     /* If a shared WoW VDM is used, make sure the process isn't in a job */
2506     if (!(dwCreationFlags & CREATE_SEPARATE_WOW_VDM) &&
2507         (NtIsProcessInJob(NtCurrentProcess(), NULL)))
2508     {
2509         /* Remove the shared flag and add the separate flag */
2510         dwCreationFlags = (dwCreationFlags &~ CREATE_SHARED_WOW_VDM) |
2511                                               CREATE_SEPARATE_WOW_VDM;
2512     }
2513 
2514     /* Convert the environment */
2515     if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
2516     {
2517         /* Scan the environment to calculate its Unicode size */
2518         AnsiEnv.Buffer = pcScan = (PCHAR)lpEnvironment;
2519         while ((*pcScan) || (*(pcScan + 1))) ++pcScan;
2520 
2521         /* Make sure the environment is not too large */
2522         EnvironmentLength = (pcScan + sizeof(ANSI_NULL) - (PCHAR)lpEnvironment);
2523         if (EnvironmentLength > MAXUSHORT)
2524         {
2525             /* Fail */
2526             SetLastError(ERROR_INVALID_PARAMETER);
2527             return FALSE;
2528         }
2529 
2530         /* Create our ANSI String */
2531         AnsiEnv.Length = (USHORT)EnvironmentLength;
2532         AnsiEnv.MaximumLength = AnsiEnv.Length + sizeof(ANSI_NULL);
2533 
2534         /* Allocate memory for the Unicode Environment */
2535         UnicodeEnv.Buffer = NULL;
2536         RegionSize = AnsiEnv.MaximumLength * sizeof(WCHAR);
2537         Status = NtAllocateVirtualMemory(NtCurrentProcess(),
2538                                          (PVOID)&UnicodeEnv.Buffer,
2539                                          0,
2540                                          &RegionSize,
2541                                          MEM_COMMIT,
2542                                          PAGE_READWRITE);
2543         if (!NT_SUCCESS(Status))
2544         {
2545             /* Fail */
2546             BaseSetLastNTError(Status);
2547             return FALSE;
2548         }
2549 
2550         /* Use the allocated size and convert */
2551         UnicodeEnv.MaximumLength = (USHORT)RegionSize;
2552         Status = RtlAnsiStringToUnicodeString(&UnicodeEnv, &AnsiEnv, FALSE);
2553         if (!NT_SUCCESS(Status))
2554         {
2555             /* Fail */
2556             NtFreeVirtualMemory(NtCurrentProcess(),
2557                                 (PVOID)&UnicodeEnv.Buffer,
2558                                 &RegionSize,
2559                                 MEM_RELEASE);
2560             BaseSetLastNTError(Status);
2561             return FALSE;
2562         }
2563 
2564         /* Now set the Unicode environment as the environment string pointer */
2565         lpEnvironment = UnicodeEnv.Buffer;
2566     }
2567 
2568     /* Make a copy of the caller's startup info since we'll modify it */
2569     StartupInfo = *lpStartupInfo;
2570 
2571     /* Check if private data is being sent on the same channel as std handles */
2572     if ((StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
2573         (StartupInfo.dwFlags & (STARTF_USEHOTKEY | STARTF_SHELLPRIVATE)))
2574     {
2575         /* Cannot use the std handles since we have monitor/hotkey values */
2576         StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES;
2577     }
2578 
2579     /* If there's a debugger, or we have to launch cmd.exe, we go back here */
2580 AppNameRetry:
2581     /* New iteration -- free any existing name buffer */
2582     if (NameBuffer)
2583     {
2584         RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
2585         NameBuffer = NULL;
2586     }
2587 
2588     /* New iteration -- free any existing free buffer */
2589     if (FreeBuffer)
2590     {
2591         RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
2592         FreeBuffer = NULL;
2593     }
2594 
2595     /* New iteration -- close any existing file handle */
2596     if (FileHandle)
2597     {
2598         NtClose(FileHandle);
2599         FileHandle = NULL;
2600     }
2601 
2602     /* Set the initial parsing state. This code can loop -- don't move this! */
2603     ErrorCode = 0;
2604     SearchRetry = TRUE;
2605     QuotesNeeded = FALSE;
2606     CmdLineIsAppName = FALSE;
2607 
2608     /* First check if we don't have an application name */
2609     if (!lpApplicationName)
2610     {
2611         /* This should be the first time we attempt creating one */
2612         ASSERT(NameBuffer == NULL);
2613 
2614         /* Allocate a buffer to hold it */
2615         NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
2616                                      0,
2617                                      MAX_PATH * sizeof(WCHAR));
2618         if (!NameBuffer)
2619         {
2620             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2621             Result = FALSE;
2622             goto Quickie;
2623         }
2624 
2625         /* Initialize the application name and our parsing parameters */
2626         lpApplicationName = NullBuffer = ScanString = lpCommandLine;
2627 
2628         /* Check for an initial quote*/
2629         if (*lpCommandLine == L'\"')
2630         {
2631             /* We found a quote, keep searching for another one */
2632             SearchRetry = FALSE;
2633             ScanString++;
2634             lpApplicationName = ScanString;
2635             while (*ScanString)
2636             {
2637                 /* Have we found the terminating quote? */
2638                 if (*ScanString == L'\"')
2639                 {
2640                     /* We're done, get out of here */
2641                     NullBuffer = ScanString;
2642                     HasQuotes = TRUE;
2643                     break;
2644                 }
2645 
2646                 /* Keep searching for the quote */
2647                 ScanString++;
2648                 NullBuffer = ScanString;
2649             }
2650         }
2651         else
2652         {
2653 StartScan:
2654             /* We simply make the application name be the command line*/
2655             lpApplicationName = lpCommandLine;
2656             while (*ScanString)
2657             {
2658                 /* Check if it starts with a space or tab */
2659                 if ((*ScanString == L' ') || (*ScanString == L'\t'))
2660                 {
2661                     /* Break out of the search loop */
2662                     NullBuffer = ScanString;
2663                     break;
2664                 }
2665 
2666                 /* Keep searching for a space or tab */
2667                 ScanString++;
2668                 NullBuffer = ScanString;
2669             }
2670         }
2671 
2672         /* We have found the end of the application name, terminate it */
2673         SaveChar = *NullBuffer;
2674         *NullBuffer = UNICODE_NULL;
2675 
2676         /* New iteration -- free any existing saved path */
2677         if (SearchPath)
2678         {
2679             RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
2680             SearchPath = NULL;
2681         }
2682 
2683         /* Now compute the final EXE path based on the name */
2684         SearchPath = BaseComputeProcessExePath((LPWSTR)lpApplicationName);
2685         DPRINT("Search Path: %S\n", SearchPath);
2686         if (!SearchPath)
2687         {
2688             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2689             Result = FALSE;
2690             goto Quickie;
2691         }
2692 
2693         /* And search for the executable in the search path */
2694         Length = SearchPathW(SearchPath,
2695                              lpApplicationName,
2696                              L".exe",
2697                              MAX_PATH,
2698                              NameBuffer,
2699                              NULL);
2700 
2701         /* Did we find it? */
2702         if ((Length) && (Length < MAX_PATH))
2703         {
2704             /* Get file attributes */
2705             FileAttribs = GetFileAttributesW(NameBuffer);
2706             if ((FileAttribs != INVALID_FILE_ATTRIBUTES) &&
2707                 (FileAttribs & FILE_ATTRIBUTE_DIRECTORY))
2708             {
2709                 /* This was a directory, fail later on */
2710                 Length = 0;
2711             }
2712             else
2713             {
2714                 /* It's a file! */
2715                 Length++;
2716             }
2717         }
2718 
2719         DPRINT("Length: %lu Buffer: %S\n", Length, NameBuffer);
2720 
2721         /* Check if there was a failure in SearchPathW */
2722         if ((Length) && (Length < MAX_PATH))
2723         {
2724             /* Everything looks good, restore the name */
2725             *NullBuffer = SaveChar;
2726             lpApplicationName = NameBuffer;
2727         }
2728         else
2729         {
2730             /* Check if this was a relative path, which would explain it */
2731             PathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2732             if (PathType != RtlPathTypeRelative)
2733             {
2734                 /* This should fail, and give us a detailed LastError */
2735                 FileHandle = CreateFileW(lpApplicationName,
2736                                          GENERIC_READ,
2737                                          FILE_SHARE_READ |
2738                                          FILE_SHARE_WRITE,
2739                                          NULL,
2740                                          OPEN_EXISTING,
2741                                          FILE_ATTRIBUTE_NORMAL,
2742                                          NULL);
2743                 if (FileHandle != INVALID_HANDLE_VALUE)
2744                 {
2745                     /* It worked? Return a generic error */
2746                     CloseHandle(FileHandle);
2747                     FileHandle = NULL;
2748                     BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2749                 }
2750             }
2751             else
2752             {
2753                 /* Path was absolute, which means it doesn't exist */
2754                 BaseSetLastNTError(STATUS_OBJECT_NAME_NOT_FOUND);
2755             }
2756 
2757             /* Did we already fail once? */
2758             if (ErrorCode)
2759             {
2760                 /* Set the error code */
2761                 SetLastError(ErrorCode);
2762             }
2763             else
2764             {
2765                 /* Not yet, cache it */
2766                 ErrorCode = GetLastError();
2767             }
2768 
2769             /* Put back the command line */
2770             *NullBuffer = SaveChar;
2771             lpApplicationName = NameBuffer;
2772 
2773             /* It's possible there's whitespace in the directory name */
2774             if (!(*ScanString) || !(SearchRetry))
2775             {
2776                 /* Not the case, give up completely */
2777                 Result = FALSE;
2778                 goto Quickie;
2779             }
2780 
2781             /* There are spaces, so keep trying the next possibility */
2782             ScanString++;
2783             NullBuffer = ScanString;
2784 
2785             /* We will have to add a quote, since there is a space */
2786             QuotesNeeded = TRUE;
2787             HasQuotes = TRUE;
2788             goto StartScan;
2789         }
2790     }
2791     else if (!(lpCommandLine) || !(*lpCommandLine))
2792     {
2793         /* We don't have a command line, so just use the application name */
2794         CmdLineIsAppName = TRUE;
2795         lpCommandLine = (LPWSTR)lpApplicationName;
2796     }
2797 
2798     /* Convert the application name to its NT path */
2799     TranslationStatus = RtlDosPathNameToRelativeNtPathName_U(lpApplicationName,
2800                                                              &PathName,
2801                                                              NULL,
2802                                                              &SxsWin32RelativePath);
2803     if (!TranslationStatus)
2804     {
2805         /* Path must be invalid somehow, bail out */
2806         DPRINT1("Path translation for SxS failed\n");
2807         SetLastError(ERROR_PATH_NOT_FOUND);
2808         Result = FALSE;
2809         goto Quickie;
2810     }
2811 
2812     /* Setup the buffer that needs to be freed at the end */
2813     ASSERT(FreeBuffer == NULL);
2814     FreeBuffer = PathName.Buffer;
2815 
2816     /* Check what kind of path the application is, for SxS (Fusion) purposes */
2817     RtlInitUnicodeString(&SxsWin32ExePath, lpApplicationName);
2818     SxsPathType = RtlDetermineDosPathNameType_U(lpApplicationName);
2819     if ((SxsPathType != RtlPathTypeDriveAbsolute) &&
2820         (SxsPathType != RtlPathTypeLocalDevice) &&
2821         (SxsPathType != RtlPathTypeRootLocalDevice) &&
2822         (SxsPathType != RtlPathTypeUncAbsolute))
2823     {
2824         /* Relative-type path, get the full path */
2825         RtlInitEmptyUnicodeString(&PathBufferString, NULL, 0);
2826         Status = RtlGetFullPathName_UstrEx(&SxsWin32ExePath,
2827                                            NULL,
2828                                            &PathBufferString,
2829                                            NULL,
2830                                            NULL,
2831                                            NULL,
2832                                            &SxsPathType,
2833                                            NULL);
2834         if (!NT_SUCCESS(Status))
2835         {
2836             /* Fail the rest of the create */
2837             RtlReleaseRelativeName(&SxsWin32RelativePath);
2838             BaseSetLastNTError(Status);
2839             Result = FALSE;
2840             goto Quickie;
2841         }
2842 
2843         /* Use this full path as the SxS path */
2844         SxsWin32ExePath = PathBufferString;
2845         PathBuffer = PathBufferString.Buffer;
2846         PathBufferString.Buffer = NULL;
2847         DPRINT("SxS Path: %S\n", PathBuffer);
2848     }
2849 
2850     /* Also set the .EXE path based on the path name */
2851 #if _SXS_SUPPORT_ENABLED_
2852     SxsNtExePath = PathName;
2853 #endif
2854     if (SxsWin32RelativePath.RelativeName.Length)
2855     {
2856         /* If it's relative, capture the relative name */
2857         PathName = SxsWin32RelativePath.RelativeName;
2858     }
2859     else
2860     {
2861         /* Otherwise, it's absolute, make sure no relative dir is used */
2862         SxsWin32RelativePath.ContainingDirectory = NULL;
2863     }
2864 
2865     /* Now use the path name, and the root path, to try opening the app */
2866     DPRINT("Path: %wZ. Dir: %p\n", &PathName, SxsWin32RelativePath.ContainingDirectory);
2867     InitializeObjectAttributes(&LocalObjectAttributes,
2868                                &PathName,
2869                                OBJ_CASE_INSENSITIVE,
2870                                SxsWin32RelativePath.ContainingDirectory,
2871                                NULL);
2872     Status = NtOpenFile(&FileHandle,
2873                         SYNCHRONIZE |
2874                         FILE_READ_ATTRIBUTES |
2875                         FILE_READ_DATA |
2876                         FILE_EXECUTE,
2877                         &LocalObjectAttributes,
2878                         &IoStatusBlock,
2879                         FILE_SHARE_READ | FILE_SHARE_DELETE,
2880                         FILE_SYNCHRONOUS_IO_NONALERT |
2881                         FILE_NON_DIRECTORY_FILE);
2882     if (!NT_SUCCESS(Status))
2883     {
2884         /* Try to open the app just for execute purposes instead */
2885         Status = NtOpenFile(&FileHandle,
2886                             SYNCHRONIZE | FILE_EXECUTE,
2887                             &LocalObjectAttributes,
2888                             &IoStatusBlock,
2889                             FILE_SHARE_READ | FILE_SHARE_DELETE,
2890                             FILE_SYNCHRONOUS_IO_NONALERT |
2891                             FILE_NON_DIRECTORY_FILE);
2892     }
2893 
2894     /* Failure path, display which file failed to open */
2895     if (!NT_SUCCESS(Status))
2896         DPRINT1("Open file failed: %lx (%wZ)\n", Status, &PathName);
2897 
2898     /* Cleanup in preparation for failure or success */
2899     RtlReleaseRelativeName(&SxsWin32RelativePath);
2900 
2901     if (!NT_SUCCESS(Status))
2902     {
2903         /* Failure path, try to understand why */
2904         if (RtlIsDosDeviceName_U(lpApplicationName))
2905         {
2906             /* If a device is being executed, return this special error code */
2907             SetLastError(ERROR_BAD_DEVICE);
2908             Result = FALSE;
2909             goto Quickie;
2910         }
2911         else
2912         {
2913             /* Otherwise return the converted NT error code */
2914             BaseSetLastNTError(Status);
2915             Result = FALSE;
2916             goto Quickie;
2917         }
2918     }
2919 
2920     /* Did the caller specify a desktop? */
2921     if (!StartupInfo.lpDesktop)
2922     {
2923         /* Use the one from the current process */
2924         StartupInfo.lpDesktop = Peb->ProcessParameters->DesktopInfo.Buffer;
2925     }
2926 
2927     /* Create a section for this file */
2928     Status = NtCreateSection(&SectionHandle,
2929                              SECTION_ALL_ACCESS,
2930                              NULL,
2931                              NULL,
2932                              PAGE_EXECUTE,
2933                              SEC_IMAGE,
2934                              FileHandle);
2935     DPRINT("Section status: %lx\n", Status);
2936     if (NT_SUCCESS(Status))
2937     {
2938         /* Are we running on Windows Embedded, Datacenter, Blade or Starter? */
2939         if (SharedUserData->SuiteMask & (VER_SUITE_EMBEDDEDNT |
2940                                          VER_SUITE_DATACENTER |
2941                                          VER_SUITE_PERSONAL |
2942                                          VER_SUITE_BLADE))
2943         {
2944             /* These SKUs do not allow running certain applications */
2945             Status = BasepCheckWebBladeHashes(FileHandle);
2946             if (Status == STATUS_ACCESS_DENIED)
2947             {
2948                 /* And this is one of them! */
2949                 DPRINT1("Invalid Blade hashes!\n");
2950                 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE);
2951                 Result = FALSE;
2952                 goto Quickie;
2953             }
2954 
2955             /* Did we get some other failure? */
2956             if (!NT_SUCCESS(Status))
2957             {
2958                 /* If we couldn't check the hashes, assume nefariousness */
2959                 DPRINT1("Tampered Blade hashes!\n");
2960                 SetLastError(ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER);
2961                 Result = FALSE;
2962                 goto Quickie;
2963             }
2964         }
2965 
2966         /* Now do Winsafer, etc, checks */
2967         Status = BasepIsProcessAllowed((LPWSTR)lpApplicationName);
2968         if (!NT_SUCCESS(Status))
2969         {
2970             /* Fail if we're not allowed to launch the process */
2971             DPRINT1("Process not allowed to launch: %lx\n", Status);
2972             BaseSetLastNTError(Status);
2973             if (SectionHandle)
2974             {
2975                 NtClose(SectionHandle);
2976                 SectionHandle = NULL;
2977             }
2978             Result = FALSE;
2979             goto Quickie;
2980         }
2981 
2982         /* Is a DOS VDM being forced, but we already have a WOW32 instance ready? */
2983         if ((dwCreationFlags & CREATE_FORCEDOS) &&
2984             (BaseStaticServerData->IsWowTaskReady))
2985         {
2986             /* This request can't be satisfied, instead, a separate VDM is needed */
2987             dwCreationFlags &= ~(CREATE_FORCEDOS | CREATE_SHARED_WOW_VDM);
2988             dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
2989 
2990             /* Set a failure code, ask for VDM reservation */
2991             Status = STATUS_INVALID_IMAGE_WIN_16;
2992             UseVdmReserve = TRUE;
2993 
2994             /* Close the current handle */
2995             NtClose(SectionHandle);
2996             SectionHandle = NULL;
2997 
2998             /* Don't query the section later */
2999             QuerySection = FALSE;
3000         }
3001     }
3002 
3003     /* Did we already do these checks? */
3004     if (!SkipSaferAndAppCompat)
3005     {
3006         /* Is everything OK so far, OR do we have an non-MZ, non-DOS app? */
3007         if ((NT_SUCCESS(Status)) ||
3008             ((Status == STATUS_INVALID_IMAGE_NOT_MZ) &&
3009             !(BaseIsDosApplication(&PathName, Status))))
3010         {
3011             /* Clear the machine type in case of failure */
3012             ImageMachine = 0;
3013 
3014             /* Clean any app compat data that may have accumulated */
3015             BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
3016             AppCompatData = NULL;
3017             AppCompatSxsData = NULL;
3018 
3019             /* Do we have a section? */
3020             if (SectionHandle)
3021             {
3022                 /* Have we already queried it? */
3023                 if (QuerySection)
3024                 {
3025                     /* Nothing to do */
3026                     AppCompatStatus = STATUS_SUCCESS;
3027                 }
3028                 else
3029                 {
3030                     /* Get some information about the executable */
3031                     AppCompatStatus = NtQuerySection(SectionHandle,
3032                                             SectionImageInformation,
3033                                             &ImageInformation,
3034                                             sizeof(ImageInformation),
3035                                             NULL);
3036                 }
3037 
3038                 /* Do we have section information now? */
3039                 if (NT_SUCCESS(AppCompatStatus))
3040                 {
3041                     /* Don't ask for it again, save the machine type */
3042                     QuerySection = TRUE;
3043                     ImageMachine = ImageInformation.Machine;
3044                 }
3045             }
3046 
3047             /* Is there a reason/Shim we shouldn't run this application? */
3048             AppCompatStatus = BasepCheckBadapp(FileHandle,
3049                                       FreeBuffer,
3050                                       lpEnvironment,
3051                                       ImageMachine,
3052                                       &AppCompatData,
3053                                       &AppCompatDataSize,
3054                                       &AppCompatSxsData,
3055                                       &AppCompatSxsDataSize,
3056                                       &FusionFlags);
3057             if (!NT_SUCCESS(AppCompatStatus))
3058             {
3059                 /* This is usually the status we get back */
3060                 DPRINT1("App compat launch failure: %lx\n", AppCompatStatus);
3061                 if (AppCompatStatus == STATUS_ACCESS_DENIED)
3062                 {
3063                     /* Convert it to something more Win32-specific */
3064                     SetLastError(ERROR_CANCELLED);
3065                 }
3066                 else
3067                 {
3068                     /* Some other error */
3069                     BaseSetLastNTError(AppCompatStatus);
3070                 }
3071 
3072                 /* Did we have a section? */
3073                 if (SectionHandle)
3074                 {
3075                     /* Clean it up */
3076                     NtClose(SectionHandle);
3077                     SectionHandle = NULL;
3078                 }
3079 
3080                 /* Fail the call */
3081                 Result = FALSE;
3082                 goto Quickie;
3083             }
3084         }
3085     }
3086 
3087     //ASSERT((dwFusionFlags & ~SXS_APPCOMPACT_FLAG_APP_RUNNING_SAFEMODE) == 0);
3088 
3089     /* Have we already done, and do we need to do, SRP (WinSafer) checks? */
3090     if (!(SkipSaferAndAppCompat) &&
3091         ~(dwCreationFlags & CREATE_PRESERVE_CODE_AUTHZ_LEVEL))
3092     {
3093         /* Assume yes */
3094         SaferNeeded = TRUE;
3095         switch (Status)
3096         {
3097             case STATUS_INVALID_IMAGE_NE_FORMAT:
3098             case STATUS_INVALID_IMAGE_PROTECT:
3099             case STATUS_INVALID_IMAGE_WIN_16:
3100             case STATUS_FILE_IS_OFFLINE:
3101                 /* For all DOS, 16-bit, OS/2 images, we do*/
3102                 break;
3103 
3104             case STATUS_INVALID_IMAGE_NOT_MZ:
3105                 /* For invalid files, we don't, unless it's a .BAT file */
3106                 if (BaseIsDosApplication(&PathName, Status)) break;
3107 
3108             default:
3109                 /* Any other error codes we also don't */
3110                 if (!NT_SUCCESS(Status))
3111                 {
3112                     SaferNeeded = FALSE;
3113                 }
3114 
3115                 /* But for success, we do */
3116                 break;
3117         }
3118 
3119         /* Okay, so what did the checks above result in? */
3120         if (SaferNeeded)
3121         {
3122             /* We have to call into the WinSafer library and actually check */
3123             SaferStatus = BasepCheckWinSaferRestrictions(hUserToken,
3124                                                     (LPWSTR)lpApplicationName,
3125                                                     FileHandle,
3126                                                     &InJob,
3127                                                     &TokenHandle,
3128                                                     &JobHandle);
3129             if (SaferStatus == 0xFFFFFFFF)
3130             {
3131                 /* Back in 2003, they didn't have an NTSTATUS for this... */
3132                 DPRINT1("WinSafer blocking process launch\n");
3133                 SetLastError(ERROR_ACCESS_DISABLED_BY_POLICY);
3134                 Result = FALSE;
3135                 goto Quickie;
3136             }
3137 
3138             /* Other status codes are not-Safer related, just convert them */
3139             if (!NT_SUCCESS(SaferStatus))
3140             {
3141                 DPRINT1("Error checking WinSafer: %lx\n", SaferStatus);
3142                 BaseSetLastNTError(SaferStatus);
3143                 Result = FALSE;
3144                 goto Quickie;
3145             }
3146         }
3147     }
3148 
3149     /* The last step is to figure out why the section object was not created */
3150     switch (Status)
3151     {
3152         case STATUS_INVALID_IMAGE_WIN_16:
3153         {
3154             /* 16-bit binary. Should we use WOW or does the caller force VDM? */
3155             if (!(dwCreationFlags & CREATE_FORCEDOS))
3156             {
3157                 /* Remember that we're launching WOW */
3158                 IsWowApp = TRUE;
3159 
3160                 /* Create the VDM environment, it's valid for WOW too */
3161                 Result = BaseCreateVDMEnvironment(lpEnvironment,
3162                                                   &VdmAnsiEnv,
3163                                                   &VdmUnicodeEnv);
3164                 if (!Result)
3165                 {
3166                     DPRINT1("VDM environment for WOW app failed\n");
3167                     goto Quickie;
3168                 }
3169 
3170                 /* We're going to try this twice, so do a loop */
3171                 while (TRUE)
3172                 {
3173                     /* Pick which kind of WOW mode we want to run in */
3174                     VdmBinaryType = (dwCreationFlags &
3175                                      CREATE_SEPARATE_WOW_VDM) ?
3176                                      BINARY_TYPE_SEPARATE_WOW : BINARY_TYPE_WOW;
3177 
3178                     /* Get all the VDM settings and current status */
3179                     Status = BaseCheckVDM(VdmBinaryType,
3180                                           lpApplicationName,
3181                                           lpCommandLine,
3182                                           lpCurrentDirectory,
3183                                           &VdmAnsiEnv,
3184                                           &CsrMsg[1],
3185                                           &VdmTask,
3186                                           dwCreationFlags,
3187                                           &StartupInfo,
3188                                           hUserToken);
3189 
3190                     /* If it worked, no need to try again */
3191                     if (NT_SUCCESS(Status)) break;
3192 
3193                     /* Check if it's disallowed or if it's our second time */
3194                     BaseSetLastNTError(Status);
3195                     if ((Status == STATUS_VDM_DISALLOWED) ||
3196                         (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW) ||
3197                         (GetLastError() == ERROR_ACCESS_DENIED))
3198                     {
3199                         /* Fail the call -- we won't try again */
3200                         DPRINT1("VDM message failure for WOW: %lx\n", Status);
3201                         Result = FALSE;
3202                         goto Quickie;
3203                     }
3204 
3205                     /* Try one more time, but with a separate WOW instance */
3206                     dwCreationFlags |= CREATE_SEPARATE_WOW_VDM;
3207                 }
3208 
3209                 /* Check which VDM state we're currently in */
3210                 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED |
3211                                                  VDM_NOT_READY |
3212                                                  VDM_READY))
3213                 {
3214                     case VDM_NOT_LOADED:
3215                         /* VDM is not fully loaded, so not that much to undo */
3216                         VdmUndoLevel = VDM_UNDO_PARTIAL;
3217 
3218                         /* Reset VDM reserve if needed */
3219                         if (UseVdmReserve) VdmReserve = 1;
3220 
3221                         /* Get the required parameters and names for launch */
3222                         Result = BaseGetVdmConfigInfo(lpCommandLine,
3223                                                       VdmTask,
3224                                                       VdmBinaryType,
3225                                                       &VdmString,
3226                                                       &VdmReserve);
3227                         if (!Result)
3228                         {
3229                             DPRINT1("VDM Configuration failed for WOW\n");
3230                             BaseSetLastNTError(Status);
3231                             goto Quickie;
3232                         }
3233 
3234                         /* Update the command-line with the VDM one instead */
3235                         lpCommandLine = VdmString.Buffer;
3236                         lpApplicationName = NULL;
3237 
3238                         /* We don't want a console, detachment, nor a window */
3239                         dwCreationFlags |= CREATE_NO_WINDOW;
3240                         dwCreationFlags &= ~(CREATE_NEW_CONSOLE | DETACHED_PROCESS);
3241 
3242                         /* Force feedback on */
3243                         StartupInfo.dwFlags |= STARTF_FORCEONFEEDBACK;
3244                         break;
3245 
3246 
3247                     case VDM_READY:
3248                         /* VDM is ready, so we have to undo everything */
3249                         VdmUndoLevel = VDM_UNDO_REUSE;
3250 
3251                         /* Check if CSRSS wants us to wait on VDM */
3252                         VdmWaitObject = CheckVdmMsg->WaitObjectForParent;
3253                         break;
3254 
3255                     case VDM_NOT_READY:
3256                         /* Something is wrong with VDM, we'll fail the call */
3257                         DPRINT1("VDM is not ready for WOW\n");
3258                         SetLastError(ERROR_NOT_READY);
3259                         Result = FALSE;
3260                         goto Quickie;
3261 
3262                     default:
3263                         break;
3264                 }
3265 
3266                 /* Since to get NULL, we allocate from 0x1, account for this */
3267                 VdmReserve--;
3268 
3269                 /* This implies VDM is ready, so skip everything else */
3270                 if (VdmWaitObject) goto VdmShortCircuit;
3271 
3272                 /* Don't inherit handles since we're doing VDM now */
3273                 bInheritHandles = FALSE;
3274 
3275                 /* Had the user passed in environment? If so, destroy it */
3276                 if ((lpEnvironment) &&
3277                     !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3278                 {
3279                     RtlDestroyEnvironment(lpEnvironment);
3280                 }
3281 
3282                 /* We've already done all these checks, don't do them again */
3283                 SkipSaferAndAppCompat = TRUE;
3284                 goto AppNameRetry;
3285             }
3286 
3287             // There is no break here on purpose, so FORCEDOS drops down!
3288         }
3289 
3290         case STATUS_INVALID_IMAGE_PROTECT:
3291         case STATUS_INVALID_IMAGE_NOT_MZ:
3292         case STATUS_INVALID_IMAGE_NE_FORMAT:
3293         {
3294             /* We're launching an executable application */
3295             BinarySubType = BINARY_TYPE_EXE;
3296 
3297             /* We can drop here from other "cases" above too, so check */
3298             if ((Status == STATUS_INVALID_IMAGE_PROTECT) ||
3299                 (Status == STATUS_INVALID_IMAGE_NE_FORMAT) ||
3300                 (BinarySubType = BaseIsDosApplication(&PathName, Status)))
3301             {
3302                 /* We're launching a DOS application */
3303                 VdmBinaryType = BINARY_TYPE_DOS;
3304 
3305                 /* Based on the caller environment, create a VDM one */
3306                 Result = BaseCreateVDMEnvironment(lpEnvironment,
3307                                                   &VdmAnsiEnv,
3308                                                   &VdmUnicodeEnv);
3309                 if (!Result)
3310                 {
3311                     DPRINT1("VDM environment for DOS failed\n");
3312                     goto Quickie;
3313                 }
3314 
3315                 /* Check the current state of the VDM subsystem */
3316                 Status = BaseCheckVDM(VdmBinaryType | BinarySubType,
3317                                       lpApplicationName,
3318                                       lpCommandLine,
3319                                       lpCurrentDirectory,
3320                                       &VdmAnsiEnv,
3321                                       &CsrMsg[1],
3322                                       &VdmTask,
3323                                       dwCreationFlags,
3324                                       &StartupInfo,
3325                                       NULL);
3326                 if (!NT_SUCCESS(Status))
3327                 {
3328                     /* Failed to inquire about VDM, fail the call */
3329                     DPRINT1("VDM message failure for DOS: %lx\n", Status);
3330                     BaseSetLastNTError(Status);
3331                     Result = FALSE;
3332                     goto Quickie;
3333                 };
3334 
3335                 /* Handle possible VDM states */
3336                 switch (CheckVdmMsg->VDMState & (VDM_NOT_LOADED |
3337                                                  VDM_NOT_READY |
3338                                                  VDM_READY))
3339                 {
3340                     case VDM_NOT_LOADED:
3341                         /* If VDM is not loaded, we'll do a partial undo */
3342                         VdmUndoLevel = VDM_UNDO_PARTIAL;
3343 
3344                         /* A VDM process can't also be detached, so fail */
3345                         if (dwCreationFlags & DETACHED_PROCESS)
3346                         {
3347                             DPRINT1("Detached process but no VDM, not allowed\n");
3348                             SetLastError(ERROR_ACCESS_DENIED);
3349                             return FALSE;
3350                         }
3351 
3352                         /* Get the required parameters and names for launch */
3353                         Result = BaseGetVdmConfigInfo(lpCommandLine,
3354                                                       VdmTask,
3355                                                       VdmBinaryType,
3356                                                       &VdmString,
3357                                                       &VdmReserve);
3358                         if (!Result)
3359                         {
3360                             DPRINT1("VDM Configuration failed for DOS\n");
3361                             BaseSetLastNTError(Status);
3362                             goto Quickie;
3363                         }
3364 
3365                         /* Update the command-line to launch VDM instead */
3366                         lpCommandLine = VdmString.Buffer;
3367                         lpApplicationName = NULL;
3368                         break;
3369 
3370                     case VDM_READY:
3371                         /* VDM is ready, so we have to undo everything */
3372                         VdmUndoLevel = VDM_UNDO_REUSE;
3373 
3374                         /* Check if CSRSS wants us to wait on VDM */
3375                         VdmWaitObject = CheckVdmMsg->WaitObjectForParent;
3376                         break;
3377 
3378                     case VDM_NOT_READY:
3379                         /* Something is wrong with VDM, we'll fail the call */
3380                         DPRINT1("VDM is not ready for DOS\n");
3381                         SetLastError(ERROR_NOT_READY);
3382                         Result = FALSE;
3383                         goto Quickie;
3384 
3385                     default:
3386                         break;
3387                 }
3388 
3389                 /* Since to get NULL, we allocate from 0x1, account for this */
3390                 VdmReserve--;
3391 
3392                 /* This implies VDM is ready, so skip everything else */
3393                 if (VdmWaitObject) goto VdmShortCircuit;
3394 
3395                 /* Don't inherit handles since we're doing VDM now */
3396                 bInheritHandles = FALSE;
3397 
3398                 /* Had the user passed in environment? If so, destroy it */
3399                 if ((lpEnvironment) &&
3400                     !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
3401                 {
3402                     RtlDestroyEnvironment(lpEnvironment);
3403                 }
3404 
3405                 /* Use our VDM Unicode environment instead */
3406                 lpEnvironment = VdmUnicodeEnv.Buffer;
3407             }
3408             else
3409             {
3410                 /* It's a batch file, get the extension */
3411                 ExtBuffer = &PathName.Buffer[PathName.Length / sizeof(WCHAR) - 4];
3412 
3413                 /* Make sure the extensions are correct */
3414                 if ((PathName.Length < (4 * sizeof(WCHAR))) ||
3415                     ((_wcsnicmp(ExtBuffer, L".bat", 4)) &&
3416                      (_wcsnicmp(ExtBuffer, L".cmd", 4))))
3417                 {
3418                     DPRINT1("'%wZ': Invalid EXE, and not a batch or script file\n", &PathName);
3419                     SetLastError(ERROR_BAD_EXE_FORMAT);
3420                     Result = FALSE;
3421                     goto Quickie;
3422                 }
3423 
3424                 /* Check if we need to account for quotes around the path */
3425                 CmdQuoteLength = CmdLineIsAppName || HasQuotes;
3426                 if (!CmdLineIsAppName)
3427                 {
3428                     if (HasQuotes) CmdQuoteLength++;
3429                 }
3430                 else
3431                 {
3432                     CmdQuoteLength++;
3433                 }
3434 
3435                 /* Calculate the length of the command line */
3436                 CmdLineLength = wcslen(lpCommandLine);
3437                 CmdLineLength += wcslen(CMD_STRING);
3438                 CmdLineLength += CmdQuoteLength + sizeof(ANSI_NULL);
3439                 CmdLineLength *= sizeof(WCHAR);
3440 
3441                 /* Allocate space for the new command line */
3442                 AnsiCmdCommand = RtlAllocateHeap(RtlGetProcessHeap(),
3443                                                  0,
3444                                                  CmdLineLength);
3445                 if (!AnsiCmdCommand)
3446                 {
3447                     BaseSetLastNTError(STATUS_NO_MEMORY);
3448                     Result = FALSE;
3449                     goto Quickie;
3450                 }
3451 
3452                 /* Build it */
3453                 wcscpy(AnsiCmdCommand, CMD_STRING);
3454                 if ((CmdLineIsAppName) || (HasQuotes))
3455                 {
3456                     wcscat(AnsiCmdCommand, L"\"");
3457                 }
3458                 wcscat(AnsiCmdCommand, lpCommandLine);
3459                 if ((CmdLineIsAppName) || (HasQuotes))
3460                 {
3461                     wcscat(AnsiCmdCommand, L"\"");
3462                 }
3463 
3464                 /* Create it as a Unicode String */
3465                 RtlInitUnicodeString(&DebuggerString, AnsiCmdCommand);
3466 
3467                 /* Set the command line to this */
3468                 lpCommandLine = DebuggerString.Buffer;
3469                 lpApplicationName = NULL;
3470                 DPRINT1("Retrying with: %S\n", lpCommandLine);
3471             }
3472 
3473             /* We've already done all these checks, don't do them again */
3474             SkipSaferAndAppCompat = TRUE;
3475             goto AppNameRetry;
3476         }
3477 
3478         case STATUS_INVALID_IMAGE_WIN_64:
3479         {
3480             /* 64-bit binaries are not allowed to run on 32-bit ReactOS */
3481             DPRINT1("64-bit binary, failing\n");
3482             SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3483             Result = FALSE;
3484             goto Quickie;
3485         }
3486 
3487         case STATUS_FILE_IS_OFFLINE:
3488         {
3489             /* Set the correct last error for this */
3490             DPRINT1("File is offline, failing\n");
3491             SetLastError(ERROR_FILE_OFFLINE);
3492             break;
3493         }
3494 
3495         default:
3496         {
3497             /* Any other error, convert it to a generic Win32 error */
3498             if (!NT_SUCCESS(Status))
3499             {
3500                 DPRINT1("Failed to create section: %lx\n", Status);
3501                 SetLastError(ERROR_BAD_EXE_FORMAT);
3502                 Result = FALSE;
3503                 goto Quickie;
3504             }
3505 
3506             /* Otherwise, this must be success */
3507             ASSERT(Status == STATUS_SUCCESS);
3508             break;
3509         }
3510     }
3511 
3512     /* Is this not a WOW application, but a WOW32 VDM was requested for it? */
3513     if (!(IsWowApp) && (dwCreationFlags & CREATE_SEPARATE_WOW_VDM))
3514     {
3515         /* Ignore the nonsensical request */
3516         dwCreationFlags &= ~CREATE_SEPARATE_WOW_VDM;
3517     }
3518 
3519     /* Did we already check information for the section? */
3520     if (!QuerySection)
3521     {
3522         /* Get some information about the executable */
3523         Status = NtQuerySection(SectionHandle,
3524                                 SectionImageInformation,
3525                                 &ImageInformation,
3526                                 sizeof(ImageInformation),
3527                                 NULL);
3528         if (!NT_SUCCESS(Status))
3529         {
3530             /* We failed, bail out */
3531             DPRINT1("Section query failed\n");
3532             BaseSetLastNTError(Status);
3533             Result = FALSE;
3534             goto Quickie;
3535         }
3536 
3537         /* Don't check this later */
3538         QuerySection = TRUE;
3539     }
3540 
3541     /* Check if this was linked as a DLL */
3542     if (ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3543     {
3544         /* These aren't valid images to try to execute! */
3545         DPRINT1("Trying to launch a DLL, failing\n");
3546         SetLastError(ERROR_BAD_EXE_FORMAT);
3547         Result = FALSE;
3548         goto Quickie;
3549     }
3550 
3551     /* Don't let callers pass in this flag -- we'll only get it from IFEO */
3552     Flags &= ~PROCESS_CREATE_FLAGS_LARGE_PAGES;
3553 
3554     /* Clear the IFEO-missing flag, before we know for sure... */
3555     ParameterFlags &= ~2;
3556 
3557     /* If the process is being debugged, only read IFEO if the PEB says so */
3558     if (!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS)) ||
3559         (NtCurrentPeb()->ReadImageFileExecOptions))
3560     {
3561         /* Let's do this! Attempt to open IFEO */
3562         IFEOStatus = LdrOpenImageFileOptionsKey(&PathName, 0, &KeyHandle);
3563         if (!NT_SUCCESS(IFEOStatus))
3564         {
3565             /* We failed, set the flag so we store this in the parameters */
3566             if (IFEOStatus == STATUS_OBJECT_NAME_NOT_FOUND) ParameterFlags |= 2;
3567         }
3568         else
3569         {
3570             /* Was this our first time going through this path? */
3571             if (!DebuggerCmdLine)
3572             {
3573                 /* Allocate a buffer for the debugger path */
3574                 DebuggerCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
3575                                                   0,
3576                                                   MAX_PATH * sizeof(WCHAR));
3577                 if (!DebuggerCmdLine)
3578                 {
3579                     /* Close IFEO on failure */
3580                     IFEOStatus = NtClose(KeyHandle);
3581                     ASSERT(NT_SUCCESS(IFEOStatus));
3582 
3583                     /* Fail the call */
3584                     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3585                     Result = FALSE;
3586                     goto Quickie;
3587                 }
3588             }
3589 
3590             /* Now query for the debugger */
3591             IFEOStatus = LdrQueryImageFileKeyOption(KeyHandle,
3592                                                  L"Debugger",
3593                                                  REG_SZ,
3594                                                  DebuggerCmdLine,
3595                                                  MAX_PATH * sizeof(WCHAR),
3596                                                  &ResultSize);
3597             if (!(NT_SUCCESS(IFEOStatus)) ||
3598                 (ResultSize < sizeof(WCHAR)) ||
3599                 (DebuggerCmdLine[0] == UNICODE_NULL))
3600             {
3601                 /* If it's not there, or too small, or invalid, ignore it */
3602                 RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3603                 DebuggerCmdLine = NULL;
3604             }
3605 
3606             /* Also query if we should map with large pages */
3607             IFEOStatus = LdrQueryImageFileKeyOption(KeyHandle,
3608                                                  L"UseLargePages",
3609                                                  REG_DWORD,
3610                                                  &UseLargePages,
3611                                                  sizeof(UseLargePages),
3612                                                  NULL);
3613             if ((NT_SUCCESS(IFEOStatus)) && (UseLargePages))
3614             {
3615                 /* Do it! This is the only way this flag can be set */
3616                 Flags |= PROCESS_CREATE_FLAGS_LARGE_PAGES;
3617             }
3618 
3619             /* We're done with IFEO, can close it now */
3620             IFEOStatus = NtClose(KeyHandle);
3621             ASSERT(NT_SUCCESS(IFEOStatus));
3622         }
3623     }
3624 
3625     /* Make sure the image was compiled for this processor */
3626     if ((ImageInformation.Machine < SharedUserData->ImageNumberLow) ||
3627         (ImageInformation.Machine > SharedUserData->ImageNumberHigh))
3628     {
3629         /* It was not -- raise a hard error */
3630         ErrorResponse = ResponseOk;
3631         ErrorParameters[0] = (ULONG_PTR)&PathName;
3632         NtRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE,
3633                          1,
3634                          1,
3635                          ErrorParameters,
3636                          OptionOk,
3637                          &ErrorResponse);
3638         if (Peb->ImageSubsystemMajorVersion <= 3)
3639         {
3640             /* If it's really old, return this error */
3641             SetLastError(ERROR_BAD_EXE_FORMAT);
3642         }
3643         else
3644         {
3645             /* Otherwise, return a more modern error */
3646             SetLastError(ERROR_EXE_MACHINE_TYPE_MISMATCH);
3647         }
3648 
3649         /* Go to the failure path */
3650         DPRINT1("Invalid image architecture: %lx\n", ImageInformation.Machine);
3651         Result = FALSE;
3652         goto Quickie;
3653     }
3654 
3655     /* Check if this isn't a Windows image */
3656     if ((ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) &&
3657         (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI))
3658     {
3659         /* Get rid of section-related information since we'll retry */
3660         NtClose(SectionHandle);
3661         SectionHandle = NULL;
3662         QuerySection = FALSE;
3663 
3664         /* The only other non-Windows image type we support here is POSIX */
3665         if (ImageInformation.SubSystemType != IMAGE_SUBSYSTEM_POSIX_CUI)
3666         {
3667             /* Bail out if it's something else */
3668             SetLastError(ERROR_CHILD_NOT_COMPLETE);
3669             Result = FALSE;
3670             goto Quickie;
3671         }
3672 
3673         /* Now build the command-line to have posix launch this image */
3674         Result = BuildSubSysCommandLine(L"POSIX /P ",
3675                                         lpApplicationName,
3676                                         lpCommandLine,
3677                                         &DebuggerString);
3678         if (!Result)
3679         {
3680             /* Bail out if that failed */
3681             DPRINT1("Subsystem command line failed\n");
3682             goto Quickie;
3683         }
3684 
3685         /* And re-try launching the process, with the new command-line now */
3686         lpCommandLine = DebuggerString.Buffer;
3687         lpApplicationName = NULL;
3688 
3689         /* We've already done all these checks, don't do them again */
3690         SkipSaferAndAppCompat = TRUE;
3691         DPRINT1("Retrying with: %S\n", lpCommandLine);
3692         goto AppNameRetry;
3693     }
3694 
3695     /* Was this image built for a version of Windows whose images we can run? */
3696     Result = BasepIsImageVersionOk(ImageInformation.SubSystemMajorVersion,
3697                                    ImageInformation.SubSystemMinorVersion);
3698     if (!Result)
3699     {
3700         /* It was not, bail out */
3701         DPRINT1("Invalid subsystem version: %hu.%hu\n",
3702                 ImageInformation.SubSystemMajorVersion,
3703                 ImageInformation.SubSystemMinorVersion);
3704         SetLastError(ERROR_BAD_EXE_FORMAT);
3705         goto Quickie;
3706     }
3707 
3708     /* Check if there is a debugger associated with the application */
3709     if (DebuggerCmdLine)
3710     {
3711         /* Get the length of the command line */
3712         n = wcslen(lpCommandLine);
3713         if (!n)
3714         {
3715             /* There's no command line, use the application name instead */
3716             lpCommandLine = (LPWSTR)lpApplicationName;
3717             n = wcslen(lpCommandLine);
3718         }
3719 
3720         /* Protect against overflow */
3721         if (n > UNICODE_STRING_MAX_CHARS)
3722         {
3723             BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3724             Result = FALSE;
3725             goto Quickie;
3726         }
3727 
3728         /* Now add the length of the debugger command-line */
3729         n += wcslen(DebuggerCmdLine);
3730 
3731         /* Again make sure we don't overflow */
3732         if (n > UNICODE_STRING_MAX_CHARS)
3733         {
3734             BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3735             Result = FALSE;
3736             goto Quickie;
3737         }
3738 
3739         /* Account for the quotes and space between the two */
3740         n += sizeof("\" \"") - sizeof(ANSI_NULL);
3741 
3742         /* Convert to bytes, and make sure we don't overflow */
3743         n *= sizeof(WCHAR);
3744         if (n > UNICODE_STRING_MAX_BYTES)
3745         {
3746             BaseSetLastNTError(STATUS_NAME_TOO_LONG);
3747             Result = FALSE;
3748             goto Quickie;
3749         }
3750 
3751         /* Allocate space for the string */
3752         DebuggerString.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, n);
3753         if (!DebuggerString.Buffer)
3754         {
3755             SetLastError(ERROR_NOT_ENOUGH_MEMORY);
3756             Result = FALSE;
3757             goto Quickie;
3758         }
3759 
3760         /* Set the length */
3761         RtlInitEmptyUnicodeString(&DebuggerString,
3762                                   DebuggerString.Buffer,
3763                                   (USHORT)n);
3764 
3765         /* Now perform the command line creation */
3766         ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString,
3767                                                   DebuggerCmdLine);
3768         ASSERT(NT_SUCCESS(ImageDbgStatus));
3769         ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, L" ");
3770         ASSERT(NT_SUCCESS(ImageDbgStatus));
3771         ImageDbgStatus = RtlAppendUnicodeToString(&DebuggerString, lpCommandLine);
3772         ASSERT(NT_SUCCESS(ImageDbgStatus));
3773 
3774         /* Make sure it all looks nice */
3775         DbgPrint("BASE: Calling debugger with '%wZ'\n", &DebuggerString);
3776 
3777         /* Update the command line and application name */
3778         lpCommandLine = DebuggerString.Buffer;
3779         lpApplicationName = NULL;
3780 
3781         /* Close all temporary state */
3782         NtClose(SectionHandle);
3783         SectionHandle = NULL;
3784         QuerySection = FALSE;
3785 
3786         /* Free all temporary memory */
3787         RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
3788         NameBuffer = NULL;
3789         RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
3790         FreeBuffer = NULL;
3791         RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
3792         DebuggerCmdLine = NULL;
3793         DPRINT1("Retrying with: %S\n", lpCommandLine);
3794         goto AppNameRetry;
3795     }
3796 
3797     /* Initialize the process object attributes */
3798     ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3799                                                   lpProcessAttributes,
3800                                                   NULL);
3801     if ((hUserToken) && (lpProcessAttributes))
3802     {
3803         /* Augment them with information from the user */
3804 
3805         LocalProcessAttributes = *lpProcessAttributes;
3806         LocalProcessAttributes.lpSecurityDescriptor = NULL;
3807         ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
3808                                                       &LocalProcessAttributes,
3809                                                       NULL);
3810     }
3811 
3812     /* Check if we're going to be debugged */
3813     if (dwCreationFlags & DEBUG_PROCESS)
3814     {
3815         /* Set process flag */
3816         Flags |= PROCESS_CREATE_FLAGS_BREAKAWAY;
3817     }
3818 
3819     /* Check if we're going to be debugged */
3820     if (dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))
3821     {
3822         /* Connect to DbgUi */
3823         Status = DbgUiConnectToDbg();
3824         if (!NT_SUCCESS(Status))
3825         {
3826             DPRINT1("Failed to connect to DbgUI!\n");
3827             BaseSetLastNTError(Status);
3828             Result = FALSE;
3829             goto Quickie;
3830         }
3831 
3832         /* Get the debug object */
3833         DebugHandle = DbgUiGetThreadDebugObject();
3834 
3835         /* Check if only this process will be debugged */
3836         if (dwCreationFlags & DEBUG_ONLY_THIS_PROCESS)
3837         {
3838             /* Set process flag */
3839             Flags |= PROCESS_CREATE_FLAGS_NO_DEBUG_INHERIT;
3840         }
3841     }
3842 
3843     /* Set inherit flag */
3844     if (bInheritHandles) Flags |= PROCESS_CREATE_FLAGS_INHERIT_HANDLES;
3845 
3846     /* Check if the process should be created with large pages */
3847     HavePrivilege = FALSE;
3848     PrivilegeState = NULL;
3849     if (Flags & PROCESS_CREATE_FLAGS_LARGE_PAGES)
3850     {
3851         /* Acquire the required privilege so that the kernel won't fail the call */
3852         PrivilegeValue = SE_LOCK_MEMORY_PRIVILEGE;
3853         Status = RtlAcquirePrivilege(&PrivilegeValue, 1, 0, &PrivilegeState);
3854         if (NT_SUCCESS(Status))
3855         {
3856             /* Remember to release it later */
3857             HavePrivilege = TRUE;
3858         }
3859     }
3860 
3861     /* Save the current TIB value since kernel overwrites it to store PEB */
3862     TibValue = Teb->NtTib.ArbitraryUserPointer;
3863 
3864     /* Tell the kernel to create the process */
3865     Status = NtCreateProcessEx(&ProcessHandle,
3866                                PROCESS_ALL_ACCESS,
3867                                ObjectAttributes,
3868                                NtCurrentProcess(),
3869                                Flags,
3870                                SectionHandle,
3871                                DebugHandle,
3872                                NULL,
3873                                InJob);
3874 
3875     /* Load the PEB address from the hacky location where the kernel stores it */
3876     RemotePeb = Teb->NtTib.ArbitraryUserPointer;
3877 
3878     /* And restore the old TIB value */
3879     Teb->NtTib.ArbitraryUserPointer = TibValue;
3880 
3881     /* Release the large page privilege if we had acquired it */
3882     if (HavePrivilege) RtlReleasePrivilege(PrivilegeState);
3883 
3884     /* And now check if the kernel failed to create the process */
3885     if (!NT_SUCCESS(Status))
3886     {
3887         /* Go to failure path */
3888         DPRINT1("Failed to create process: %lx\n", Status);
3889         BaseSetLastNTError(Status);
3890         Result = FALSE;
3891         goto Quickie;
3892     }
3893 
3894     /* Check if there is a priority class to set */
3895     if (PriorityClass.PriorityClass)
3896     {
3897         /* Reset current privilege state */
3898         RealTimePrivilegeState = NULL;
3899 
3900         /* Is realtime priority being requested? */
3901         if (PriorityClass.PriorityClass == PROCESS_PRIORITY_CLASS_REALTIME)
3902         {
3903             /* Check if the caller has real-time access, and enable it if so */
3904             RealTimePrivilegeState = BasepIsRealtimeAllowed(TRUE);
3905         }
3906 
3907         /* Set the new priority class and release the privilege */
3908         Status = NtSetInformationProcess(ProcessHandle,
3909                                          ProcessPriorityClass,
3910                                          &PriorityClass,
3911                                          sizeof(PROCESS_PRIORITY_CLASS));
3912         if (RealTimePrivilegeState) RtlReleasePrivilege(RealTimePrivilegeState);
3913 
3914         /* Check if we failed to set the priority class */
3915         if (!NT_SUCCESS(Status))
3916         {
3917             /* Bail out on failure */
3918             DPRINT1("Failed to set priority class: %lx\n", Status);
3919             BaseSetLastNTError(Status);
3920             Result = FALSE;
3921             goto Quickie;
3922         }
3923     }
3924 
3925     /* Check if the caller wants the default error mode */
3926     if (dwCreationFlags & CREATE_DEFAULT_ERROR_MODE)
3927     {
3928         /* Set Error Mode to only fail on critical errors */
3929         HardErrorMode = SEM_FAILCRITICALERRORS;
3930         NtSetInformationProcess(ProcessHandle,
3931                                 ProcessDefaultHardErrorMode,
3932                                 &HardErrorMode,
3933                                 sizeof(ULONG));
3934     }
3935 
3936     /* Check if this was a VDM binary */
3937     if (VdmBinaryType)
3938     {
3939         /* Update VDM by telling it the process has now been created */
3940         VdmWaitObject = ProcessHandle;
3941         Result = BaseUpdateVDMEntry(VdmEntryUpdateProcess,
3942                                     &VdmWaitObject,
3943                                     VdmTask,
3944                                     VdmBinaryType);
3945 
3946         if (!Result)
3947         {
3948             /* Bail out on failure */
3949             DPRINT1("Failed to update VDM with wait object\n");
3950             VdmWaitObject = NULL;
3951             goto Quickie;
3952         }
3953 
3954         /* At this point, a failure means VDM has to undo all the state */
3955         VdmUndoLevel |= VDM_UNDO_FULL;
3956     }
3957 
3958     /* Check if VDM needed reserved low-memory */
3959     if (VdmReserve)
3960     {
3961         /* Reserve the requested allocation */
3962         RegionSize = VdmReserve;
3963         Status = NtAllocateVirtualMemory(ProcessHandle,
3964                                          &BaseAddress,
3965                                          0,
3966                                          &RegionSize,
3967                                          MEM_RESERVE,
3968                                          PAGE_EXECUTE_READWRITE);
3969         if (!NT_SUCCESS(Status))
3970         {
3971             /* Bail out on failure */
3972             DPRINT1("Failed to reserve memory for VDM: %lx\n", Status);
3973             BaseSetLastNTError(Status);
3974             Result = FALSE;
3975             goto Quickie;
3976         }
3977 
3978         VdmReserve = (ULONG)RegionSize;
3979     }
3980 
3981     /* Check if we've already queried information on the section */
3982     if (!QuerySection)
3983     {
3984         /* We haven't, so get some information about the executable */
3985         Status = NtQuerySection(SectionHandle,
3986                                 SectionImageInformation,
3987                                 &ImageInformation,
3988                                 sizeof(ImageInformation),
3989                                 NULL);
3990         if (!NT_SUCCESS(Status))
3991         {
3992             /* Bail out on failure */
3993             DPRINT1("Failed to query section: %lx\n", Status);
3994             BaseSetLastNTError(Status);
3995             Result = FALSE;
3996             goto Quickie;
3997         }
3998 
3999         /* If we encounter a restart, don't re-query this information again */
4000         QuerySection = TRUE;
4001     }
4002 
4003     /* Do we need to apply SxS to this image? */
4004     if (!(ImageInformation.DllCharacteristics & IMAGE_DLLCHARACTERISTICS_NO_ISOLATION))
4005     {
4006         /* Too bad, we don't support this yet */
4007         DPRINT1("Image should receive SxS Fusion Isolation\n");
4008     }
4009 
4010     /* There's some SxS flag that we need to set if fusion flags have 1 set */
4011     if (FusionFlags & 1) CreateProcessMsg->Sxs.Flags |= 0x10;
4012 
4013     /* Check if we have a current directory */
4014     if (lpCurrentDirectory)
4015     {
4016         /* Allocate a buffer so we can keep a Unicode copy */
4017         DPRINT("Current directory: %S\n", lpCurrentDirectory);
4018         CurrentDirectory = RtlAllocateHeap(RtlGetProcessHeap(),
4019                                            0,
4020                                            (MAX_PATH * sizeof(WCHAR)) +
4021                                            sizeof(UNICODE_NULL));
4022         if (!CurrentDirectory)
4023         {
4024             /* Bail out if this failed */
4025             BaseSetLastNTError(STATUS_NO_MEMORY);
4026             Result = FALSE;
4027             goto Quickie;
4028         }
4029 
4030         /* Get the length in Unicode */
4031         Length = GetFullPathNameW(lpCurrentDirectory,
4032                                   MAX_PATH,
4033                                   CurrentDirectory,
4034                                   &FilePart);
4035         if (Length > MAX_PATH)
4036         {
4037             /* The directory is too long, so bail out */
4038             SetLastError(ERROR_DIRECTORY);
4039             Result = FALSE;
4040             goto Quickie;
4041         }
4042 
4043         /* Make sure the directory is actually valid */
4044         FileAttribs = GetFileAttributesW(CurrentDirectory);
4045         if ((FileAttribs == INVALID_FILE_ATTRIBUTES) ||
4046            !(FileAttribs & FILE_ATTRIBUTE_DIRECTORY))
4047         {
4048             /* It isn't, so bail out */
4049             DPRINT1("Current directory is invalid\n");
4050             SetLastError(ERROR_DIRECTORY);
4051             Result = FALSE;
4052             goto Quickie;
4053         }
4054     }
4055 
4056     /* Insert quotes if needed */
4057     if ((QuotesNeeded) || (CmdLineIsAppName))
4058     {
4059         /* Allocate our buffer, plus enough space for quotes and a NULL */
4060         QuotedCmdLine = RtlAllocateHeap(RtlGetProcessHeap(),
4061                                         0,
4062                                         (wcslen(lpCommandLine) * sizeof(WCHAR)) +
4063                                         (2 * sizeof(L'\"') + sizeof(UNICODE_NULL)));
4064         if (QuotedCmdLine)
4065         {
4066             /* Copy the first quote */
4067             wcscpy(QuotedCmdLine, L"\"");
4068 
4069             /* Save the current null-character */
4070             if (QuotesNeeded)
4071             {
4072                 SaveChar = *NullBuffer;
4073                 *NullBuffer = UNICODE_NULL;
4074             }
4075 
4076             /* Copy the command line and the final quote */
4077             wcscat(QuotedCmdLine, lpCommandLine);
4078             wcscat(QuotedCmdLine, L"\"");
4079 
4080             /* Copy the null-char back */
4081             if (QuotesNeeded)
4082             {
4083                 *NullBuffer = SaveChar;
4084                 wcscat(QuotedCmdLine, NullBuffer);
4085             }
4086         }
4087         else
4088         {
4089             /* We can't put quotes around the thing, so try it anyway */
4090             if (QuotesNeeded) QuotesNeeded = FALSE;
4091             if (CmdLineIsAppName) CmdLineIsAppName = FALSE;
4092         }
4093     }
4094 
4095     /* Use isolation if needed */
4096     if (CreateProcessMsg->Sxs.Flags & 1) ParameterFlags |= 1;
4097 
4098     /* Set the new command-line if needed */
4099     if ((QuotesNeeded) || (CmdLineIsAppName)) lpCommandLine = QuotedCmdLine;
4100 
4101     /* Call the helper function in charge of RTL_USER_PROCESS_PARAMETERS */
4102     Result = BasePushProcessParameters(ParameterFlags,
4103                                        ProcessHandle,
4104                                        RemotePeb,
4105                                        lpApplicationName,
4106                                        CurrentDirectory,
4107                                        lpCommandLine,
4108                                        lpEnvironment,
4109                                        &StartupInfo,
4110                                        dwCreationFlags | NoWindow,
4111                                        bInheritHandles,
4112                                        IsWowApp ? IMAGE_SUBSYSTEM_WINDOWS_GUI: 0,
4113                                        AppCompatData,
4114                                        AppCompatDataSize);
4115     if (!Result)
4116     {
4117         /* The remote process would have an undefined state, so fail the call */
4118         DPRINT1("BasePushProcessParameters failed\n");
4119         goto Quickie;
4120     }
4121 
4122     /* Free the VDM command line string as it's no longer needed */
4123     RtlFreeUnicodeString(&VdmString);
4124     VdmString.Buffer = NULL;
4125 
4126     /* Non-VDM console applications usually inherit handles unless specified */
4127     if (!(VdmBinaryType) &&
4128         !(bInheritHandles) &&
4129         !(StartupInfo.dwFlags & STARTF_USESTDHANDLES) &&
4130         !(dwCreationFlags & (CREATE_NO_WINDOW |
4131                              CREATE_NEW_CONSOLE |
4132                              DETACHED_PROCESS)) &&
4133         (ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_CUI))
4134     {
4135         /* Get the remote parameters */
4136         Status = NtReadVirtualMemory(ProcessHandle,
4137                                      &RemotePeb->ProcessParameters,
4138                                      &ProcessParameters,
4139                                      sizeof(PRTL_USER_PROCESS_PARAMETERS),
4140                                      NULL);
4141         if (NT_SUCCESS(Status))
4142         {
4143             /* Duplicate standard input unless it's a console handle */
4144             if (!IsConsoleHandle(Peb->ProcessParameters->StandardInput))
4145             {
4146                 StuffStdHandle(ProcessHandle,
4147                                Peb->ProcessParameters->StandardInput,
4148                                &ProcessParameters->StandardInput);
4149             }
4150 
4151             /* Duplicate standard output unless it's a console handle */
4152             if (!IsConsoleHandle(Peb->ProcessParameters->StandardOutput))
4153             {
4154                 StuffStdHandle(ProcessHandle,
4155                                Peb->ProcessParameters->StandardOutput,
4156                                &ProcessParameters->StandardOutput);
4157             }
4158 
4159             /* Duplicate standard error unless it's a console handle */
4160             if (!IsConsoleHandle(Peb->ProcessParameters->StandardError))
4161             {
4162                 StuffStdHandle(ProcessHandle,
4163                                Peb->ProcessParameters->StandardError,
4164                                &ProcessParameters->StandardError);
4165             }
4166         }
4167     }
4168 
4169     /* Create the Thread's Stack */
4170     StackSize = max(256 * 1024, ImageInformation.MaximumStackSize);
4171     Status = BaseCreateStack(ProcessHandle,
4172                              ImageInformation.CommittedStackSize,
4173                              StackSize,
4174                              &InitialTeb);
4175     if (!NT_SUCCESS(Status))
4176     {
4177         DPRINT1("Creating the thread stack failed: %lx\n", Status);
4178         BaseSetLastNTError(Status);
4179         Result = FALSE;
4180         goto Quickie;
4181     }
4182 
4183     /* Create the Thread's Context */
4184     BaseInitializeContext(&Context,
4185                           Peb,
4186                           ImageInformation.TransferAddress,
4187                           InitialTeb.StackBase,
4188                           0);
4189 
4190     /* Convert the thread attributes */
4191     ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
4192                                                   lpThreadAttributes,
4193                                                   NULL);
4194     if ((hUserToken) && (lpThreadAttributes))
4195     {
4196         /* If the caller specified a user token, zero the security descriptor */
4197         LocalThreadAttributes = *lpThreadAttributes;
4198         LocalThreadAttributes.lpSecurityDescriptor = NULL;
4199         ObjectAttributes = BaseFormatObjectAttributes(&LocalObjectAttributes,
4200                                                       &LocalThreadAttributes,
4201                                                       NULL);
4202     }
4203 
4204     /* Create the Kernel Thread Object */
4205     Status = NtCreateThread(&ThreadHandle,
4206                             THREAD_ALL_ACCESS,
4207                             ObjectAttributes,
4208                             ProcessHandle,
4209                             &ClientId,
4210                             &Context,
4211                             &InitialTeb,
4212                             TRUE);
4213     if (!NT_SUCCESS(Status))
4214     {
4215         /* A process is not allowed to exist without a main thread, so fail */
4216         DPRINT1("Creating the main thread failed: %lx\n", Status);
4217         BaseSetLastNTError(Status);
4218         Result = FALSE;
4219         goto Quickie;
4220     }
4221 
4222     /* Begin filling out the CSRSS message, first with our IDs and handles */
4223     CreateProcessMsg->ProcessHandle = ProcessHandle;
4224     CreateProcessMsg->ThreadHandle = ThreadHandle;
4225     CreateProcessMsg->ClientId = ClientId;
4226 
4227     /* Write the remote PEB address and clear it locally, we no longer use it */
4228     CreateProcessMsg->PebAddressNative = RemotePeb;
4229 #ifdef _WIN64
4230     DPRINT1("TODO: WOW64 is not supported yet\n");
4231     CreateProcessMsg->PebAddressWow64 = 0;
4232 #else
4233     CreateProcessMsg->PebAddressWow64 = (ULONG)RemotePeb;
4234 #endif
4235     RemotePeb = NULL;
4236 
4237     /* Now check what kind of architecture this image was made for */
4238     switch (ImageInformation.Machine)
4239     {
4240         /* IA32, IA64 and AMD64 are supported in Server 2003 */
4241         case IMAGE_FILE_MACHINE_I386:
4242             CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
4243             break;
4244         case IMAGE_FILE_MACHINE_IA64:
4245             CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64;
4246             break;
4247         case IMAGE_FILE_MACHINE_AMD64:
4248             CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
4249             break;
4250 
4251         /* Anything else results in image unknown -- but no failure */
4252         default:
4253             DbgPrint("kernel32: No mapping for ImageInformation.Machine == %04x\n",
4254                      ImageInformation.Machine);
4255             CreateProcessMsg->ProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
4256             break;
4257     }
4258 
4259     /* Write the input creation flags except any debugger-related flags */
4260     CreateProcessMsg->CreationFlags = dwCreationFlags &
4261                                       ~(DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS);
4262 
4263     /* CSRSS needs to know if this is a GUI app or not */
4264     if ((ImageInformation.SubSystemType == IMAGE_SUBSYSTEM_WINDOWS_GUI) ||
4265         (IsWowApp))
4266     {
4267         /*
4268          * For GUI apps we turn on the 2nd bit. This allow CSRSS server dlls
4269          * (basesrv in particular) to know whether or not this is a GUI or a
4270          * TUI application.
4271          */
4272         AddToHandle(CreateProcessMsg->ProcessHandle, 2);
4273 
4274         /* Also check if the parent is also a GUI process */
4275         NtHeaders = RtlImageNtHeader(GetModuleHandle(NULL));
4276         if ((NtHeaders) &&
4277             (NtHeaders->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI))
4278         {
4279             /* Let it know that it should display the hourglass mouse cursor */
4280             AddToHandle(CreateProcessMsg->ProcessHandle, 1);
4281         }
4282     }
4283 
4284     /* For all apps, if this flag is on, the hourglass mouse cursor is shown */
4285     if (StartupInfo.dwFlags & STARTF_FORCEONFEEDBACK)
4286     {
4287         AddToHandle(CreateProcessMsg->ProcessHandle, 1);
4288     }
4289 
4290     /* Likewise, the opposite holds as well */
4291     if (StartupInfo.dwFlags & STARTF_FORCEOFFFEEDBACK)
4292     {
4293         RemoveFromHandle(CreateProcessMsg->ProcessHandle, 1);
4294     }
4295 
4296     /* Also store which kind of VDM app (if any) this is */
4297     CreateProcessMsg->VdmBinaryType = VdmBinaryType;
4298 
4299     /* And if it really is a VDM app... */
4300     if (VdmBinaryType)
4301     {
4302         /* Store the task ID and VDM console handle */
4303         CreateProcessMsg->hVDM = VdmTask ? 0 : Peb->ProcessParameters->ConsoleHandle;
4304         CreateProcessMsg->VdmTask = VdmTask;
4305     }
4306     else if (VdmReserve)
4307     {
4308         /* Extended VDM, set a flag */
4309         CreateProcessMsg->VdmBinaryType |= BINARY_TYPE_WOW_EX;
4310     }
4311 
4312     /* Check if there's side-by-side assembly data associated with the process */
4313     if (CreateProcessMsg->Sxs.Flags)
4314     {
4315         /* This should not happen in ReactOS yet */
4316         DPRINT1("This is an SxS Message -- should not happen yet\n");
4317         BaseSetLastNTError(STATUS_NOT_IMPLEMENTED);
4318         NtTerminateProcess(ProcessHandle, STATUS_NOT_IMPLEMENTED);
4319         Result = FALSE;
4320         goto Quickie;
4321     }
4322 
4323     /* We are finally ready to call CSRSS to tell it about our new process! */
4324     CsrClientCallServer((PCSR_API_MESSAGE)&CsrMsg[0],
4325                         CaptureBuffer,
4326                         CSR_CREATE_API_NUMBER(BASESRV_SERVERDLL_INDEX,
4327                                               BasepCreateProcess),
4328                         sizeof(*CreateProcessMsg));
4329 
4330     /* CSRSS has returned, free the capture buffer now if we had one */
4331     if (CaptureBuffer)
4332     {
4333         CsrFreeCaptureBuffer(CaptureBuffer);
4334         CaptureBuffer = NULL;
4335     }
4336 
4337     /* Check if CSRSS failed to accept ownership of the new Windows process */
4338     if (!NT_SUCCESS(CsrMsg[0].Status))
4339     {
4340         /* Terminate the process and enter failure path with the CSRSS status */
4341         DPRINT1("Failed to tell csrss about new process\n");
4342         BaseSetLastNTError(CsrMsg[0].Status);
4343         NtTerminateProcess(ProcessHandle, CsrMsg[0].Status);
4344         Result = FALSE;
4345         goto Quickie;
4346     }
4347 
4348     /* Check if we have a token due to Authz/Safer, not passed by the user */
4349     if ((TokenHandle) && !(hUserToken))
4350     {
4351         /* Replace the process and/or thread token with the one from Safer */
4352         Status = BasepReplaceProcessThreadTokens(TokenHandle,
4353                                                  ProcessHandle,
4354                                                  ThreadHandle);
4355         if (!NT_SUCCESS(Status))
4356         {
4357             /* If this failed, kill the process and enter the failure path */
4358             DPRINT1("Failed to update process token: %lx\n", Status);
4359             NtTerminateProcess(ProcessHandle, Status);
4360             BaseSetLastNTError(Status);
4361             Result = FALSE;
4362             goto Quickie;
4363         }
4364     }
4365 
4366     /* Check if a job was associated with this process */
4367     if (JobHandle)
4368     {
4369         /* Bind the process and job together now */
4370         Status = NtAssignProcessToJobObject(JobHandle, ProcessHandle);
4371         if (!NT_SUCCESS(Status))
4372         {
4373             /* Kill the process and enter the failure path if binding failed */
4374             DPRINT1("Failed to assign process to job: %lx\n", Status);
4375             NtTerminateProcess(ProcessHandle, STATUS_ACCESS_DENIED);
4376             BaseSetLastNTError(Status);
4377             Result = FALSE;
4378             goto Quickie;
4379         }
4380     }
4381 
4382     /* Finally, resume the thread to actually get the process started */
4383     if (!(dwCreationFlags & CREATE_SUSPENDED))
4384     {
4385         NtResumeThread(ThreadHandle, &ResumeCount);
4386     }
4387 
4388 VdmShortCircuit:
4389     /* We made it this far, meaning we have a fully created process and thread */
4390     Result = TRUE;
4391 
4392     /* Anyone doing a VDM undo should now undo everything, since we are done */
4393     if (VdmUndoLevel) VdmUndoLevel |= VDM_UNDO_COMPLETED;
4394 
4395     /* Having a VDM wait object implies this must be a VDM process */
4396     if (VdmWaitObject)
4397     {
4398         /* Check if it's a 16-bit separate WOW process */
4399         if (VdmBinaryType == BINARY_TYPE_SEPARATE_WOW)
4400         {
4401             /* OR-in the special flag to indicate this, and return to caller */
4402             AddToHandle(VdmWaitObject, 2);
4403             lpProcessInformation->hProcess = VdmWaitObject;
4404 
4405             /* Check if this was a re-used VDM */
4406             if (VdmUndoLevel & VDM_UNDO_REUSE)
4407             {
4408                 /* No Client ID should be returned in this case */
4409                 ClientId.UniqueProcess = 0;
4410                 ClientId.UniqueThread = 0;
4411             }
4412         }
4413         else
4414         {
4415             /* OR-in the special flag to indicate this is not a separate VDM */
4416             AddToHandle(VdmWaitObject, 1);
4417 
4418             /* Return handle to the caller */
4419             lpProcessInformation->hProcess = VdmWaitObject;
4420         }
4421 
4422         /* Close the original process handle, since it's not needed for VDM */
4423         if (ProcessHandle) NtClose(ProcessHandle);
4424     }
4425     else
4426     {
4427         /* This is a regular process, so return the real process handle */
4428         lpProcessInformation->hProcess = ProcessHandle;
4429     }
4430 
4431     /* Return the rest of the process information based on what we have so far */
4432     lpProcessInformation->hThread = ThreadHandle;
4433     lpProcessInformation->dwProcessId = HandleToUlong(ClientId.UniqueProcess);
4434     lpProcessInformation->dwThreadId = HandleToUlong(ClientId.UniqueThread);
4435 
4436     /* NULL these out here so we know to treat this as a success scenario */
4437     ProcessHandle = NULL;
4438     ThreadHandle = NULL;
4439 
4440 Quickie:
4441     /* Free the debugger command line if one was allocated */
4442     if (DebuggerCmdLine) RtlFreeHeap(RtlGetProcessHeap(), 0, DebuggerCmdLine);
4443 
4444     /* Check if an SxS full path as queried */
4445     if (PathBuffer)
4446     {
4447         /* Reinitialize the executable path */
4448         RtlInitEmptyUnicodeString(&SxsWin32ExePath, NULL, 0);
4449         SxsWin32ExePath.Length = 0;
4450 
4451         /* Free the path buffer */
4452         RtlFreeHeap(RtlGetProcessHeap(), 0, PathBuffer);
4453     }
4454 
4455 #if _SXS_SUPPORT_ENABLED_
4456     /* Check if this was a non-VDM process */
4457     if (!VdmBinaryType)
4458     {
4459         /* Then it must've had SxS data, so close the handles used for it */
4460         BasepSxsCloseHandles(&Handles);
4461         BasepSxsCloseHandles(&FileHandles);
4462 
4463         /* Check if we built SxS byte buffers for this create process request */
4464         if (SxsConglomeratedBuffer)
4465         {
4466             /* Loop all of them */
4467             for (i = 0; i < 5; i++)
4468             {
4469                 /* Check if this one was allocated */
4470                 ThisBuffer = SxsStaticBuffers[i];
4471                 if (ThisBuffer)
4472                 {
4473                     /* Get the underlying RTL_BUFFER structure */
4474                     ByteBuffer = &ThisBuffer->ByteBuffer;
4475                     if ((ThisBuffer != (PVOID)-8) && (ByteBuffer->Buffer))
4476                     {
4477                         /* Check if it was dynamic */
4478                         if (ByteBuffer->Buffer != ByteBuffer->StaticBuffer)
4479                         {
4480                             /* Free it from the heap */
4481                             FreeString.Buffer = (PWCHAR)ByteBuffer->Buffer;
4482                             RtlFreeUnicodeString(&FreeString);
4483                         }
4484 
4485                         /* Reset the buffer to its static data */
4486                         ByteBuffer->Buffer = ByteBuffer->StaticBuffer;
4487                         ByteBuffer->Size = ByteBuffer->StaticSize;
4488                     }
4489 
4490                     /* Reset the string to the static buffer */
4491                     RtlInitEmptyUnicodeString(&ThisBuffer->String,
4492                                               (PWCHAR)ByteBuffer->StaticBuffer,
4493                                               ByteBuffer->StaticSize);
4494                     if (ThisBuffer->String.Buffer)
4495                     {
4496                         /* Also NULL-terminate it */
4497                         *ThisBuffer->String.Buffer = UNICODE_NULL;
4498                     }
4499                 }
4500             }
4501         }
4502     }
4503 #endif
4504     /* Check if an environment was passed in */
4505     if ((lpEnvironment) && !(dwCreationFlags & CREATE_UNICODE_ENVIRONMENT))
4506     {
4507         /* Destroy it */
4508         RtlDestroyEnvironment(lpEnvironment);
4509 
4510         /* If this was the VDM environment too, clear that as well */
4511         if (VdmUnicodeEnv.Buffer == lpEnvironment) VdmUnicodeEnv.Buffer = NULL;
4512         lpEnvironment = NULL;
4513     }
4514 
4515     /* Unconditionally free all the name parsing buffers we always allocate */
4516     RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine);
4517     RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer);
4518     RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory);
4519     RtlFreeHeap(RtlGetProcessHeap(), 0, FreeBuffer);
4520 
4521     /* Close open file/section handles */
4522     if (FileHandle) NtClose(FileHandle);
4523     if (SectionHandle) NtClose(SectionHandle);
4524 
4525     /* If we have a thread handle, this was a failure path */
4526     if (ThreadHandle)
4527     {
4528         /* So kill the process and close the thread handle */
4529         NtTerminateProcess(ProcessHandle, 0);
4530         NtClose(ThreadHandle);
4531     }
4532 
4533     /* If we have a process handle, this was a failure path, so close it */
4534     if (ProcessHandle) NtClose(ProcessHandle);
4535 
4536     /* Thread/process handles, if any, are now processed. Now close this one. */
4537     if (JobHandle) NtClose(JobHandle);
4538 
4539     /* Check if we had created a token */
4540     if (TokenHandle)
4541     {
4542         /* And if the user asked for one */
4543         if (hUserToken)
4544         {
4545             /* Then return it */
4546             *hNewToken = TokenHandle;
4547         }
4548         else
4549         {
4550             /* User didn't want it, so we used it temporarily -- close it */
4551             NtClose(TokenHandle);
4552         }
4553     }
4554 
4555     /* Free any temporary app compatibility data, it's no longer needed */
4556     BasepFreeAppCompatData(AppCompatData, AppCompatSxsData);
4557 
4558     /* Free a few strings. The API takes care of these possibly being NULL */
4559     RtlFreeUnicodeString(&VdmString);
4560     RtlFreeUnicodeString(&DebuggerString);
4561 
4562     /* Check if we had built any sort of VDM environment */
4563     if ((VdmAnsiEnv.Buffer) || (VdmUnicodeEnv.Buffer))
4564     {
4565         /* Free it */
4566         BaseDestroyVDMEnvironment(&VdmAnsiEnv, &VdmUnicodeEnv);
4567     }
4568 
4569     /* Check if this was any kind of VDM application that we ended up creating */
4570     if ((VdmUndoLevel) && (!(VdmUndoLevel & VDM_UNDO_COMPLETED)))
4571     {
4572         /* Send an undo */
4573         BaseUpdateVDMEntry(VdmEntryUndo,
4574                            (PHANDLE)&VdmTask,
4575                            VdmUndoLevel,
4576                            VdmBinaryType);
4577 
4578         /* And close whatever VDM handle we were using for notifications */
4579         if (VdmWaitObject) NtClose(VdmWaitObject);
4580     }
4581 
4582     /* Check if we ended up here with an allocated search path, and free it */
4583     if (SearchPath) RtlFreeHeap(RtlGetProcessHeap(), 0, SearchPath);
4584 
4585     /* Finally, return the API's result */
4586     return Result;
4587 }
4588 
4589 /*
4590  * @implemented
4591  */
4592 BOOL
4593 WINAPI
4594 DECLSPEC_HOTPATCH
4595 CreateProcessW(LPCWSTR lpApplicationName,
4596                LPWSTR lpCommandLine,
4597                LPSECURITY_ATTRIBUTES lpProcessAttributes,
4598                LPSECURITY_ATTRIBUTES lpThreadAttributes,
4599                BOOL bInheritHandles,
4600                DWORD dwCreationFlags,
4601                LPVOID lpEnvironment,
4602                LPCWSTR lpCurrentDirectory,
4603                LPSTARTUPINFOW lpStartupInfo,
4604                LPPROCESS_INFORMATION lpProcessInformation)
4605 {
4606     /* Call the internal (but exported) version */
4607     return CreateProcessInternalW(NULL,
4608                                   lpApplicationName,
4609                                   lpCommandLine,
4610                                   lpProcessAttributes,
4611                                   lpThreadAttributes,
4612                                   bInheritHandles,
4613                                   dwCreationFlags,
4614                                   lpEnvironment,
4615                                   lpCurrentDirectory,
4616                                   lpStartupInfo,
4617                                   lpProcessInformation,
4618                                   NULL);
4619 }
4620 
4621 /*
4622  * @implemented
4623  */
4624 BOOL
4625 WINAPI
4626 CreateProcessInternalA(HANDLE hToken,
4627                        LPCSTR lpApplicationName,
4628                        LPSTR lpCommandLine,
4629                        LPSECURITY_ATTRIBUTES lpProcessAttributes,
4630                        LPSECURITY_ATTRIBUTES lpThreadAttributes,
4631                        BOOL bInheritHandles,
4632                        DWORD dwCreationFlags,
4633                        LPVOID lpEnvironment,
4634                        LPCSTR lpCurrentDirectory,
4635                        LPSTARTUPINFOA lpStartupInfo,
4636                        LPPROCESS_INFORMATION lpProcessInformation,
4637                        PHANDLE hNewToken)
4638 {
4639     UNICODE_STRING CommandLine;
4640     UNICODE_STRING ApplicationName;
4641     UNICODE_STRING CurrentDirectory;
4642     BOOL bRetVal;
4643     STARTUPINFOW StartupInfo;
4644 
4645     DPRINT("dwCreationFlags %x, lpEnvironment %p, lpCurrentDirectory %p, "
4646             "lpStartupInfo %p, lpProcessInformation %p\n",
4647             dwCreationFlags, lpEnvironment, lpCurrentDirectory,
4648             lpStartupInfo, lpProcessInformation);
4649 
4650     /* Copy Startup Info */
4651     RtlMoveMemory(&StartupInfo, lpStartupInfo, sizeof(*lpStartupInfo));
4652 
4653     /* Initialize all strings to nothing */
4654     CommandLine.Buffer = NULL;
4655     ApplicationName.Buffer = NULL;
4656     CurrentDirectory.Buffer = NULL;
4657     StartupInfo.lpDesktop = NULL;
4658     StartupInfo.lpReserved = NULL;
4659     StartupInfo.lpTitle = NULL;
4660 
4661     /* Convert the Command line */
4662     if (lpCommandLine)
4663     {
4664         Basep8BitStringToDynamicUnicodeString(&CommandLine,
4665                                               lpCommandLine);
4666     }
4667 
4668     /* Convert the Name and Directory */
4669     if (lpApplicationName)
4670     {
4671         Basep8BitStringToDynamicUnicodeString(&ApplicationName,
4672                                               lpApplicationName);
4673     }
4674     if (lpCurrentDirectory)
4675     {
4676         Basep8BitStringToDynamicUnicodeString(&CurrentDirectory,
4677                                               lpCurrentDirectory);
4678     }
4679 
4680     /* Now convert Startup Strings */
4681     if (lpStartupInfo->lpReserved)
4682     {
4683         BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpReserved,
4684                                            &StartupInfo.lpReserved);
4685     }
4686     if (lpStartupInfo->lpDesktop)
4687     {
4688         BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpDesktop,
4689                                            &StartupInfo.lpDesktop);
4690     }
4691     if (lpStartupInfo->lpTitle)
4692     {
4693         BasepAnsiStringToHeapUnicodeString(lpStartupInfo->lpTitle,
4694                                            &StartupInfo.lpTitle);
4695     }
4696 
4697     /* Call the Unicode function */
4698     bRetVal = CreateProcessInternalW(hToken,
4699                                      ApplicationName.Buffer,
4700                                      CommandLine.Buffer,
4701                                      lpProcessAttributes,
4702                                      lpThreadAttributes,
4703                                      bInheritHandles,
4704                                      dwCreationFlags,
4705                                      lpEnvironment,
4706                                      CurrentDirectory.Buffer,
4707                                      &StartupInfo,
4708                                      lpProcessInformation,
4709                                      hNewToken);
4710 
4711     /* Clean up */
4712     RtlFreeUnicodeString(&ApplicationName);
4713     RtlFreeUnicodeString(&CommandLine);
4714     RtlFreeUnicodeString(&CurrentDirectory);
4715     RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpDesktop);
4716     RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpReserved);
4717     RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo.lpTitle);
4718 
4719     /* Return what Unicode did */
4720     return bRetVal;
4721 }
4722 
4723 /*
4724  * FUNCTION: The CreateProcess function creates a new process and its
4725  * primary thread. The new process executes the specified executable file
4726  * ARGUMENTS:
4727  *
4728  *     lpApplicationName = Pointer to name of executable module
4729  *     lpCommandLine = Pointer to command line string
4730  *     lpProcessAttributes = Process security attributes
4731  *     lpThreadAttributes = Thread security attributes
4732  *     bInheritHandles = Handle inheritance flag
4733  *     dwCreationFlags = Creation flags
4734  *     lpEnvironment = Pointer to new environment block
4735  *     lpCurrentDirectory = Pointer to current directory name
4736  *     lpStartupInfo = Pointer to startup info
4737  *     lpProcessInformation = Pointer to process information
4738  *
4739  * @implemented
4740  */
4741 BOOL
4742 WINAPI
4743 DECLSPEC_HOTPATCH
4744 CreateProcessA(LPCSTR lpApplicationName,
4745                LPSTR lpCommandLine,
4746                LPSECURITY_ATTRIBUTES lpProcessAttributes,
4747                LPSECURITY_ATTRIBUTES lpThreadAttributes,
4748                BOOL bInheritHandles,
4749                DWORD dwCreationFlags,
4750                LPVOID lpEnvironment,
4751                LPCSTR lpCurrentDirectory,
4752                LPSTARTUPINFOA lpStartupInfo,
4753                LPPROCESS_INFORMATION lpProcessInformation)
4754 {
4755     /* Call the internal (but exported) version */
4756     return CreateProcessInternalA(NULL,
4757                                   lpApplicationName,
4758                                   lpCommandLine,
4759                                   lpProcessAttributes,
4760                                   lpThreadAttributes,
4761                                   bInheritHandles,
4762                                   dwCreationFlags,
4763                                   lpEnvironment,
4764                                   lpCurrentDirectory,
4765                                   lpStartupInfo,
4766                                   lpProcessInformation,
4767                                   NULL);
4768 }
4769 
4770 /*
4771  * @implemented
4772  */
4773 UINT
4774 WINAPI
4775 DECLSPEC_HOTPATCH
4776 WinExec(LPCSTR lpCmdLine,
4777         UINT uCmdShow)
4778 {
4779     STARTUPINFOA StartupInfo;
4780     PROCESS_INFORMATION  ProcessInformation;
4781     DWORD dosErr;
4782 
4783     RtlZeroMemory(&StartupInfo, sizeof(StartupInfo));
4784     StartupInfo.cb = sizeof(STARTUPINFOA);
4785     StartupInfo.wShowWindow = (WORD)uCmdShow;
4786     StartupInfo.dwFlags = 0;
4787 
4788     if (!CreateProcessA(NULL,
4789                         (PVOID)lpCmdLine,
4790                         NULL,
4791                         NULL,
4792                         FALSE,
4793                         0,
4794                         NULL,
4795                         NULL,
4796                         &StartupInfo,
4797                         &ProcessInformation))
4798     {
4799         dosErr = GetLastError();
4800         return dosErr < 32 ? dosErr : ERROR_BAD_FORMAT;
4801     }
4802 
4803     if (NULL != UserWaitForInputIdleRoutine)
4804     {
4805         UserWaitForInputIdleRoutine(ProcessInformation.hProcess,
4806                                            10000);
4807     }
4808 
4809     NtClose(ProcessInformation.hProcess);
4810     NtClose(ProcessInformation.hThread);
4811 
4812     return 33; /* Something bigger than 31 means success. */
4813 }
4814 
4815 /* EOF */
4816