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