2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/kernel32/process/create.c
5 * PURPOSE: Process functions
6 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
7 * Ariadne ( ariadne@xs4all.nl)
10 /* INCLUDES ****************************************************************/
15 #include "../include/debug.h"
17 #define CMD_STRING L"cmd /c "
19 extern __declspec(noreturn
)
22 ConsoleControlDispatcher(DWORD CodeAndFlag
);
24 /* INTERNAL FUNCTIONS *******************************************************/
26 _SEH_FILTER(BaseExceptionFilter
)
28 EXCEPTION_POINTERS
*ExceptionInfo
= _SEH_GetExceptionPointers();
29 LONG ExceptionDisposition
= EXCEPTION_EXECUTE_HANDLER
;
31 if (GlobalTopLevelExceptionFilter
!= NULL
)
35 ExceptionDisposition
= GlobalTopLevelExceptionFilter(ExceptionInfo
);
42 if ((ExceptionDisposition
== EXCEPTION_CONTINUE_SEARCH
|| ExceptionDisposition
== EXCEPTION_EXECUTE_HANDLER
) &&
43 GlobalTopLevelExceptionFilter
!= UnhandledExceptionFilter
)
45 ExceptionDisposition
= UnhandledExceptionFilter(ExceptionInfo
);
48 return ExceptionDisposition
;
53 BaseProcessStartup(PPROCESS_START_ROUTINE lpStartAddress
)
57 DPRINT("BaseProcessStartup(..) - setting up exception frame.\n");
61 /* Set our Start Address */
62 NtSetInformationThread(NtCurrentThread(),
63 ThreadQuerySetWin32StartAddress
,
65 sizeof(PPROCESS_START_ROUTINE
));
67 /* Call the Start Routine */
68 uExitCode
= (lpStartAddress
)();
70 _SEH_EXCEPT(BaseExceptionFilter
)
72 /* Get the SEH Error */
73 uExitCode
= _SEH_GetExceptionCode();
77 /* Exit the Process with our error */
78 ExitProcess(uExitCode
);
82 * Tells CSR that a new process was created
86 BasepNotifyCsrOfCreation(ULONG dwCreationFlags
,
88 IN BOOL InheritHandles
)
90 ULONG Request
= CREATE_PROCESS
;
91 CSR_API_MESSAGE CsrRequest
;
94 DPRINT("BasepNotifyCsrOfCreation: Process: %lx, Flags %lx\n",
95 ProcessId
, dwCreationFlags
);
97 /* Fill out the request */
98 CsrRequest
.Data
.CreateProcessRequest
.NewProcessId
= ProcessId
;
99 CsrRequest
.Data
.CreateProcessRequest
.Flags
= dwCreationFlags
;
100 CsrRequest
.Data
.CreateProcessRequest
.bInheritHandles
= InheritHandles
;
103 Status
= CsrClientCallServer(&CsrRequest
,
105 MAKE_CSR_API(Request
, CSR_NATIVE
),
106 sizeof(CSR_API_MESSAGE
));
107 if (!NT_SUCCESS(Status
) || !NT_SUCCESS(CsrRequest
.Status
))
109 DPRINT1("Failed to tell csrss about new process\n");
110 return CsrRequest
.Status
;
114 return STATUS_SUCCESS
;
118 * Creates the first Thread in a Proces
122 BasepCreateFirstThread(HANDLE ProcessHandle
,
123 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
124 PSECTION_IMAGE_INFORMATION SectionImageInfo
,
127 OBJECT_ATTRIBUTES LocalObjectAttributes
;
128 POBJECT_ATTRIBUTES ObjectAttributes
;
130 INITIAL_TEB InitialTeb
;
134 DPRINT("BasepCreateFirstThread. hProcess: %lx\n", ProcessHandle
);
136 /* Create the Thread's Stack */
137 BasepCreateStack(ProcessHandle
,
138 SectionImageInfo
->MaximumStackSize
,
139 SectionImageInfo
->CommittedStackSize
,
142 /* Create the Thread's Context */
143 BasepInitializeContext(&Context
,
145 SectionImageInfo
->TransferAddress
,
146 InitialTeb
.StackBase
,
149 /* Convert the thread attributes */
150 ObjectAttributes
= BasepConvertObjectAttributes(&LocalObjectAttributes
,
154 /* Create the Kernel Thread Object */
155 Status
= NtCreateThread(&hThread
,
169 * Converts ANSI to Unicode Environment
173 BasepConvertUnicodeEnvironment(OUT SIZE_T
* EnvSize
,
174 IN PVOID lpEnvironment
)
178 UNICODE_STRING UnicodeEnv
;
181 DPRINT("BasepConvertUnicodeEnvironment\n");
183 /* Scan the environment to calculate its Unicode size */
184 AnsiEnv
.Buffer
= pcScan
= (PCHAR
)lpEnvironment
;
187 pcScan
+= strlen(pcScan
) + 1;
190 /* Create our ANSI String */
191 if (pcScan
== (PCHAR
)lpEnvironment
)
193 AnsiEnv
.Length
= 2 * sizeof(CHAR
);
198 AnsiEnv
.Length
= (ULONG_PTR
)pcScan
- (ULONG_PTR
)lpEnvironment
+ sizeof(CHAR
);
200 AnsiEnv
.MaximumLength
= AnsiEnv
.Length
+ 1;
202 /* Allocate memory for the Unicode Environment */
203 UnicodeEnv
.Buffer
= NULL
;
204 *EnvSize
= AnsiEnv
.MaximumLength
* sizeof(WCHAR
);
205 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
206 (PVOID
)&UnicodeEnv
.Buffer
,
212 if (!NT_SUCCESS(Status
))
214 SetLastError(Status
);
219 /* Use the allocated size */
220 UnicodeEnv
.MaximumLength
= *EnvSize
;
223 RtlAnsiStringToUnicodeString(&UnicodeEnv
, &AnsiEnv
, FALSE
);
224 return UnicodeEnv
.Buffer
;
228 * Converts a Win32 Priority Class to NT
232 BasepConvertPriorityClass(IN ULONG dwCreationFlags
)
236 if(dwCreationFlags
& IDLE_PRIORITY_CLASS
)
238 ReturnClass
= PROCESS_PRIORITY_CLASS_IDLE
;
240 else if(dwCreationFlags
& BELOW_NORMAL_PRIORITY_CLASS
)
242 ReturnClass
= PROCESS_PRIORITY_CLASS_BELOW_NORMAL
;
244 else if(dwCreationFlags
& NORMAL_PRIORITY_CLASS
)
246 ReturnClass
= PROCESS_PRIORITY_CLASS_NORMAL
;
248 else if(dwCreationFlags
& ABOVE_NORMAL_PRIORITY_CLASS
)
250 ReturnClass
= PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
;
252 else if(dwCreationFlags
& HIGH_PRIORITY_CLASS
)
254 ReturnClass
= PROCESS_PRIORITY_CLASS_HIGH
;
256 else if(dwCreationFlags
& REALTIME_PRIORITY_CLASS
)
258 /* Check for Privilege First */
259 if (BasepCheckRealTimePrivilege())
261 ReturnClass
= PROCESS_PRIORITY_CLASS_REALTIME
;
265 ReturnClass
= PROCESS_PRIORITY_CLASS_HIGH
;
270 ReturnClass
= PROCESS_PRIORITY_CLASS_INVALID
;
277 * Duplicates a standard handle and writes it where requested.
281 BasepDuplicateAndWriteHandle(IN HANDLE ProcessHandle
,
282 IN HANDLE StandardHandle
,
286 HANDLE DuplicatedHandle
;
289 DPRINT("BasepDuplicateAndWriteHandle. hProcess: %lx, Handle: %lx,"
290 "Address: %p\n", ProcessHandle
, StandardHandle
, Address
);
292 /* Don't touch Console Handles */
293 if (IsConsoleHandle(StandardHandle
)) return;
295 /* Duplicate the handle */
296 Status
= NtDuplicateObject(NtCurrentProcess(),
300 DUPLICATE_SAME_ACCESS
| DUPLICATE_SAME_ATTRIBUTES
,
303 if (NT_SUCCESS(Status
))
306 NtWriteVirtualMemory(ProcessHandle
,
316 BasepGetDllPath(LPWSTR FullPath
,
319 /* FIXME: Not yet implemented */
325 BasepCopyHandles(IN PRTL_USER_PROCESS_PARAMETERS Params
,
326 IN PRTL_USER_PROCESS_PARAMETERS PebParams
,
327 IN BOOL InheritHandles
)
329 DPRINT("BasepCopyHandles %p %p, %d\n", Params
, PebParams
, InheritHandles
);
331 /* Copy the handle if we are inheriting or if it's a console handle */
332 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardInput
))
334 Params
->StandardInput
= PebParams
->StandardInput
;
336 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardOutput
))
338 Params
->StandardOutput
= PebParams
->StandardOutput
;
340 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardError
))
342 Params
->StandardError
= PebParams
->StandardError
;
348 BasepInitializeEnvironment(HANDLE ProcessHandle
,
350 LPWSTR ApplicationPathName
,
351 LPWSTR lpCurrentDirectory
,
352 LPWSTR lpCommandLine
,
353 LPVOID lpEnvironment
,
355 LPSTARTUPINFOW StartupInfo
,
359 WCHAR FullPath
[MAX_PATH
];
361 LPWSTR DllPathString
;
362 PRTL_USER_PROCESS_PARAMETERS ProcessParameters
;
363 PRTL_USER_PROCESS_PARAMETERS RemoteParameters
= NULL
;
364 UNICODE_STRING DllPath
, ImageName
, CommandLine
, CurrentDirectory
;
370 UNICODE_STRING Desktop
, Shell
, Runtime
, Title
;
371 PPEB OurPeb
= NtCurrentPeb();
372 LPVOID Environment
= lpEnvironment
;
374 DPRINT("BasepInitializeEnvironment\n");
376 /* Get the full path name */
377 RetVal
= GetFullPathNameW(ApplicationPathName
,
381 DPRINT("ApplicationPathName: %S, FullPath: %S\n", ApplicationPathName
,
384 /* Get the DLL Path */
385 DllPathString
= BasepGetDllPath(FullPath
, Environment
);
387 /* Initialize Strings */
388 RtlInitUnicodeString(&DllPath
, DllPathString
);
389 RtlInitUnicodeString(&ImageName
, FullPath
);
390 RtlInitUnicodeString(&CommandLine
, lpCommandLine
);
391 RtlInitUnicodeString(&CurrentDirectory
, lpCurrentDirectory
);
393 /* Initialize more Strings from the Startup Info */
394 if (StartupInfo
->lpDesktop
)
396 RtlInitUnicodeString(&Desktop
, StartupInfo
->lpDesktop
);
400 RtlInitUnicodeString(&Desktop
, L
"");
402 if (StartupInfo
->lpReserved
)
404 RtlInitUnicodeString(&Shell
, StartupInfo
->lpReserved
);
408 RtlInitUnicodeString(&Shell
, L
"");
410 if (StartupInfo
->lpTitle
)
412 RtlInitUnicodeString(&Title
, StartupInfo
->lpTitle
);
416 RtlInitUnicodeString(&Title
, L
"");
419 /* This one is special because the length can differ */
420 Runtime
.Buffer
= (LPWSTR
)StartupInfo
->lpReserved2
;
421 Runtime
.MaximumLength
= Runtime
.Length
= StartupInfo
->cbReserved2
;
423 /* Create the Parameter Block */
424 DPRINT("Creating Process Parameters: %wZ %wZ %wZ %wZ %wZ %wZ %wZ\n",
425 &ImageName
, &DllPath
, &CommandLine
, &Desktop
, &Title
, &Shell
,
427 Status
= RtlCreateProcessParameters(&ProcessParameters
,
431 &CurrentDirectory
: NULL
,
439 if (!NT_SUCCESS(Status
))
441 DPRINT1("Failed to create process parameters!\n");
445 /* Check if we got an environment. If not, use ours */
448 /* Save pointer and start lookup */
449 Environment
= ScanChar
= ProcessParameters
->Environment
;
453 /* Save pointer and start lookup */
454 Environment
= ScanChar
= OurPeb
->ProcessParameters
->Environment
;
457 /* Find the environment size */
460 if (EnvSize
&& Environment
== lpEnvironment
)
462 /* its a converted ansi environment, bypass the length calculation */
463 EnviroSize
= EnvSize
;
469 ScanChar
+= wcslen(ScanChar
) + 1;
472 /* Calculate the size of the block */
473 if (ScanChar
== Environment
)
475 EnviroSize
= 2 * sizeof(WCHAR
);
479 EnviroSize
= (ULONG
)((ULONG_PTR
)ScanChar
- (ULONG_PTR
)Environment
+ sizeof(WCHAR
));
482 DPRINT("EnvironmentSize %ld\n", EnviroSize
);
484 /* Allocate and Initialize new Environment Block */
486 ProcessParameters
->Environment
= NULL
;
487 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
488 (PVOID
*)&ProcessParameters
->Environment
,
493 if (!NT_SUCCESS(Status
))
495 DPRINT1("Failed to allocate Environment Block\n");
499 /* Write the Environment Block */
500 ZwWriteVirtualMemory(ProcessHandle
,
501 ProcessParameters
->Environment
,
507 /* Write new parameters */
508 ProcessParameters
->StartingX
= StartupInfo
->dwX
;
509 ProcessParameters
->StartingY
= StartupInfo
->dwY
;
510 ProcessParameters
->CountX
= StartupInfo
->dwXSize
;
511 ProcessParameters
->CountY
= StartupInfo
->dwYSize
;
512 ProcessParameters
->CountCharsX
= StartupInfo
->dwXCountChars
;
513 ProcessParameters
->CountCharsY
= StartupInfo
->dwYCountChars
;
514 ProcessParameters
->FillAttribute
= StartupInfo
->dwFillAttribute
;
515 ProcessParameters
->WindowFlags
= StartupInfo
->dwFlags
;
516 ProcessParameters
->ShowWindowFlags
= StartupInfo
->wShowWindow
;
518 /* Write the handles only if we have to */
519 if (StartupInfo
->dwFlags
& STARTF_USESTDHANDLES
)
521 DPRINT("Using Standard Handles\n");
522 ProcessParameters
->StandardInput
= StartupInfo
->hStdInput
;
523 ProcessParameters
->StandardOutput
= StartupInfo
->hStdOutput
;
524 ProcessParameters
->StandardError
= StartupInfo
->hStdError
;
527 /* Use Special Flags for ConDllInitialize in Kernel32 */
528 if (CreationFlags
& DETACHED_PROCESS
)
530 ProcessParameters
->ConsoleHandle
= HANDLE_DETACHED_PROCESS
;
532 else if (CreationFlags
& CREATE_NO_WINDOW
)
534 ProcessParameters
->ConsoleHandle
= HANDLE_CREATE_NO_WINDOW
;
536 else if (CreationFlags
& CREATE_NEW_CONSOLE
)
538 ProcessParameters
->ConsoleHandle
= HANDLE_CREATE_NEW_CONSOLE
;
542 /* Inherit our Console Handle */
543 ProcessParameters
->ConsoleHandle
= OurPeb
->ProcessParameters
->ConsoleHandle
;
545 /* Is the shell trampling on our Handles? */
546 if (!(StartupInfo
->dwFlags
&
547 (STARTF_USESTDHANDLES
| STARTF_USEHOTKEY
| STARTF_SHELLPRIVATE
)))
549 /* Use handles from PEB, if inheriting or they are console */
550 DPRINT("Copying handles from parent\n");
551 BasepCopyHandles(ProcessParameters
,
552 OurPeb
->ProcessParameters
,
557 /* Also set the Console Flag */
558 if (CreationFlags
& CREATE_NEW_PROCESS_GROUP
)
560 ProcessParameters
->ConsoleFlags
= 1;
563 /* Allocate memory for the parameter block */
564 Size
= ProcessParameters
->Length
;
565 Status
= NtAllocateVirtualMemory(ProcessHandle
,
566 (PVOID
*)&RemoteParameters
,
571 if (!NT_SUCCESS(Status
))
573 DPRINT1("Failed to allocate Parameters Block\n");
577 /* Set the allocated size */
578 ProcessParameters
->MaximumLength
= Size
;
580 /* Handle some Parameter Flags */
581 ProcessParameters
->ConsoleFlags
= (CreationFlags
& CREATE_NEW_PROCESS_GROUP
);
582 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_USER
) ?
583 PPF_PROFILE_USER
: 0;
584 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_KERNEL
) ?
585 PPF_PROFILE_KERNEL
: 0;
586 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_SERVER
) ?
587 PPF_PROFILE_SERVER
: 0;
588 ProcessParameters
->Flags
|= (NtCurrentPeb()->ProcessParameters
->Flags
&
589 PPF_DISABLE_HEAP_CHECKS
);
591 /* Write the Parameter Block */
592 Status
= NtWriteVirtualMemory(ProcessHandle
,
595 ProcessParameters
->Length
,
598 /* Write the PEB Pointer */
599 Status
= NtWriteVirtualMemory(ProcessHandle
,
600 &Peb
->ProcessParameters
,
606 RtlFreeHeap(GetProcessHeap(), 0, DllPath
.Buffer
);
607 RtlDestroyProcessParameters(ProcessParameters
);
609 DPRINT("Completed\n");
610 return STATUS_SUCCESS
;
613 /* FUNCTIONS ****************************************************************/
620 CreateProcessInternalW(HANDLE hToken
,
621 LPCWSTR lpApplicationName
,
622 LPWSTR lpCommandLine
,
623 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
624 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
625 BOOL bInheritHandles
,
626 DWORD dwCreationFlags
,
627 LPVOID lpEnvironment
,
628 LPCWSTR lpCurrentDirectory
,
629 LPSTARTUPINFOW lpStartupInfo
,
630 LPPROCESS_INFORMATION lpProcessInformation
,
634 PROCESS_PRIORITY_CLASS PriorityClass
;
635 BOOLEAN FoundQuotes
= FALSE
;
636 BOOLEAN QuotesNeeded
= FALSE
;
637 BOOLEAN CmdLineIsAppName
= FALSE
;
638 UNICODE_STRING ApplicationName
;
639 OBJECT_ATTRIBUTES LocalObjectAttributes
;
640 POBJECT_ATTRIBUTES ObjectAttributes
;
641 HANDLE hSection
, hProcess
, hThread
;
642 SECTION_IMAGE_INFORMATION SectionImageInfo
;
643 LPWSTR CurrentDirectory
= NULL
;
644 LPWSTR CurrentDirectoryPart
;
645 PROCESS_BASIC_INFORMATION ProcessBasicInfo
;
646 STARTUPINFOW StartupInfo
;
648 LPWSTR BatchCommandLine
;
650 UNICODE_STRING CommandLineString
;
652 LPWSTR QuotedCmdLine
= NULL
;
654 LPWSTR NullBuffer
= NULL
;
655 LPWSTR NameBuffer
= NULL
;
659 BOOLEAN SearchDone
= FALSE
;
660 BOOLEAN Escape
= FALSE
;
662 PPEB OurPeb
= NtCurrentPeb();
666 DPRINT("CreateProcessW: lpApplicationName: %S lpCommandLine: %S"
667 " lpEnvironment: %p lpCurrentDirectory: %S dwCreationFlags: %lx\n",
668 lpApplicationName
, lpCommandLine
, lpEnvironment
, lpCurrentDirectory
,
671 /* Flags we don't handle yet */
672 if (dwCreationFlags
& CREATE_SEPARATE_WOW_VDM
)
674 DPRINT1("CREATE_SEPARATE_WOW_VDM not handled\n");
676 if (dwCreationFlags
& CREATE_SHARED_WOW_VDM
)
678 DPRINT1("CREATE_SHARED_WOW_VDM not handled\n");
680 if (dwCreationFlags
& CREATE_FORCEDOS
)
682 DPRINT1("CREATE_FORCEDOS not handled\n");
685 /* Fail on this flag, it's only valid with the WithLogonW function */
686 if (dwCreationFlags
& CREATE_PRESERVE_CODE_AUTHZ_LEVEL
)
688 DPRINT1("Invalid flag used\n");
689 SetLastError(ERROR_INVALID_PARAMETER
);
693 /* This combination is illegal (see MSDN) */
694 if ((dwCreationFlags
& (DETACHED_PROCESS
| CREATE_NEW_CONSOLE
)) ==
695 (DETACHED_PROCESS
| CREATE_NEW_CONSOLE
))
697 DPRINT1("Invalid flag combo used\n");
698 SetLastError(ERROR_INVALID_PARAMETER
);
702 /* Another illegal combo */
703 if ((dwCreationFlags
& (CREATE_SEPARATE_WOW_VDM
| CREATE_SHARED_WOW_VDM
)) ==
704 (CREATE_SEPARATE_WOW_VDM
| CREATE_SHARED_WOW_VDM
))
706 DPRINT1("Invalid flag combo used\n");
707 SetLastError(ERROR_INVALID_PARAMETER
);
712 * We're going to modify and mask out flags and stuff in lpStartupInfo,
713 * so we'll use our own local copy for that.
715 StartupInfo
= *lpStartupInfo
;
717 /* FIXME: Use default Separate/Shared VDM Flag */
719 /* If we are inside a Job, use Separate VDM so it won't escape the Job */
720 if (!(dwCreationFlags
& CREATE_SEPARATE_WOW_VDM
))
722 if (NtIsProcessInJob(NtCurrentProcess(), NULL
))
724 /* Remove the shared flag and add the separate flag. */
725 dwCreationFlags
= (dwCreationFlags
&~ CREATE_SHARED_WOW_VDM
) |
726 CREATE_SEPARATE_WOW_VDM
;
731 * According to some sites, ShellExecuteEx uses an undocumented flag to
732 * send private handle data (such as HMONITOR or HICON). See:
733 * www.catch22.net/tuts/undoc01.asp. This implies that we can't use the
734 * standard handles anymore since we'd be overwriting this private data
736 if ((StartupInfo
.dwFlags
& STARTF_USESTDHANDLES
) &&
737 (StartupInfo
.dwFlags
& (STARTF_USEHOTKEY
| STARTF_SHELLPRIVATE
)))
739 StartupInfo
.dwFlags
&= ~STARTF_USESTDHANDLES
;
742 /* Start by zeroing out the fields */
743 RtlZeroMemory(lpProcessInformation
, sizeof(PROCESS_INFORMATION
));
745 /* Easy stuff first, convert the process priority class */
746 PriorityClass
.Foreground
= FALSE
;
747 PriorityClass
.PriorityClass
= BasepConvertPriorityClass(dwCreationFlags
);
751 /* Serach for escape sequences */
752 ScanString
= lpCommandLine
;
753 while (NULL
!= (ScanString
= wcschr(ScanString
, L
'^')))
756 if (*ScanString
== L
'\"' || *ScanString
== L
'^' || *ScanString
== L
'\"')
764 /* Get the application name and do all the proper formating necessary */
766 /* See if we have an application name (oh please let us have one!) */
767 if (!lpApplicationName
)
770 NameBuffer
= RtlAllocateHeap(GetProcessHeap(),
772 MAX_PATH
* sizeof(WCHAR
));
774 /* This is all we have to work with :( */
775 lpApplicationName
= lpCommandLine
;
777 /* Initialize our friends at the beginning */
778 NullBuffer
= (LPWSTR
)lpApplicationName
;
779 ScanString
= (LPWSTR
)lpApplicationName
;
781 /* We will start by looking for a quote */
782 if (*ScanString
== L
'\"')
787 /* Advance past quote */
789 lpApplicationName
= ScanString
;
791 /* Find the closing quote */
794 if (*ScanString
== L
'\"' && *(ScanString
- 1) != L
'^')
797 NullBuffer
= ScanString
;
804 NullBuffer
= ScanString
;
809 /* No quotes, so we'll be looking for white space */
811 /* Reset the pointer */
812 lpApplicationName
= lpCommandLine
;
814 /* Find whitespace of Tab */
817 if (*ScanString
== ' ' || *ScanString
== '\t')
820 NullBuffer
= ScanString
;
826 NullBuffer
= ScanString
;
830 /* Set the Null Buffer */
831 SaveChar
= *NullBuffer
;
832 *NullBuffer
= UNICODE_NULL
;
834 /* Do a search for the file */
835 DPRINT("Ready for SearchPathW: %S\n", lpApplicationName
);
836 RetVal
= SearchPathW(NULL
,
841 NULL
) * sizeof(WCHAR
);
843 /* Did it find something? */
846 /* Get file attributes */
847 ULONG Attributes
= GetFileAttributesW(NameBuffer
);
848 if (Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
850 /* Give it a length of 0 to fail, this was a directory. */
856 RetVal
+= sizeof(WCHAR
);
860 /* Now check if we have a file, and if the path size is OK */
861 if (!RetVal
|| RetVal
>= (MAX_PATH
* sizeof(WCHAR
)))
866 /* We failed, try to get the Path Type */
867 DPRINT("SearchPathW failed. Retval: %ld\n", RetVal
);
868 PathType
= RtlDetermineDosPathNameType_U(lpApplicationName
);
870 /* If it's not relative, try to get the error */
871 if (PathType
!= RELATIVE_PATH
)
873 /* This should fail, and give us a detailed LastError */
874 hFile
= CreateFileW(lpApplicationName
,
876 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
879 FILE_ATTRIBUTE_NORMAL
,
882 /* Did it actually NOT fail? */
883 if (hFile
!= INVALID_HANDLE_VALUE
)
887 SetLastErrorByStatus(STATUS_OBJECT_NAME_NOT_FOUND
);
892 /* Immediately set the error */
893 SetLastErrorByStatus(STATUS_OBJECT_NAME_NOT_FOUND
);
896 /* Did we already fail once? */
903 /* Not yet, cache it */
904 Error
= GetLastError();
907 /* Put back the command line */
908 *NullBuffer
= SaveChar
;
909 lpApplicationName
= NameBuffer
;
912 * If the search isn't done and we still have cmdline
913 * then start over. Ex: c:\ha ha ha\haha.exe
915 if (*ScanString
&& !SearchDone
)
917 /* Move in the buffer */
919 NullBuffer
= ScanString
;
921 /* We will have to add a quote, since there is a space*/
924 /* And we will also fake the fact we found one */
931 /* We totally failed */
935 /* Put back the command line */
936 *NullBuffer
= SaveChar
;
937 lpApplicationName
= NameBuffer
;
938 DPRINT("SearchPathW suceeded (%ld): %S\n", RetVal
, NameBuffer
);
940 else if (!lpCommandLine
|| *lpCommandLine
== UNICODE_NULL
)
942 /* We have an app name (good!) but no command line */
943 CmdLineIsAppName
= TRUE
;
944 lpCommandLine
= (LPWSTR
)lpApplicationName
;
947 /* At this point the name has been toyed with enough to be openable */
948 Status
= BasepMapFile(lpApplicationName
, &hSection
, &ApplicationName
);
950 /* Check for failure */
951 if (!NT_SUCCESS(Status
))
953 /* Could be a non-PE File */
956 /* Check if the Kernel tells us it's not even valid MZ */
957 case STATUS_INVALID_IMAGE_NE_FORMAT
:
958 case STATUS_INVALID_IMAGE_PROTECT
:
959 case STATUS_INVALID_IMAGE_NOT_MZ
:
962 /* If it's a DOS app, use VDM */
963 if ((BasepCheckDosApp(&ApplicationName
)))
965 DPRINT1("Launching VDM...\n");
966 RtlFreeHeap(GetProcessHeap(), 0, NameBuffer
);
967 RtlFreeHeap(GetProcessHeap(), 0, ApplicationName
.Buffer
);
968 return CreateProcessW(L
"ntvdm.exe",
969 (LPWSTR
)lpApplicationName
,
977 lpProcessInformation
);
980 /* It's a batch file */
981 Extension
= &ApplicationName
.Buffer
[ApplicationName
.Length
/
984 /* Make sure the extensions are correct */
985 if (_wcsnicmp(Extension
, L
".bat", 4) && _wcsnicmp(Extension
, L
".cmd", 4))
987 SetLastError(ERROR_BAD_EXE_FORMAT
);
991 /* Calculate the length of the command line */
992 CmdLineLength
= wcslen(CMD_STRING
) + wcslen(lpCommandLine
) + 1;
994 /* If we found quotes, then add them into the length size */
995 if (CmdLineIsAppName
|| FoundQuotes
) CmdLineLength
+= 2;
996 CmdLineLength
*= sizeof(WCHAR
);
998 /* Allocate space for the new command line */
999 BatchCommandLine
= RtlAllocateHeap(GetProcessHeap(),
1004 wcscpy(BatchCommandLine
, CMD_STRING
);
1005 if (CmdLineIsAppName
|| FoundQuotes
)
1007 wcscat(BatchCommandLine
, L
"\"");
1009 wcscat(BatchCommandLine
, lpCommandLine
);
1010 if (CmdLineIsAppName
|| FoundQuotes
)
1012 wcscat(BatchCommandLine
, L
"\"");
1015 /* Create it as a Unicode String */
1016 RtlInitUnicodeString(&CommandLineString
, BatchCommandLine
);
1018 /* Set the command line to this */
1019 lpCommandLine
= CommandLineString
.Buffer
;
1020 lpApplicationName
= NULL
;
1023 RtlFreeHeap(GetProcessHeap(), 0, ApplicationName
.Buffer
);
1024 ApplicationName
.Buffer
= NULL
;
1028 case STATUS_INVALID_IMAGE_WIN_16
:
1030 /* It's a Win16 Image, use VDM */
1031 DPRINT1("Launching VDM...\n");
1032 RtlFreeHeap(GetProcessHeap(), 0, NameBuffer
);
1033 RtlFreeHeap(GetProcessHeap(), 0, ApplicationName
.Buffer
);
1034 return CreateProcessW(L
"ntvdm.exe",
1035 (LPWSTR
)lpApplicationName
,
1036 lpProcessAttributes
,
1043 lpProcessInformation
);
1046 /* Invalid Image Type */
1047 SetLastError(ERROR_BAD_EXE_FORMAT
);
1052 /* Use our desktop if we didn't get any */
1053 if (!StartupInfo
.lpDesktop
)
1055 StartupInfo
.lpDesktop
= OurPeb
->ProcessParameters
->DesktopInfo
.Buffer
;
1058 /* FIXME: Check if Application is allowed to run */
1060 /* FIXME: Allow CREATE_SEPARATE only for WOW Apps, once we have that. */
1062 /* Get some information about the executable */
1063 Status
= ZwQuerySection(hSection
,
1064 SectionImageInformation
,
1066 sizeof(SectionImageInfo
),
1068 if(!NT_SUCCESS(Status
))
1071 DPRINT1("Unable to get SectionImageInformation, status 0x%x\n", Status
);
1072 SetLastErrorByStatus(Status
);
1076 /* Don't execute DLLs */
1077 if (SectionImageInfo
.ImageCharacteristics
& IMAGE_FILE_DLL
)
1080 DPRINT1("Can't execute a DLL\n");
1081 SetLastError(ERROR_BAD_EXE_FORMAT
);
1085 /* FIXME: Check for Debugger */
1087 /* FIXME: Check if Machine Type and SubSys Version Match */
1089 /* We don't support POSIX or anything else for now */
1090 if (IMAGE_SUBSYSTEM_WINDOWS_GUI
!= SectionImageInfo
.SubsystemType
&&
1091 IMAGE_SUBSYSTEM_WINDOWS_CUI
!= SectionImageInfo
.SubsystemType
)
1094 DPRINT1("Invalid subsystem %d\n", SectionImageInfo
.SubsystemType
);
1095 SetLastError(ERROR_BAD_EXE_FORMAT
);
1099 /* Initialize the process object attributes */
1100 ObjectAttributes
= BasepConvertObjectAttributes(&LocalObjectAttributes
,
1101 lpProcessAttributes
,
1104 /* Create the Process */
1105 Status
= NtCreateProcess(&hProcess
,
1113 if(!NT_SUCCESS(Status
))
1116 DPRINT1("Unable to create process, status 0x%x\n", Status
);
1117 SetLastErrorByStatus(Status
);
1122 Status
= NtSetInformationProcess(hProcess
,
1123 ProcessPriorityClass
,
1125 sizeof(PROCESS_PRIORITY_CLASS
));
1126 if(!NT_SUCCESS(Status
))
1130 DPRINT1("Unable to set new process priority, status 0x%x\n", Status
);
1131 SetLastErrorByStatus(Status
);
1135 /* Set Error Mode */
1136 if (dwCreationFlags
& CREATE_DEFAULT_ERROR_MODE
)
1138 ULONG ErrorMode
= SEM_FAILCRITICALERRORS
;
1139 NtSetInformationProcess(hProcess
,
1140 ProcessDefaultHardErrorMode
,
1145 /* Convert the directory to a full path */
1146 if (lpCurrentDirectory
)
1148 /* Allocate a buffer */
1149 CurrentDirectory
= RtlAllocateHeap(GetProcessHeap(),
1151 MAX_PATH
* sizeof(WCHAR
) + 2);
1153 /* Get the length */
1154 if (GetFullPathNameW(lpCurrentDirectory
,
1157 &CurrentDirectoryPart
) > MAX_PATH
)
1159 DPRINT1("Directory name too long\n");
1160 SetLastError(ERROR_DIRECTORY
);
1165 /* Insert quotes if needed */
1166 if (QuotesNeeded
|| CmdLineIsAppName
)
1168 /* Allocate a buffer */
1169 QuotedCmdLine
= RtlAllocateHeap(GetProcessHeap(),
1171 (wcslen(lpCommandLine
) + 2 + 1) *
1174 /* Copy the first quote */
1175 wcscpy(QuotedCmdLine
, L
"\"");
1177 /* Save a null char */
1180 SaveChar
= *NullBuffer
;
1181 *NullBuffer
= UNICODE_NULL
;
1184 /* Add the command line and the finishing quote */
1185 wcscat(QuotedCmdLine
, lpCommandLine
);
1186 wcscat(QuotedCmdLine
, L
"\"");
1188 /* Add the null char */
1191 *NullBuffer
= SaveChar
;
1192 wcscat(QuotedCmdLine
, NullBuffer
);
1195 DPRINT("Quoted CmdLine: %S\n", QuotedCmdLine
);
1200 if (QuotedCmdLine
== NULL
)
1202 QuotedCmdLine
= RtlAllocateHeap(GetProcessHeap(),
1204 (wcslen(lpCommandLine
) + 1) * sizeof(WCHAR
));
1205 wcscpy(QuotedCmdLine
, lpCommandLine
);
1208 ScanString
= QuotedCmdLine
;
1209 while (NULL
!= (ScanString
= wcschr(ScanString
, L
'^')))
1212 if (*ScanString
== L
'\"' || *ScanString
== L
'^' || *ScanString
== L
'\\')
1214 memmove(ScanString
-1, ScanString
, wcslen(ScanString
) * sizeof(WCHAR
) + sizeof(WCHAR
));
1219 /* Get the Process Information */
1220 Status
= NtQueryInformationProcess(hProcess
,
1221 ProcessBasicInformation
,
1223 sizeof(ProcessBasicInfo
),
1226 /* Convert the environment */
1227 if(lpEnvironment
&& !(dwCreationFlags
& CREATE_UNICODE_ENVIRONMENT
))
1229 lpEnvironment
= BasepConvertUnicodeEnvironment(&EnvSize
, lpEnvironment
);
1230 if (!lpEnvironment
) return FALSE
;
1233 /* Create Process Environment */
1234 RemotePeb
= ProcessBasicInfo
.PebBaseAddress
;
1235 Status
= BasepInitializeEnvironment(hProcess
,
1237 (LPWSTR
)lpApplicationName
,
1239 (QuotesNeeded
|| CmdLineIsAppName
|| Escape
) ?
1240 QuotedCmdLine
: lpCommandLine
,
1247 /* Cleanup Environment */
1248 if (lpEnvironment
&& !(dwCreationFlags
& CREATE_UNICODE_ENVIRONMENT
))
1250 RtlDestroyEnvironment(lpEnvironment
);
1253 if (!NT_SUCCESS(Status
))
1255 DPRINT1("Could not initialize Process Environment\n");
1256 SetLastErrorByStatus(Status
);
1260 /* Close the section */
1264 /* Duplicate the handles if needed */
1265 if (!bInheritHandles
&& !(StartupInfo
.dwFlags
& STARTF_USESTDHANDLES
) &&
1266 SectionImageInfo
.SubsystemType
== IMAGE_SUBSYSTEM_WINDOWS_CUI
)
1268 PRTL_USER_PROCESS_PARAMETERS RemoteParameters
;
1270 /* Get the remote parameters */
1271 Status
= NtReadVirtualMemory(hProcess
,
1272 &RemotePeb
->ProcessParameters
,
1276 if (!NT_SUCCESS(Status
))
1278 DPRINT1("Failed to read memory\n");
1282 /* Duplicate and write the handles */
1283 BasepDuplicateAndWriteHandle(hProcess
,
1284 OurPeb
->ProcessParameters
->StandardInput
,
1285 &RemoteParameters
->StandardInput
);
1286 BasepDuplicateAndWriteHandle(hProcess
,
1287 OurPeb
->ProcessParameters
->StandardOutput
,
1288 &RemoteParameters
->StandardOutput
);
1289 BasepDuplicateAndWriteHandle(hProcess
,
1290 OurPeb
->ProcessParameters
->StandardError
,
1291 &RemoteParameters
->StandardError
);
1294 /* Create the first thread */
1295 DPRINT("Creating thread for process (EntryPoint = 0x%.08x)\n",
1296 SectionImageInfo
.TransferAddress
);
1297 hThread
= BasepCreateFirstThread(hProcess
,
1302 if (hThread
== NULL
)
1304 DPRINT1("Could not create Initial Thread\n");
1310 Status
= BasepNotifyCsrOfCreation(dwCreationFlags
,
1311 (HANDLE
)ProcessBasicInfo
.UniqueProcessId
,
1314 if (!NT_SUCCESS(Status
))
1316 DPRINT1("CSR Notification Failed");
1317 SetLastErrorByStatus(Status
);
1321 if (!(dwCreationFlags
& CREATE_SUSPENDED
))
1323 NtResumeThread(hThread
, &Dummy
);
1327 lpProcessInformation
->dwProcessId
= (DWORD
)ClientId
.UniqueProcess
;
1328 lpProcessInformation
->dwThreadId
= (DWORD
)ClientId
.UniqueThread
;
1329 lpProcessInformation
->hProcess
= hProcess
;
1330 lpProcessInformation
->hThread
= hThread
;
1331 DPRINT("hThread[%lx]: %lx inside hProcess[%lx]: %lx\n", hThread
,
1332 ClientId
.UniqueThread
, ClientId
.UniqueProcess
, hProcess
);
1333 hProcess
= hThread
= NULL
;
1335 /* De-allocate heap strings */
1336 if (NameBuffer
) RtlFreeHeap(GetProcessHeap(), 0, NameBuffer
);
1337 if (ApplicationName
.Buffer
)
1338 RtlFreeHeap(GetProcessHeap(), 0, ApplicationName
.Buffer
);
1339 if (CurrentDirectory
) RtlFreeHeap(GetProcessHeap(), 0, CurrentDirectory
);
1340 if (QuotedCmdLine
) RtlFreeHeap(GetProcessHeap(), 0, QuotedCmdLine
);
1342 /* Kill any handles still alive */
1343 if (hSection
) NtClose(hSection
);
1346 /* We don't know any more details then this */
1347 NtTerminateProcess(hProcess
, STATUS_UNSUCCESSFUL
);
1350 if (hProcess
) NtClose(hProcess
);
1352 /* Return Success */
1361 CreateProcessW(LPCWSTR lpApplicationName
,
1362 LPWSTR lpCommandLine
,
1363 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
1364 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
1365 BOOL bInheritHandles
,
1366 DWORD dwCreationFlags
,
1367 LPVOID lpEnvironment
,
1368 LPCWSTR lpCurrentDirectory
,
1369 LPSTARTUPINFOW lpStartupInfo
,
1370 LPPROCESS_INFORMATION lpProcessInformation
)
1372 /* Call the internal (but exported) version */
1373 return CreateProcessInternalW(0,
1376 lpProcessAttributes
,
1383 lpProcessInformation
,
1392 CreateProcessInternalA(HANDLE hToken
,
1393 LPCSTR lpApplicationName
,
1394 LPSTR lpCommandLine
,
1395 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
1396 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
1397 BOOL bInheritHandles
,
1398 DWORD dwCreationFlags
,
1399 LPVOID lpEnvironment
,
1400 LPCSTR lpCurrentDirectory
,
1401 LPSTARTUPINFOA lpStartupInfo
,
1402 LPPROCESS_INFORMATION lpProcessInformation
,
1405 PUNICODE_STRING CommandLine
= NULL
;
1406 UNICODE_STRING DummyString
;
1407 UNICODE_STRING LiveCommandLine
;
1408 UNICODE_STRING ApplicationName
;
1409 UNICODE_STRING CurrentDirectory
;
1411 STARTUPINFOW StartupInfo
;
1413 DPRINT("dwCreationFlags %x, lpEnvironment %x, lpCurrentDirectory %x, "
1414 "lpStartupInfo %x, lpProcessInformation %x\n",
1415 dwCreationFlags
, lpEnvironment
, lpCurrentDirectory
,
1416 lpStartupInfo
, lpProcessInformation
);
1418 /* Copy Startup Info */
1419 RtlMoveMemory(&StartupInfo
, lpStartupInfo
, sizeof(*lpStartupInfo
));
1421 /* Initialize all strings to nothing */
1422 LiveCommandLine
.Buffer
= NULL
;
1423 DummyString
.Buffer
= NULL
;
1424 ApplicationName
.Buffer
= NULL
;
1425 CurrentDirectory
.Buffer
= NULL
;
1426 StartupInfo
.lpDesktop
= NULL
;
1427 StartupInfo
.lpReserved
= NULL
;
1428 StartupInfo
.lpTitle
= NULL
;
1430 /* Convert the Command line */
1433 /* If it's too long, then we'll have a problem */
1434 if ((strlen(lpCommandLine
) + 1) * sizeof(WCHAR
) <
1435 NtCurrentTeb()->StaticUnicodeString
.MaximumLength
)
1437 /* Cache it in the TEB */
1438 CommandLine
= Basep8BitStringToCachedUnicodeString(lpCommandLine
);
1442 /* Use a dynamic version */
1443 Basep8BitStringToHeapUnicodeString(&LiveCommandLine
,
1449 /* The logic below will use CommandLine, so we must make it valid */
1450 CommandLine
= &DummyString
;
1453 /* Convert the Name and Directory */
1454 if (lpApplicationName
)
1456 Basep8BitStringToHeapUnicodeString(&ApplicationName
,
1459 if (lpCurrentDirectory
)
1461 Basep8BitStringToHeapUnicodeString(&CurrentDirectory
,
1462 lpCurrentDirectory
);
1465 /* Now convert Startup Strings */
1466 if (lpStartupInfo
->lpReserved
)
1468 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpReserved
,
1469 &StartupInfo
.lpReserved
);
1471 if (lpStartupInfo
->lpDesktop
)
1473 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpDesktop
,
1474 &StartupInfo
.lpDesktop
);
1476 if (lpStartupInfo
->lpTitle
)
1478 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpTitle
,
1479 &StartupInfo
.lpTitle
);
1482 /* Call the Unicode function */
1483 bRetVal
= CreateProcessInternalW(hToken
,
1484 ApplicationName
.Buffer
,
1485 LiveCommandLine
.Buffer
?
1486 LiveCommandLine
.Buffer
: CommandLine
->Buffer
,
1487 lpProcessAttributes
,
1492 CurrentDirectory
.Buffer
,
1494 lpProcessInformation
,
1498 RtlFreeUnicodeString(&ApplicationName
);
1499 RtlFreeUnicodeString(&LiveCommandLine
);
1500 RtlFreeUnicodeString(&CurrentDirectory
);
1501 RtlFreeHeap(GetProcessHeap(), 0, StartupInfo
.lpDesktop
);
1502 RtlFreeHeap(GetProcessHeap(), 0, StartupInfo
.lpReserved
);
1503 RtlFreeHeap(GetProcessHeap(), 0, StartupInfo
.lpTitle
);
1505 /* Return what Unicode did */
1510 * FUNCTION: The CreateProcess function creates a new process and its
1511 * primary thread. The new process executes the specified executable file
1514 * lpApplicationName = Pointer to name of executable module
1515 * lpCommandLine = Pointer to command line string
1516 * lpProcessAttributes = Process security attributes
1517 * lpThreadAttributes = Thread security attributes
1518 * bInheritHandles = Handle inheritance flag
1519 * dwCreationFlags = Creation flags
1520 * lpEnvironment = Pointer to new environment block
1521 * lpCurrentDirectory = Pointer to current directory name
1522 * lpStartupInfo = Pointer to startup info
1523 * lpProcessInformation = Pointer to process information
1529 CreateProcessA(LPCSTR lpApplicationName
,
1530 LPSTR lpCommandLine
,
1531 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
1532 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
1533 BOOL bInheritHandles
,
1534 DWORD dwCreationFlags
,
1535 LPVOID lpEnvironment
,
1536 LPCSTR lpCurrentDirectory
,
1537 LPSTARTUPINFOA lpStartupInfo
,
1538 LPPROCESS_INFORMATION lpProcessInformation
)
1540 /* Call the internal (but exported) version */
1541 return CreateProcessInternalA(0,
1544 lpProcessAttributes
,
1551 lpProcessInformation
,