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