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