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 ****************************************************************/
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
);
39 ExceptionDisposition
= UnhandledExceptionFilter(ExceptionInfo
);
44 return ExceptionDisposition
;
49 BaseProcessStartup(PPROCESS_START_ROUTINE lpStartAddress
)
53 DPRINT("BaseProcessStartup(..) - setting up exception frame.\n");
57 /* Set our Start Address */
58 NtSetInformationThread(NtCurrentThread(),
59 ThreadQuerySetWin32StartAddress
,
61 sizeof(PPROCESS_START_ROUTINE
));
63 /* Call the Start Routine */
64 uExitCode
= (lpStartAddress
)();
66 _SEH_EXCEPT(BaseExceptionFilter
)
68 /* Get the SEH Error */
69 uExitCode
= _SEH_GetExceptionCode();
73 /* Exit the Process with our error */
74 ExitProcess(uExitCode
);
78 * Tells CSR that a new process was created
82 BasepNotifyCsrOfCreation(ULONG dwCreationFlags
,
84 IN ULONG SubsystemType
,
85 OUT PHANDLE ConsoleHandle
,
86 OUT PHANDLE InputHandle
,
87 OUT PHANDLE OutputHandle
)
89 ULONG Request
= CREATE_PROCESS
;
90 CSR_API_MESSAGE CsrRequest
;
93 DPRINT("BasepNotifyCsrOfCreation\n");
95 /* Some hacks (heck, this whole API is a hack) */
96 if (SubsystemType
== IMAGE_SUBSYSTEM_WINDOWS_GUI
)
98 dwCreationFlags
= (dwCreationFlags
&~ CREATE_NEW_CONSOLE
) |
101 else if (SubsystemType
== IMAGE_SUBSYSTEM_WINDOWS_CUI
)
103 dwCreationFlags
|= CREATE_NEW_CONSOLE
;
106 /* Fill out the request */
107 CsrRequest
.Data
.CreateProcessRequest
.NewProcessId
= ProcessId
;
108 CsrRequest
.Data
.CreateProcessRequest
.Flags
= dwCreationFlags
;
109 CsrRequest
.Data
.CreateProcessRequest
.CtrlDispatcher
= ConsoleControlDispatcher
;
110 CsrRequest
.Data
.CreateProcessRequest
.InputHandle
= 0;
111 CsrRequest
.Data
.CreateProcessRequest
.OutputHandle
= 0;
112 CsrRequest
.Data
.CreateProcessRequest
.Console
= 0;
115 Status
= CsrClientCallServer(&CsrRequest
,
117 MAKE_CSR_API(Request
, CSR_NATIVE
),
118 sizeof(CSR_API_MESSAGE
));
119 if (!NT_SUCCESS(Status
) || !NT_SUCCESS(CsrRequest
.Status
))
121 DPRINT1("Failed to tell csrss about new process\n");
122 return CsrRequest
.Status
;
125 *ConsoleHandle
= CsrRequest
.Data
.CreateProcessRequest
.Console
;
126 *InputHandle
= CsrRequest
.Data
.CreateProcessRequest
.InputHandle
;
127 *OutputHandle
= CsrRequest
.Data
.CreateProcessRequest
.OutputHandle
;
128 DPRINT("CSR Created: %lx %lx\n", *InputHandle
, *OutputHandle
);
131 return STATUS_SUCCESS
;
135 * Creates the first Thread in a Proces
139 BasepCreateFirstThread(HANDLE ProcessHandle
,
140 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
141 PSECTION_IMAGE_INFORMATION SectionImageInfo
,
144 OBJECT_ATTRIBUTES LocalObjectAttributes
;
145 POBJECT_ATTRIBUTES ObjectAttributes
;
147 INITIAL_TEB InitialTeb
;
151 DPRINT("BasepCreateFirstThread. hProcess: %lx\n", ProcessHandle
);
153 /* Create the Thread's Stack */
154 BasepCreateStack(ProcessHandle
,
155 SectionImageInfo
->MaximumStackSize
,
156 SectionImageInfo
->CommittedStackSize
,
159 /* Create the Thread's Context */
160 BasepInitializeContext(&Context
,
162 SectionImageInfo
->TransferAddress
,
163 InitialTeb
.StackBase
,
166 /* Convert the thread attributes */
167 ObjectAttributes
= BasepConvertObjectAttributes(&LocalObjectAttributes
,
171 /* Create the Kernel Thread Object */
172 Status
= NtCreateThread(&hThread
,
186 * Converts ANSI to Unicode Environment
190 BasepConvertUnicodeEnvironment(IN PVOID lpEnvironment
)
195 UNICODE_STRING UnicodeEnv
;
198 DPRINT("BasepConvertUnicodeEnvironment\n");
200 /* Scan the environment to calculate its Unicode size */
201 AnsiEnv
.Buffer
= pcScan
= lpEnvironment
;
202 while (*pcScan
) while (*pcScan
++);
204 /* Create our ANSI String */
205 AnsiEnv
.Length
= (ULONG_PTR
)pcScan
- (ULONG_PTR
)lpEnvironment
+ 1;
206 AnsiEnv
.MaximumLength
= AnsiEnv
.Length
+ 1;
208 /* Allocate memory for the Unicode Environment */
209 UnicodeEnv
.Buffer
= NULL
;
210 EnvSize
= AnsiEnv
.MaximumLength
* sizeof(WCHAR
);
211 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
212 (PVOID
)&UnicodeEnv
.Buffer
,
218 if (!NT_SUCCESS(Status
))
220 SetLastError(Status
);
224 /* Use the allocated size */
225 UnicodeEnv
.MaximumLength
= EnvSize
;
228 RtlAnsiStringToUnicodeString(&UnicodeEnv
, &AnsiEnv
, FALSE
);
229 return UnicodeEnv
.Buffer
;
233 * Converts a Win32 Priority Class to NT
237 BasepConvertPriorityClass(IN ULONG dwCreationFlags
)
241 if(dwCreationFlags
& IDLE_PRIORITY_CLASS
)
243 ReturnClass
= PROCESS_PRIORITY_CLASS_IDLE
;
245 else if(dwCreationFlags
& BELOW_NORMAL_PRIORITY_CLASS
)
247 ReturnClass
= PROCESS_PRIORITY_CLASS_BELOW_NORMAL
;
249 else if(dwCreationFlags
& NORMAL_PRIORITY_CLASS
)
251 ReturnClass
= PROCESS_PRIORITY_CLASS_NORMAL
;
253 else if(dwCreationFlags
& ABOVE_NORMAL_PRIORITY_CLASS
)
255 ReturnClass
= PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
;
257 else if(dwCreationFlags
& HIGH_PRIORITY_CLASS
)
259 ReturnClass
= PROCESS_PRIORITY_CLASS_HIGH
;
261 else if(dwCreationFlags
& REALTIME_PRIORITY_CLASS
)
263 /* Check for Privilege First */
264 if (BasepCheckRealTimePrivilege())
266 ReturnClass
= PROCESS_PRIORITY_CLASS_REALTIME
;
270 ReturnClass
= PROCESS_PRIORITY_CLASS_HIGH
;
275 ReturnClass
= 0 /* FIXME */;
282 * Duplicates a standard handle and writes it where requested.
286 BasepDuplicateAndWriteHandle(IN HANDLE ProcessHandle
,
287 IN HANDLE StandardHandle
,
291 HANDLE DuplicatedHandle
;
294 DPRINT("BasepDuplicateAndWriteHandle. hProcess: %lx, Handle: %lx,"
295 "Address: %p\n", ProcessHandle
, StandardHandle
, Address
);
297 /* Don't touch Console Handles */
298 if (IsConsoleHandle(StandardHandle
)) return;
300 /* Duplicate the handle */
301 Status
= NtDuplicateObject(NtCurrentProcess(),
305 DUPLICATE_SAME_ACCESS
| DUPLICATE_SAME_ATTRIBUTES
,
308 if (NT_SUCCESS(Status
))
311 NtWriteVirtualMemory(ProcessHandle
,
321 BasepGetDllPath(LPWSTR FullPath
,
324 /* FIXME: Not yet implemented */
330 BasepCopyHandles(IN PRTL_USER_PROCESS_PARAMETERS Params
,
331 IN PRTL_USER_PROCESS_PARAMETERS PebParams
,
332 IN BOOL InheritHandles
)
334 /* Copy the handle if we are inheriting or if it's a console handle */
335 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardInput
))
337 Params
->StandardInput
= PebParams
->StandardInput
;
339 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardOutput
))
341 Params
->StandardOutput
= PebParams
->StandardOutput
;
343 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardError
))
345 Params
->StandardError
= PebParams
->StandardError
;
351 BasepInitializeEnvironment(HANDLE ProcessHandle
,
353 LPWSTR ApplicationPathName
,
354 LPWSTR lpCurrentDirectory
,
355 LPWSTR lpCommandLine
,
357 LPSTARTUPINFOW StartupInfo
,
363 WCHAR FullPath
[MAX_PATH
];
365 LPWSTR DllPathString
;
366 PRTL_USER_PROCESS_PARAMETERS ProcessParameters
;
367 PRTL_USER_PROCESS_PARAMETERS RemoteParameters
= NULL
;
368 UNICODE_STRING DllPath
, ImageName
, CommandLine
, CurrentDirectory
;
374 UNICODE_STRING Desktop
, Shell
, Runtime
, Title
;
375 PPEB OurPeb
= NtCurrentPeb();
377 DPRINT("BasepInitializeEnvironment\n");
379 /* Get the full path name */
380 RetVal
= GetFullPathNameW(ApplicationPathName
,
384 DPRINT("ApplicationPathName: %S, FullPath: %S\n", ApplicationPathName
,
387 /* Get the DLL Path */
388 DllPathString
= BasepGetDllPath(FullPath
, Environment
);
390 /* Initialize Strings */
391 RtlInitUnicodeString(&DllPath
, DllPathString
);
392 RtlInitUnicodeString(&ImageName
, FullPath
);
393 RtlInitUnicodeString(&CommandLine
, lpCommandLine
);
394 RtlInitUnicodeString(&CurrentDirectory
, lpCurrentDirectory
);
396 /* Initialize more Strings from the Startup Info */
397 if (StartupInfo
->lpDesktop
)
399 RtlInitUnicodeString(&Desktop
, StartupInfo
->lpDesktop
);
403 RtlInitUnicodeString(&Desktop
, L
"");
405 if (StartupInfo
->lpReserved
)
407 RtlInitUnicodeString(&Shell
, StartupInfo
->lpReserved
);
411 RtlInitUnicodeString(&Shell
, L
"");
413 if (StartupInfo
->lpTitle
)
415 RtlInitUnicodeString(&Title
, StartupInfo
->lpTitle
);
419 RtlInitUnicodeString(&Title
, L
"");
422 /* This one is special because the length can differ */
423 Runtime
.Buffer
= (LPWSTR
)StartupInfo
->lpReserved2
;
424 Runtime
.MaximumLength
= Runtime
.Length
= StartupInfo
->cbReserved2
;
426 /* Create the Parameter Block */
427 DPRINT("Creating Process Parameters: %wZ %wZ %wZ %wZ %wZ %wZ %wZ\n",
428 &ImageName
, &DllPath
, &CommandLine
, &Desktop
, &Title
, &Shell
,
430 Status
= RtlCreateProcessParameters(&ProcessParameters
,
434 &CurrentDirectory
: NULL
,
442 if (!NT_SUCCESS(Status
))
444 DPRINT1("Failed to create process parameters!\n");
448 /* Check if we got an environment. If not, use ours */
451 /* Save pointer and start lookup */
452 Environment
= ScanChar
= ProcessParameters
->Environment
;
456 /* Save pointer and start lookup */
457 Environment
= ScanChar
= OurPeb
->ProcessParameters
->Environment
;
460 /* Find the environment size */
463 while (*ScanChar
) while (*ScanChar
++);
465 /* Calculate the size of the block */
466 EnviroSize
= (ULONG
)((ULONG_PTR
)ScanChar
- (ULONG_PTR
)Environment
);
467 DPRINT("EnvironmentSize %ld\n", EnviroSize
);
469 /* Allocate and Initialize new Environment Block */
471 ProcessParameters
->Environment
= NULL
;
472 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
473 (PVOID
*)&ProcessParameters
->Environment
,
478 if (!NT_SUCCESS(Status
))
480 DPRINT1("Failed to allocate Environment Block\n");
484 /* Write the Environment Block */
485 ZwWriteVirtualMemory(ProcessHandle
,
486 ProcessParameters
->Environment
,
492 /* Write new parameters */
493 ProcessParameters
->StartingX
= StartupInfo
->dwX
;
494 ProcessParameters
->StartingY
= StartupInfo
->dwY
;
495 ProcessParameters
->CountX
= StartupInfo
->dwXSize
;
496 ProcessParameters
->CountY
= StartupInfo
->dwYSize
;
497 ProcessParameters
->CountCharsX
= StartupInfo
->dwXCountChars
;
498 ProcessParameters
->CountCharsY
= StartupInfo
->dwYCountChars
;
499 ProcessParameters
->FillAttribute
= StartupInfo
->dwFillAttribute
;
500 ProcessParameters
->WindowFlags
= StartupInfo
->dwFlags
;
501 ProcessParameters
->ShowWindowFlags
= StartupInfo
->wShowWindow
;
503 /* Write the handles only if we have to */
504 if (StartupInfo
->dwFlags
& STARTF_USESTDHANDLES
)
506 ProcessParameters
->StandardInput
= StartupInfo
->hStdInput
;
507 ProcessParameters
->StandardOutput
= StartupInfo
->hStdOutput
;
508 ProcessParameters
->StandardError
= StartupInfo
->hStdError
;
511 /* Use Special Flags for ConDllInitialize in Kernel32 */
512 if (CreationFlags
& DETACHED_PROCESS
)
514 ProcessParameters
->ConsoleHandle
= HANDLE_DETACHED_PROCESS
;
516 else if (CreationFlags
& CREATE_NO_WINDOW
)
518 ProcessParameters
->ConsoleHandle
= HANDLE_CREATE_NO_WINDOW
;
520 else if (CreationFlags
& CREATE_NEW_CONSOLE
)
522 ProcessParameters
->ConsoleHandle
= HANDLE_CREATE_NEW_CONSOLE
;
526 /* Inherit our Console Handle */
527 ProcessParameters
->ConsoleHandle
= OurPeb
->ProcessParameters
->ConsoleHandle
;
529 /* Is the shell trampling on our Handles? */
530 if (!(StartupInfo
->dwFlags
&
531 (STARTF_USESTDHANDLES
| STARTF_USEHOTKEY
| STARTF_SHELLPRIVATE
)))
533 /* Use handles from PEB, if inheriting or they are console */
534 BasepCopyHandles(ProcessParameters
,
535 OurPeb
->ProcessParameters
,
540 /* Also set the Console Flag */
541 if (CreationFlags
& CREATE_NEW_PROCESS_GROUP
)
543 ProcessParameters
->ConsoleFlags
= 1;
546 /* Allocate memory for the parameter block */
547 Size
= ProcessParameters
->Length
;
548 Status
= NtAllocateVirtualMemory(ProcessHandle
,
549 (PVOID
*)&RemoteParameters
,
554 if (!NT_SUCCESS(Status
))
556 DPRINT1("Failed to allocate Parameters Block\n");
560 /* Set the allocated size */
561 ProcessParameters
->MaximumLength
= Size
;
563 /* Handle some Parameter Flags */
564 ProcessParameters
->ConsoleFlags
= (CreationFlags
& CREATE_NEW_PROCESS_GROUP
);
565 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_USER
) ?
566 PPF_PROFILE_USER
: 0;
567 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_KERNEL
) ?
568 PPF_PROFILE_KERNEL
: 0;
569 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_SERVER
) ?
570 PPF_PROFILE_SERVER
: 0;
571 ProcessParameters
->Flags
|= (NtCurrentPeb()->ProcessParameters
->Flags
&
572 PPF_DISABLE_HEAP_CHECKS
);
575 * FIXME: Our console init stuff is messy. See my comment in kernel32's
578 if (!ProcessParameters
->StandardInput
) ProcessParameters
->StandardInput
= hInput
;
579 if (!ProcessParameters
->StandardOutput
) ProcessParameters
->StandardOutput
= hOutput
;
580 if (!ProcessParameters
->StandardError
) ProcessParameters
->StandardError
= hOutput
;
582 /* Write the Parameter Block */
583 Status
= NtWriteVirtualMemory(ProcessHandle
,
586 ProcessParameters
->Length
,
589 /* Write the PEB Pointer */
590 Status
= NtWriteVirtualMemory(ProcessHandle
,
591 &Peb
->ProcessParameters
,
597 RtlFreeHeap(GetProcessHeap(), 0, DllPath
.Buffer
);
598 RtlDestroyProcessParameters(ProcessParameters
);
600 DPRINT("Completed\n");
601 return STATUS_SUCCESS
;
604 /* FUNCTIONS ****************************************************************/
607 * FUNCTION: The CreateProcess function creates a new process and its
608 * primary thread. The new process executes the specified executable file
611 * lpApplicationName = Pointer to name of executable module
612 * lpCommandLine = Pointer to command line string
613 * lpProcessAttributes = Process security attributes
614 * lpThreadAttributes = Thread security attributes
615 * bInheritHandles = Handle inheritance flag
616 * dwCreationFlags = Creation flags
617 * lpEnvironment = Pointer to new environment block
618 * lpCurrentDirectory = Pointer to current directory name
619 * lpStartupInfo = Pointer to startup info
620 * lpProcessInformation = Pointer to process information
626 CreateProcessA(LPCSTR lpApplicationName
,
628 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
629 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
630 BOOL bInheritHandles
,
631 DWORD dwCreationFlags
,
632 LPVOID lpEnvironment
,
633 LPCSTR lpCurrentDirectory
,
634 LPSTARTUPINFOA lpStartupInfo
,
635 LPPROCESS_INFORMATION lpProcessInformation
)
637 PUNICODE_STRING CommandLine
= NULL
;
638 UNICODE_STRING DummyString
;
639 UNICODE_STRING LiveCommandLine
;
640 UNICODE_STRING ApplicationName
;
641 UNICODE_STRING CurrentDirectory
;
643 STARTUPINFOW StartupInfo
;
645 DPRINT("dwCreationFlags %x, lpEnvironment %x, lpCurrentDirectory %x, "
646 "lpStartupInfo %x, lpProcessInformation %x\n",
647 dwCreationFlags
, lpEnvironment
, lpCurrentDirectory
,
648 lpStartupInfo
, lpProcessInformation
);
650 /* Copy Startup Info */
652 RtlMoveMemory(&StartupInfo
, lpStartupInfo
, sizeof(*lpStartupInfo
));
655 /* Initialize all strings to nothing */
656 LiveCommandLine
.Buffer
= NULL
;
657 DummyString
.Buffer
= NULL
;
658 ApplicationName
.Buffer
= NULL
;
659 CurrentDirectory
.Buffer
= NULL
;
660 StartupInfo
.lpDesktop
= NULL
;
661 StartupInfo
.lpReserved
= NULL
;
662 StartupInfo
.lpTitle
= NULL
;
664 /* Convert the Command line */
668 /* If it's too long, then we'll have a problem */
669 if ((strlen(lpCommandLine
) + 1) * sizeof(WCHAR
) <
670 NtCurrentTeb()->StaticUnicodeString
.MaximumLength
)
672 /* Cache it in the TEB */
674 CommandLine
= Basep8BitStringToCachedUnicodeString(lpCommandLine
);
678 /* Use a dynamic version */
680 Basep8BitStringToLiveUnicodeString(&LiveCommandLine
,
687 /* The logic below will use CommandLine, so we must make it valid */
688 CommandLine
= &DummyString
;
691 /* Convert the Name and Directory */
692 if (lpApplicationName
)
695 Basep8BitStringToLiveUnicodeString(&ApplicationName
,
698 if (lpCurrentDirectory
)
701 Basep8BitStringToLiveUnicodeString(&CurrentDirectory
,
705 /* Now convert Startup Strings */
706 if (lpStartupInfo
->lpReserved
)
709 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpReserved
,
710 &StartupInfo
.lpReserved
);
712 if (lpStartupInfo
->lpDesktop
)
715 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpDesktop
,
716 &StartupInfo
.lpDesktop
);
718 if (lpStartupInfo
->lpTitle
)
721 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpTitle
,
722 &StartupInfo
.lpTitle
);
725 /* Call the Unicode function */
726 bRetVal
= CreateProcessW(ApplicationName
.Buffer
,
727 LiveCommandLine
.Buffer
?
728 LiveCommandLine
.Buffer
: CommandLine
->Buffer
,
734 CurrentDirectory
.Buffer
,
736 lpProcessInformation
);
739 RtlFreeUnicodeString(&ApplicationName
);
740 RtlFreeUnicodeString(&LiveCommandLine
);
741 RtlFreeUnicodeString(&CurrentDirectory
);
742 RtlFreeHeap(GetProcessHeap(), 0, StartupInfo
.lpDesktop
);
743 RtlFreeHeap(GetProcessHeap(), 0, StartupInfo
.lpReserved
);
744 RtlFreeHeap(GetProcessHeap(), 0, StartupInfo
.lpTitle
);
746 /* Return what Unicode did */
755 CreateProcessW(LPCWSTR lpApplicationName
,
756 LPWSTR lpCommandLine
,
757 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
758 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
759 BOOL bInheritHandles
,
760 DWORD dwCreationFlags
,
761 LPVOID lpEnvironment
,
762 LPCWSTR lpCurrentDirectory
,
763 LPSTARTUPINFOW lpStartupInfo
,
764 LPPROCESS_INFORMATION lpProcessInformation
)
767 PROCESS_PRIORITY_CLASS PriorityClass
;
768 BOOLEAN FoundQuotes
= FALSE
;
769 BOOLEAN QuotesNeeded
= FALSE
;
770 BOOLEAN CmdLineIsAppName
= FALSE
;
771 UNICODE_STRING ApplicationName
;
772 OBJECT_ATTRIBUTES LocalObjectAttributes
;
773 POBJECT_ATTRIBUTES ObjectAttributes
;
774 HANDLE hSection
, hProcess
, hThread
;
775 SECTION_IMAGE_INFORMATION SectionImageInfo
;
776 LPWSTR CurrentDirectory
= NULL
;
777 LPWSTR CurrentDirectoryPart
;
778 PROCESS_BASIC_INFORMATION ProcessBasicInfo
;
779 STARTUPINFOW StartupInfo
;
781 LPWSTR QuotedCmdLine
= NULL
;
784 LPWSTR NameBuffer
= NULL
;
788 BOOLEAN SearchDone
= FALSE
;
789 HANDLE hConsole
, hInput
, hOutput
;
791 PPEB OurPeb
= NtCurrentPeb();
794 DPRINT("CreateProcessW: lpApplicationName: %S lpCommandLine: %S"
795 " lpEnvironment: %p lpCurrentDirectory: %S dwCreationFlags: %lx\n",
796 lpApplicationName
, lpCommandLine
, lpEnvironment
, lpCurrentDirectory
,
799 /* Flags we don't handle yet */
800 if (dwCreationFlags
& CREATE_SEPARATE_WOW_VDM
)
802 DPRINT1("CREATE_SEPARATE_WOW_VDM not handled\n");
804 if (dwCreationFlags
& CREATE_SHARED_WOW_VDM
)
806 DPRINT1("CREATE_SHARED_WOW_VDM not handled\n");
808 if (dwCreationFlags
& CREATE_FORCEDOS
)
810 DPRINT1("CREATE_FORCEDOS not handled\n");
813 /* Fail on this flag, it's only valid with the WithLogonW function */
814 if (dwCreationFlags
& CREATE_WITH_USERPROFILE
)
816 DPRINT1("Invalid flag used\n");
817 SetLastError(ERROR_INVALID_PARAMETER
);
821 /* This combination is illegal (see MSDN) */
822 if ((dwCreationFlags
& (DETACHED_PROCESS
| CREATE_NEW_CONSOLE
)) ==
823 (DETACHED_PROCESS
| CREATE_NEW_CONSOLE
))
825 DPRINT1("Invalid flag combo used\n");
826 SetLastError(ERROR_INVALID_PARAMETER
);
830 /* Another illegal combo */
831 if ((dwCreationFlags
& (CREATE_SEPARATE_WOW_VDM
| CREATE_SHARED_WOW_VDM
)) ==
832 (CREATE_SEPARATE_WOW_VDM
| CREATE_SHARED_WOW_VDM
))
834 DPRINT1("Invalid flag combo used\n");
835 SetLastError(ERROR_INVALID_PARAMETER
);
840 * We're going to modify and mask out flags and stuff in lpStartupInfo,
841 * so we'll use our own local copy for that.
843 StartupInfo
= *lpStartupInfo
;
845 /* FIXME: Use default Separate/Shared VDM Flag */
847 /* If we are inside a Job, use Separate VDM so it won't escape the Job */
848 if (!(dwCreationFlags
& CREATE_SEPARATE_WOW_VDM
))
850 if (NtIsProcessInJob(NtCurrentProcess(), NULL
))
852 /* Remove the shared flag and add the separate flag. */
853 dwCreationFlags
= (dwCreationFlags
&~ CREATE_SHARED_WOW_VDM
) |
854 CREATE_SEPARATE_WOW_VDM
;
859 * According to some sites, ShellExecuteEx uses an undocumented flag to
860 * send private handle data (such as HMONITOR or HICON). See:
861 * www.catch22.net/tuts/undoc01.asp. This implies that we can't use the
862 * standard handles anymore since we'd be overwriting this private data
864 if ((StartupInfo
.dwFlags
& STARTF_USESTDHANDLES
) &&
865 (StartupInfo
.dwFlags
& (STARTF_USEHOTKEY
| STARTF_SHELLPRIVATE
)))
867 StartupInfo
.dwFlags
&= ~STARTF_USESTDHANDLES
;
870 /* Start by zeroing out the fields */
871 RtlZeroMemory(lpProcessInformation
, sizeof(PROCESS_INFORMATION
));
873 /* Easy stuff first, convert the process priority class */
874 PriorityClass
.Foreground
= FALSE
;
875 PriorityClass
.PriorityClass
= BasepConvertPriorityClass(dwCreationFlags
);
877 /* Convert the environment */
878 if(lpEnvironment
&& !(dwCreationFlags
& CREATE_UNICODE_ENVIRONMENT
))
880 lpEnvironment
= BasepConvertUnicodeEnvironment(lpEnvironment
);
881 if (!lpEnvironment
) return FALSE
;
884 /* Get the application name and do all the proper formating necessary */
886 /* See if we have an application name (oh please let us have one!) */
887 if (!lpApplicationName
)
890 NameBuffer
= RtlAllocateHeap(GetProcessHeap(),
892 MAX_PATH
* sizeof(WCHAR
));
894 /* This is all we have to work with :( */
895 lpApplicationName
= lpCommandLine
;
897 /* Initialize our friends at the beginning */
898 NullBuffer
= (LPWSTR
)lpApplicationName
;
899 ScanString
= (LPWSTR
)lpApplicationName
;
901 /* We will start by looking for a quote */
902 if (*ScanString
== L
'\"')
907 /* Advance past quote */
909 lpApplicationName
= ScanString
;
911 /* Find the closing quote */
914 if (*ScanString
== L
'\"')
917 NullBuffer
= ScanString
;
924 NullBuffer
= ScanString
;
929 /* No quotes, so we'll be looking for white space */
931 /* Reset the pointer */
932 lpApplicationName
= lpCommandLine
;
934 /* Find whitespace of Tab */
937 if (*ScanString
== ' ' || *ScanString
== '\t')
940 NullBuffer
= ScanString
;
946 NullBuffer
= ScanString
;
950 /* Set the Null Buffer */
951 SaveChar
= *NullBuffer
;
952 *NullBuffer
= UNICODE_NULL
;
954 /* Do a search for the file */
955 DPRINT("Ready for SearchPathW: %S\n", lpApplicationName
);
956 RetVal
= SearchPathW(NULL
,
961 NULL
) * sizeof(WCHAR
);
963 /* Did it find something? */
966 /* Get file attributes */
967 ULONG Attributes
= GetFileAttributesW(NameBuffer
);
968 if (Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
970 /* Give it a length of 0 to fail, this was a directory. */
976 RetVal
+= sizeof(WCHAR
);
980 /* Now check if we have a file, and if the path size is OK */
981 if (!RetVal
|| RetVal
>= (MAX_PATH
/ sizeof(WCHAR
)))
986 /* We failed, try to get the Path Type */
987 DPRINT("SearchPathW failed. Retval: %ld\n", RetVal
);
988 PathType
= RtlDetermineDosPathNameType_U(lpApplicationName
);
990 /* If it's not relative, try to get the error */
991 if (PathType
!= RELATIVE_PATH
)
993 /* This should fail, and give us a detailed LastError */
994 hFile
= CreateFileW(lpApplicationName
,
996 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
999 FILE_ATTRIBUTE_NORMAL
,
1002 /* Did it actually NOT fail? */
1003 if (hFile
!= INVALID_HANDLE_VALUE
)
1005 /* Fake the error */
1007 SetLastErrorByStatus(STATUS_OBJECT_NAME_NOT_FOUND
);
1012 /* Immediately set the error */
1013 SetLastErrorByStatus(STATUS_OBJECT_NAME_NOT_FOUND
);
1016 /* Did we already fail once? */
1019 SetLastError(Error
);
1023 /* Not yet, cache it */
1024 Error
= GetLastError();
1027 /* Put back the command line */
1028 *NullBuffer
= SaveChar
;
1029 lpApplicationName
= NameBuffer
;
1032 * If the search isn't done and we still have cmdline
1033 * then start over. Ex: c:\ha ha ha\haha.exe
1035 if (*ScanString
&& !SearchDone
)
1037 /* Move in the buffer */
1039 NullBuffer
= ScanString
;
1041 /* We will have to add a quote, since there is a space*/
1042 QuotesNeeded
= TRUE
;
1044 /* And we will also fake the fact we found one */
1051 /* We totally failed */
1055 /* Put back the command line */
1056 *NullBuffer
= SaveChar
;
1057 lpApplicationName
= NameBuffer
;
1058 DPRINT("SearchPathW suceeded (%ld): %S\n", RetVal
, NameBuffer
);
1060 else if (!lpCommandLine
|| *lpCommandLine
== UNICODE_NULL
)
1062 /* We have an app name (good!) but no command line */
1063 CmdLineIsAppName
= TRUE
;
1064 lpCommandLine
= (LPWSTR
)lpApplicationName
;
1067 /* At this point the name has been toyed with enough to be openable */
1068 Status
= BasepMapFile(lpApplicationName
, &hSection
, &ApplicationName
);
1070 /* Check for failure */
1071 if (!NT_SUCCESS(Status
))
1073 /* Could be a non-PE File */
1076 /* Check if the Kernel tells us it's not even valid MZ */
1077 case STATUS_INVALID_IMAGE_NE_FORMAT
:
1078 case STATUS_INVALID_IMAGE_PROTECT
:
1079 case STATUS_INVALID_IMAGE_NOT_MZ
:
1081 /* If it's a DOS app, use VDM */
1082 //if ((BasepCheckDosApp(&ApplicationName)))
1084 DPRINT1("Launching VDM...\n");
1085 RtlFreeHeap(GetProcessHeap(), 0, NameBuffer
);
1086 RtlFreeHeap(GetProcessHeap(), 0, ApplicationName
.Buffer
);
1087 return CreateProcessW(L
"ntvdm.exe",
1088 (LPWSTR
)lpApplicationName
,
1089 lpProcessAttributes
,
1096 lpProcessInformation
);
1099 /* It's a batch file */
1100 LPWSTR BatchCommandLine
;
1101 ULONG CmdLineLength
;
1102 UNICODE_STRING CommandLineString
;
1105 &ApplicationName
.Buffer
[ApplicationName
.Length
/ sizeof(WCHAR
) - 4];
1107 /* Make sure the extensions are correct */
1108 if (_wcsnicmp(Extension
, L
".bat", 4) && _wcsnicmp(Extension
, L
".cmd", 4))
1110 SetLastError(ERROR_BAD_EXE_FORMAT
);
1114 /* Calculate the length of the command line */
1115 CmdLineLength
= wcslen(CMD_STRING
) + wcslen(lpCommandLine
) + 1;
1117 /* If we found quotes, then add them into the length size */
1118 if (CmdLineIsAppName
|| FoundQuotes
) CmdLineLength
+= 2;
1119 CmdLineLength
*= sizeof(WCHAR
);
1121 /* Allocate space for the new command line */
1122 BatchCommandLine
= RtlAllocateHeap(GetProcessHeap(),
1127 wcscpy(BatchCommandLine
, CMD_STRING
);
1128 if (CmdLineIsAppName
|| FoundQuotes
)
1130 wcscat(BatchCommandLine
, L
"\"");
1132 wcscat(BatchCommandLine
, lpCommandLine
);
1133 if (CmdLineIsAppName
|| FoundQuotes
)
1135 wcscat(BatchCommandLine
, L
"\"");
1138 /* Create it as a Unicode String */
1139 RtlInitUnicodeString(&CommandLineString
, BatchCommandLine
);
1141 /* Set the command line to this */
1142 lpCommandLine
= CommandLineString
.Buffer
;
1143 lpApplicationName
= NULL
;
1146 RtlFreeHeap(GetProcessHeap(), 0, TempBuffer
);
1147 RtlFreeHeap(GetProcessHeap(), 0, ApplicationName
.Buffer
);
1148 ApplicationName
.Buffer
= NULL
;
1153 case STATUS_INVALID_IMAGE_WIN_16
:
1155 /* It's a Win16 Image, use VDM */
1156 DPRINT1("Launching VDM...\n");
1157 RtlFreeHeap(GetProcessHeap(), 0, NameBuffer
);
1158 RtlFreeHeap(GetProcessHeap(), 0, ApplicationName
.Buffer
);
1159 return CreateProcessW(L
"ntvdm.exe",
1160 (LPWSTR
)lpApplicationName
,
1161 lpProcessAttributes
,
1168 lpProcessInformation
);
1171 /* Invalid Image Type */
1172 SetLastError(ERROR_BAD_EXE_FORMAT
);
1177 /* Use our desktop if we didn't get any */
1178 if (!StartupInfo
.lpDesktop
)
1180 StartupInfo
.lpDesktop
= OurPeb
->ProcessParameters
->DesktopInfo
.Buffer
;
1183 /* FIXME: Check if Application is allowed to run */
1185 /* FIXME: Allow CREATE_SEPARATE only for WOW Apps, once we have that. */
1187 /* Get some information about the executable */
1188 Status
= ZwQuerySection(hSection
,
1189 SectionImageInformation
,
1191 sizeof(SectionImageInfo
),
1193 if(!NT_SUCCESS(Status
))
1196 DPRINT1("Unable to get SectionImageInformation, status 0x%x\n", Status
);
1197 SetLastErrorByStatus(Status
);
1201 /* Don't execute DLLs */
1202 if (SectionImageInfo
.ImageCharacteristics
& IMAGE_FILE_DLL
)
1205 DPRINT1("Can't execute a DLL\n");
1206 SetLastError(ERROR_BAD_EXE_FORMAT
);
1210 /* FIXME: Check for Debugger */
1212 /* FIXME: Check if Machine Type and SubSys Version Match */
1214 /* We don't support POSIX or anything else for now */
1215 if (IMAGE_SUBSYSTEM_WINDOWS_GUI
!= SectionImageInfo
.SubsystemType
&&
1216 IMAGE_SUBSYSTEM_WINDOWS_CUI
!= SectionImageInfo
.SubsystemType
)
1219 DPRINT1("Invalid subsystem %d\n", SectionImageInfo
.SubsystemType
);
1220 SetLastError(ERROR_BAD_EXE_FORMAT
);
1224 /* Initialize the process object attributes */
1225 ObjectAttributes
= BasepConvertObjectAttributes(&LocalObjectAttributes
,
1226 lpProcessAttributes
,
1229 /* Create the Process */
1230 Status
= NtCreateProcess(&hProcess
,
1238 if(!NT_SUCCESS(Status
))
1241 DPRINT1("Unable to create process, status 0x%x\n", Status
);
1242 SetLastErrorByStatus(Status
);
1247 Status
= NtSetInformationProcess(hProcess
,
1248 ProcessPriorityClass
,
1250 sizeof(PROCESS_PRIORITY_CLASS
));
1251 if(!NT_SUCCESS(Status
))
1255 DPRINT1("Unable to set new process priority, status 0x%x\n", Status
);
1256 SetLastErrorByStatus(Status
);
1260 /* Set Error Mode */
1261 if (dwCreationFlags
& CREATE_DEFAULT_ERROR_MODE
)
1263 ULONG ErrorMode
= SEM_FAILCRITICALERRORS
;
1264 NtSetInformationProcess(hProcess
,
1265 ProcessDefaultHardErrorMode
,
1270 /* Convert the directory to a full path */
1271 if (lpCurrentDirectory
)
1273 /* Allocate a buffer */
1274 CurrentDirectory
= RtlAllocateHeap(GetProcessHeap(),
1276 MAX_PATH
* sizeof(WCHAR
) + 2);
1278 /* Get the length */
1279 if (GetFullPathNameW(lpCurrentDirectory
,
1282 &CurrentDirectoryPart
) > MAX_PATH
)
1284 DPRINT1("Directory name too long\n");
1285 SetLastError(ERROR_DIRECTORY
);
1290 /* Insert quotes if needed */
1291 if (QuotesNeeded
|| CmdLineIsAppName
)
1293 /* Allocate a buffer */
1294 QuotedCmdLine
= RtlAllocateHeap(GetProcessHeap(),
1296 (wcslen(lpCommandLine
) + 2 + 1) *
1299 /* Copy the first quote */
1300 wcscpy(QuotedCmdLine
, L
"\"");
1302 /* Save a null char */
1305 SaveChar
= *NullBuffer
;
1306 *NullBuffer
= UNICODE_NULL
;
1309 /* Add the command line and the finishing quote */
1310 wcscat(QuotedCmdLine
, lpCommandLine
);
1311 wcscat(QuotedCmdLine
, L
"\"");
1313 /* Add the null char */
1316 *NullBuffer
= SaveChar
;
1317 wcscat(QuotedCmdLine
, NullBuffer
);
1320 DPRINT("Quoted CmdLine: %S\n", QuotedCmdLine
);
1323 /* Get the Process Information */
1324 Status
= NtQueryInformationProcess(hProcess
,
1325 ProcessBasicInformation
,
1327 sizeof(ProcessBasicInfo
),
1331 Status
= BasepNotifyCsrOfCreation(dwCreationFlags
,
1332 (HANDLE
)ProcessBasicInfo
.UniqueProcessId
,
1333 SectionImageInfo
.SubsystemType
,
1338 if (!NT_SUCCESS(Status
))
1340 DPRINT1("CSR Notification Failed");
1341 SetLastErrorByStatus(Status
);
1345 /* Create Process Environment */
1346 RemotePeb
= ProcessBasicInfo
.PebBaseAddress
;
1347 Status
= BasepInitializeEnvironment(hProcess
,
1349 (LPWSTR
)lpApplicationName
,
1351 (QuotesNeeded
|| CmdLineIsAppName
) ?
1352 QuotedCmdLine
: lpCommandLine
,
1359 if (!NT_SUCCESS(Status
))
1361 DPRINT1("Could not initialize Process Environment\n");
1362 SetLastErrorByStatus(Status
);
1366 /* Close the section */
1370 /* Duplicate the handles if needed */
1371 if (!bInheritHandles
&& !(StartupInfo
.dwFlags
& STARTF_USESTDHANDLES
) &&
1372 SectionImageInfo
.SubsystemType
== IMAGE_SUBSYSTEM_WINDOWS_CUI
)
1374 PRTL_USER_PROCESS_PARAMETERS RemoteParameters
;
1376 /* Get the remote parameters */
1377 Status
= NtReadVirtualMemory(hProcess
,
1378 &RemotePeb
->ProcessParameters
,
1382 if (!NT_SUCCESS(Status
))
1384 DPRINT1("Failed to read memory\n");
1388 /* Duplicate and write the handles */
1389 BasepDuplicateAndWriteHandle(hProcess
,
1390 OurPeb
->ProcessParameters
->StandardInput
,
1391 &RemoteParameters
->StandardInput
);
1392 BasepDuplicateAndWriteHandle(hProcess
,
1393 OurPeb
->ProcessParameters
->StandardOutput
,
1394 &RemoteParameters
->StandardOutput
);
1395 BasepDuplicateAndWriteHandle(hProcess
,
1396 OurPeb
->ProcessParameters
->StandardError
,
1397 &RemoteParameters
->StandardError
);
1400 /* Create the first thread */
1401 DPRINT("Creating thread for process (EntryPoint = 0x%.08x)\n",
1402 SectionImageInfo
.TransferAddress
);
1403 hThread
= BasepCreateFirstThread(hProcess
,
1408 if (hThread
== NULL
)
1410 DPRINT1("Could not create Initial Thread\n");
1414 if (!(dwCreationFlags
& CREATE_SUSPENDED
))
1416 NtResumeThread(hThread
, &Dummy
);
1419 /* Cleanup Environment */
1420 if (lpEnvironment
&& !(dwCreationFlags
& CREATE_UNICODE_ENVIRONMENT
))
1422 RtlDestroyEnvironment(lpEnvironment
);
1426 lpProcessInformation
->dwProcessId
= (DWORD
)ClientId
.UniqueProcess
;
1427 lpProcessInformation
->dwThreadId
= (DWORD
)ClientId
.UniqueThread
;
1428 lpProcessInformation
->hProcess
= hProcess
;
1429 lpProcessInformation
->hThread
= hThread
;
1430 DPRINT("hThread[%lx]: %lx inside hProcess[%lx]: %lx\n", hThread
,
1431 ClientId
.UniqueThread
, ClientId
.UniqueProcess
, hProcess
);
1432 hProcess
= hThread
= NULL
;
1434 /* De-allocate heap strings */
1435 if (NameBuffer
) RtlFreeHeap(GetProcessHeap(), 0, NameBuffer
);
1436 if (ApplicationName
.Buffer
)
1437 RtlFreeHeap(GetProcessHeap(), 0, ApplicationName
.Buffer
);
1438 if (CurrentDirectory
) RtlFreeHeap(GetProcessHeap(), 0, CurrentDirectory
);
1439 if (QuotedCmdLine
) RtlFreeHeap(GetProcessHeap(), 0, QuotedCmdLine
);
1441 /* Kill any handles still alive */
1442 if (hSection
) NtClose(hSection
);
1445 /* We don't know any more details then this */
1446 NtTerminateProcess(hProcess
, STATUS_UNSUCCESSFUL
);
1449 if (hProcess
) NtClose(hProcess
);
1451 /* Return Success */