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
);
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
,
85 ULONG Request
= CREATE_PROCESS
;
86 CSR_API_MESSAGE CsrRequest
;
89 DPRINT1("BasepNotifyCsrOfCreation: Process: %lx, Flags %lx\n",
90 ProcessId
, dwCreationFlags
);
92 /* Fill out the request */
93 CsrRequest
.Data
.CreateProcessRequest
.NewProcessId
= ProcessId
;
94 CsrRequest
.Data
.CreateProcessRequest
.Flags
= dwCreationFlags
;
97 Status
= CsrClientCallServer(&CsrRequest
,
99 MAKE_CSR_API(Request
, CSR_NATIVE
),
100 sizeof(CSR_API_MESSAGE
));
101 if (!NT_SUCCESS(Status
) || !NT_SUCCESS(CsrRequest
.Status
))
103 DPRINT1("Failed to tell csrss about new process\n");
104 return CsrRequest
.Status
;
108 return STATUS_SUCCESS
;
112 * Creates the first Thread in a Proces
116 BasepCreateFirstThread(HANDLE ProcessHandle
,
117 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
118 PSECTION_IMAGE_INFORMATION SectionImageInfo
,
121 OBJECT_ATTRIBUTES LocalObjectAttributes
;
122 POBJECT_ATTRIBUTES ObjectAttributes
;
124 INITIAL_TEB InitialTeb
;
128 DPRINT("BasepCreateFirstThread. hProcess: %lx\n", ProcessHandle
);
130 /* Create the Thread's Stack */
131 BasepCreateStack(ProcessHandle
,
132 SectionImageInfo
->MaximumStackSize
,
133 SectionImageInfo
->CommittedStackSize
,
136 /* Create the Thread's Context */
137 BasepInitializeContext(&Context
,
139 SectionImageInfo
->TransferAddress
,
140 InitialTeb
.StackBase
,
143 /* Convert the thread attributes */
144 ObjectAttributes
= BasepConvertObjectAttributes(&LocalObjectAttributes
,
148 /* Create the Kernel Thread Object */
149 Status
= NtCreateThread(&hThread
,
163 * Converts ANSI to Unicode Environment
167 BasepConvertUnicodeEnvironment(IN PVOID lpEnvironment
)
172 UNICODE_STRING UnicodeEnv
;
175 DPRINT("BasepConvertUnicodeEnvironment\n");
177 /* Scan the environment to calculate its Unicode size */
178 AnsiEnv
.Buffer
= pcScan
= lpEnvironment
;
179 while (*pcScan
) while (*pcScan
++);
181 /* Create our ANSI String */
182 AnsiEnv
.Length
= (ULONG_PTR
)pcScan
- (ULONG_PTR
)lpEnvironment
+ 1;
183 AnsiEnv
.MaximumLength
= AnsiEnv
.Length
+ 1;
185 /* Allocate memory for the Unicode Environment */
186 UnicodeEnv
.Buffer
= NULL
;
187 EnvSize
= AnsiEnv
.MaximumLength
* sizeof(WCHAR
);
188 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
189 (PVOID
)&UnicodeEnv
.Buffer
,
195 if (!NT_SUCCESS(Status
))
197 SetLastError(Status
);
201 /* Use the allocated size */
202 UnicodeEnv
.MaximumLength
= EnvSize
;
205 RtlAnsiStringToUnicodeString(&UnicodeEnv
, &AnsiEnv
, FALSE
);
206 return UnicodeEnv
.Buffer
;
210 * Converts a Win32 Priority Class to NT
214 BasepConvertPriorityClass(IN ULONG dwCreationFlags
)
218 if(dwCreationFlags
& IDLE_PRIORITY_CLASS
)
220 ReturnClass
= PROCESS_PRIORITY_CLASS_IDLE
;
222 else if(dwCreationFlags
& BELOW_NORMAL_PRIORITY_CLASS
)
224 ReturnClass
= PROCESS_PRIORITY_CLASS_BELOW_NORMAL
;
226 else if(dwCreationFlags
& NORMAL_PRIORITY_CLASS
)
228 ReturnClass
= PROCESS_PRIORITY_CLASS_NORMAL
;
230 else if(dwCreationFlags
& ABOVE_NORMAL_PRIORITY_CLASS
)
232 ReturnClass
= PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
;
234 else if(dwCreationFlags
& HIGH_PRIORITY_CLASS
)
236 ReturnClass
= PROCESS_PRIORITY_CLASS_HIGH
;
238 else if(dwCreationFlags
& REALTIME_PRIORITY_CLASS
)
240 /* Check for Privilege First */
241 if (BasepCheckRealTimePrivilege())
243 ReturnClass
= PROCESS_PRIORITY_CLASS_REALTIME
;
247 ReturnClass
= PROCESS_PRIORITY_CLASS_HIGH
;
252 ReturnClass
= 0 /* FIXME */;
259 * Duplicates a standard handle and writes it where requested.
263 BasepDuplicateAndWriteHandle(IN HANDLE ProcessHandle
,
264 IN HANDLE StandardHandle
,
268 HANDLE DuplicatedHandle
;
271 DPRINT("BasepDuplicateAndWriteHandle. hProcess: %lx, Handle: %lx,"
272 "Address: %p\n", ProcessHandle
, StandardHandle
, Address
);
274 /* Don't touch Console Handles */
275 if (IsConsoleHandle(StandardHandle
)) return;
277 /* Duplicate the handle */
278 Status
= NtDuplicateObject(NtCurrentProcess(),
282 DUPLICATE_SAME_ACCESS
| DUPLICATE_SAME_ATTRIBUTES
,
285 if (NT_SUCCESS(Status
))
288 NtWriteVirtualMemory(ProcessHandle
,
298 BasepGetDllPath(LPWSTR FullPath
,
301 /* FIXME: Not yet implemented */
307 BasepCopyHandles(IN PRTL_USER_PROCESS_PARAMETERS Params
,
308 IN PRTL_USER_PROCESS_PARAMETERS PebParams
,
309 IN BOOL InheritHandles
)
311 /* Copy the handle if we are inheriting or if it's a console handle */
312 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardInput
))
314 Params
->StandardInput
= PebParams
->StandardInput
;
316 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardOutput
))
318 Params
->StandardOutput
= PebParams
->StandardOutput
;
320 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardError
))
322 Params
->StandardError
= PebParams
->StandardError
;
328 BasepInitializeEnvironment(HANDLE ProcessHandle
,
330 LPWSTR ApplicationPathName
,
331 LPWSTR lpCurrentDirectory
,
332 LPWSTR lpCommandLine
,
334 LPSTARTUPINFOW StartupInfo
,
338 WCHAR FullPath
[MAX_PATH
];
340 LPWSTR DllPathString
;
341 PRTL_USER_PROCESS_PARAMETERS ProcessParameters
;
342 PRTL_USER_PROCESS_PARAMETERS RemoteParameters
= NULL
;
343 UNICODE_STRING DllPath
, ImageName
, CommandLine
, CurrentDirectory
;
349 UNICODE_STRING Desktop
, Shell
, Runtime
, Title
;
350 PPEB OurPeb
= NtCurrentPeb();
352 DPRINT("BasepInitializeEnvironment\n");
354 /* Get the full path name */
355 RetVal
= GetFullPathNameW(ApplicationPathName
,
359 DPRINT("ApplicationPathName: %S, FullPath: %S\n", ApplicationPathName
,
362 /* Get the DLL Path */
363 DllPathString
= BasepGetDllPath(FullPath
, Environment
);
365 /* Initialize Strings */
366 RtlInitUnicodeString(&DllPath
, DllPathString
);
367 RtlInitUnicodeString(&ImageName
, FullPath
);
368 RtlInitUnicodeString(&CommandLine
, lpCommandLine
);
369 RtlInitUnicodeString(&CurrentDirectory
, lpCurrentDirectory
);
371 /* Initialize more Strings from the Startup Info */
372 if (StartupInfo
->lpDesktop
)
374 RtlInitUnicodeString(&Desktop
, StartupInfo
->lpDesktop
);
378 RtlInitUnicodeString(&Desktop
, L
"");
380 if (StartupInfo
->lpReserved
)
382 RtlInitUnicodeString(&Shell
, StartupInfo
->lpReserved
);
386 RtlInitUnicodeString(&Shell
, L
"");
388 if (StartupInfo
->lpTitle
)
390 RtlInitUnicodeString(&Title
, StartupInfo
->lpTitle
);
394 RtlInitUnicodeString(&Title
, L
"");
397 /* This one is special because the length can differ */
398 Runtime
.Buffer
= (LPWSTR
)StartupInfo
->lpReserved2
;
399 Runtime
.MaximumLength
= Runtime
.Length
= StartupInfo
->cbReserved2
;
401 /* Create the Parameter Block */
402 DPRINT("Creating Process Parameters: %wZ %wZ %wZ %wZ %wZ %wZ %wZ\n",
403 &ImageName
, &DllPath
, &CommandLine
, &Desktop
, &Title
, &Shell
,
405 Status
= RtlCreateProcessParameters(&ProcessParameters
,
409 &CurrentDirectory
: NULL
,
417 if (!NT_SUCCESS(Status
))
419 DPRINT1("Failed to create process parameters!\n");
423 /* Check if we got an environment. If not, use ours */
426 /* Save pointer and start lookup */
427 Environment
= ScanChar
= ProcessParameters
->Environment
;
431 /* Save pointer and start lookup */
432 Environment
= ScanChar
= OurPeb
->ProcessParameters
->Environment
;
435 /* Find the environment size */
438 while (*ScanChar
) while (*ScanChar
++);
440 /* Calculate the size of the block */
441 EnviroSize
= (ULONG
)((ULONG_PTR
)ScanChar
- (ULONG_PTR
)Environment
);
442 DPRINT("EnvironmentSize %ld\n", EnviroSize
);
444 /* Allocate and Initialize new Environment Block */
446 ProcessParameters
->Environment
= NULL
;
447 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
448 (PVOID
*)&ProcessParameters
->Environment
,
453 if (!NT_SUCCESS(Status
))
455 DPRINT1("Failed to allocate Environment Block\n");
459 /* Write the Environment Block */
460 ZwWriteVirtualMemory(ProcessHandle
,
461 ProcessParameters
->Environment
,
467 /* Write new parameters */
468 ProcessParameters
->StartingX
= StartupInfo
->dwX
;
469 ProcessParameters
->StartingY
= StartupInfo
->dwY
;
470 ProcessParameters
->CountX
= StartupInfo
->dwXSize
;
471 ProcessParameters
->CountY
= StartupInfo
->dwYSize
;
472 ProcessParameters
->CountCharsX
= StartupInfo
->dwXCountChars
;
473 ProcessParameters
->CountCharsY
= StartupInfo
->dwYCountChars
;
474 ProcessParameters
->FillAttribute
= StartupInfo
->dwFillAttribute
;
475 ProcessParameters
->WindowFlags
= StartupInfo
->dwFlags
;
476 ProcessParameters
->ShowWindowFlags
= StartupInfo
->wShowWindow
;
478 /* Write the handles only if we have to */
479 if (StartupInfo
->dwFlags
& STARTF_USESTDHANDLES
)
481 ProcessParameters
->StandardInput
= StartupInfo
->hStdInput
;
482 ProcessParameters
->StandardOutput
= StartupInfo
->hStdOutput
;
483 ProcessParameters
->StandardError
= StartupInfo
->hStdError
;
486 /* Use Special Flags for ConDllInitialize in Kernel32 */
487 if (CreationFlags
& DETACHED_PROCESS
)
489 ProcessParameters
->ConsoleHandle
= HANDLE_DETACHED_PROCESS
;
491 else if (CreationFlags
& CREATE_NO_WINDOW
)
493 ProcessParameters
->ConsoleHandle
= HANDLE_CREATE_NO_WINDOW
;
495 else if (CreationFlags
& CREATE_NEW_CONSOLE
)
497 ProcessParameters
->ConsoleHandle
= HANDLE_CREATE_NEW_CONSOLE
;
501 /* Inherit our Console Handle */
502 ProcessParameters
->ConsoleHandle
= OurPeb
->ProcessParameters
->ConsoleHandle
;
504 /* Is the shell trampling on our Handles? */
505 if (!(StartupInfo
->dwFlags
&
506 (STARTF_USESTDHANDLES
| STARTF_USEHOTKEY
| STARTF_SHELLPRIVATE
)))
508 /* Use handles from PEB, if inheriting or they are console */
509 BasepCopyHandles(ProcessParameters
,
510 OurPeb
->ProcessParameters
,
515 /* Also set the Console Flag */
516 if (CreationFlags
& CREATE_NEW_PROCESS_GROUP
)
518 ProcessParameters
->ConsoleFlags
= 1;
521 /* Allocate memory for the parameter block */
522 Size
= ProcessParameters
->Length
;
523 Status
= NtAllocateVirtualMemory(ProcessHandle
,
524 (PVOID
*)&RemoteParameters
,
529 if (!NT_SUCCESS(Status
))
531 DPRINT1("Failed to allocate Parameters Block\n");
535 /* Set the allocated size */
536 ProcessParameters
->MaximumLength
= Size
;
538 /* Handle some Parameter Flags */
539 ProcessParameters
->ConsoleFlags
= (CreationFlags
& CREATE_NEW_PROCESS_GROUP
);
540 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_USER
) ?
541 PPF_PROFILE_USER
: 0;
542 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_KERNEL
) ?
543 PPF_PROFILE_KERNEL
: 0;
544 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_SERVER
) ?
545 PPF_PROFILE_SERVER
: 0;
546 ProcessParameters
->Flags
|= (NtCurrentPeb()->ProcessParameters
->Flags
&
547 PPF_DISABLE_HEAP_CHECKS
);
549 /* Write the Parameter Block */
550 Status
= NtWriteVirtualMemory(ProcessHandle
,
553 ProcessParameters
->Length
,
556 /* Write the PEB Pointer */
557 Status
= NtWriteVirtualMemory(ProcessHandle
,
558 &Peb
->ProcessParameters
,
564 RtlFreeHeap(GetProcessHeap(), 0, DllPath
.Buffer
);
565 RtlDestroyProcessParameters(ProcessParameters
);
567 DPRINT("Completed\n");
568 return STATUS_SUCCESS
;
571 /* FUNCTIONS ****************************************************************/
574 * FUNCTION: The CreateProcess function creates a new process and its
575 * primary thread. The new process executes the specified executable file
578 * lpApplicationName = Pointer to name of executable module
579 * lpCommandLine = Pointer to command line string
580 * lpProcessAttributes = Process security attributes
581 * lpThreadAttributes = Thread security attributes
582 * bInheritHandles = Handle inheritance flag
583 * dwCreationFlags = Creation flags
584 * lpEnvironment = Pointer to new environment block
585 * lpCurrentDirectory = Pointer to current directory name
586 * lpStartupInfo = Pointer to startup info
587 * lpProcessInformation = Pointer to process information
593 CreateProcessA(LPCSTR lpApplicationName
,
595 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
596 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
597 BOOL bInheritHandles
,
598 DWORD dwCreationFlags
,
599 LPVOID lpEnvironment
,
600 LPCSTR lpCurrentDirectory
,
601 LPSTARTUPINFOA lpStartupInfo
,
602 LPPROCESS_INFORMATION lpProcessInformation
)
604 PUNICODE_STRING CommandLine
= NULL
;
605 UNICODE_STRING DummyString
;
606 UNICODE_STRING LiveCommandLine
;
607 UNICODE_STRING ApplicationName
;
608 UNICODE_STRING CurrentDirectory
;
610 STARTUPINFOW StartupInfo
;
612 DPRINT("dwCreationFlags %x, lpEnvironment %x, lpCurrentDirectory %x, "
613 "lpStartupInfo %x, lpProcessInformation %x\n",
614 dwCreationFlags
, lpEnvironment
, lpCurrentDirectory
,
615 lpStartupInfo
, lpProcessInformation
);
617 /* Copy Startup Info */
618 RtlMoveMemory(&StartupInfo
, lpStartupInfo
, sizeof(*lpStartupInfo
));
620 /* Initialize all strings to nothing */
621 LiveCommandLine
.Buffer
= NULL
;
622 DummyString
.Buffer
= NULL
;
623 ApplicationName
.Buffer
= NULL
;
624 CurrentDirectory
.Buffer
= NULL
;
625 StartupInfo
.lpDesktop
= NULL
;
626 StartupInfo
.lpReserved
= NULL
;
627 StartupInfo
.lpTitle
= NULL
;
629 /* Convert the Command line */
632 /* If it's too long, then we'll have a problem */
633 if ((strlen(lpCommandLine
) + 1) * sizeof(WCHAR
) <
634 NtCurrentTeb()->StaticUnicodeString
.MaximumLength
)
636 /* Cache it in the TEB */
637 CommandLine
= Basep8BitStringToCachedUnicodeString(lpCommandLine
);
641 /* Use a dynamic version */
642 Basep8BitStringToLiveUnicodeString(&LiveCommandLine
,
648 /* The logic below will use CommandLine, so we must make it valid */
649 CommandLine
= &DummyString
;
652 /* Convert the Name and Directory */
653 if (lpApplicationName
)
655 Basep8BitStringToLiveUnicodeString(&ApplicationName
,
658 if (lpCurrentDirectory
)
660 Basep8BitStringToLiveUnicodeString(&CurrentDirectory
,
664 /* Now convert Startup Strings */
665 if (lpStartupInfo
->lpReserved
)
667 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpReserved
,
668 &StartupInfo
.lpReserved
);
670 if (lpStartupInfo
->lpDesktop
)
672 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpDesktop
,
673 &StartupInfo
.lpDesktop
);
675 if (lpStartupInfo
->lpTitle
)
677 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpTitle
,
678 &StartupInfo
.lpTitle
);
681 /* Call the Unicode function */
682 bRetVal
= CreateProcessW(ApplicationName
.Buffer
,
683 LiveCommandLine
.Buffer
?
684 LiveCommandLine
.Buffer
: CommandLine
->Buffer
,
690 CurrentDirectory
.Buffer
,
692 lpProcessInformation
);
695 RtlFreeUnicodeString(&ApplicationName
);
696 RtlFreeUnicodeString(&LiveCommandLine
);
697 RtlFreeUnicodeString(&CurrentDirectory
);
698 RtlFreeHeap(GetProcessHeap(), 0, StartupInfo
.lpDesktop
);
699 RtlFreeHeap(GetProcessHeap(), 0, StartupInfo
.lpReserved
);
700 RtlFreeHeap(GetProcessHeap(), 0, StartupInfo
.lpTitle
);
702 /* Return what Unicode did */
711 CreateProcessW(LPCWSTR lpApplicationName
,
712 LPWSTR lpCommandLine
,
713 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
714 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
715 BOOL bInheritHandles
,
716 DWORD dwCreationFlags
,
717 LPVOID lpEnvironment
,
718 LPCWSTR lpCurrentDirectory
,
719 LPSTARTUPINFOW lpStartupInfo
,
720 LPPROCESS_INFORMATION lpProcessInformation
)
723 PROCESS_PRIORITY_CLASS PriorityClass
;
724 BOOLEAN FoundQuotes
= FALSE
;
725 BOOLEAN QuotesNeeded
= FALSE
;
726 BOOLEAN CmdLineIsAppName
= FALSE
;
727 UNICODE_STRING ApplicationName
;
728 OBJECT_ATTRIBUTES LocalObjectAttributes
;
729 POBJECT_ATTRIBUTES ObjectAttributes
;
730 HANDLE hSection
, hProcess
, hThread
;
731 SECTION_IMAGE_INFORMATION SectionImageInfo
;
732 LPWSTR CurrentDirectory
= NULL
;
733 LPWSTR CurrentDirectoryPart
;
734 PROCESS_BASIC_INFORMATION ProcessBasicInfo
;
735 STARTUPINFOW StartupInfo
;
737 LPWSTR BatchCommandLine
;
739 UNICODE_STRING CommandLineString
;
742 LPWSTR QuotedCmdLine
= NULL
;
745 LPWSTR NameBuffer
= NULL
;
749 BOOLEAN SearchDone
= FALSE
;
751 PPEB OurPeb
= NtCurrentPeb();
754 DPRINT("CreateProcessW: lpApplicationName: %S lpCommandLine: %S"
755 " lpEnvironment: %p lpCurrentDirectory: %S dwCreationFlags: %lx\n",
756 lpApplicationName
, lpCommandLine
, lpEnvironment
, lpCurrentDirectory
,
759 /* Flags we don't handle yet */
760 if (dwCreationFlags
& CREATE_SEPARATE_WOW_VDM
)
762 DPRINT1("CREATE_SEPARATE_WOW_VDM not handled\n");
764 if (dwCreationFlags
& CREATE_SHARED_WOW_VDM
)
766 DPRINT1("CREATE_SHARED_WOW_VDM not handled\n");
768 if (dwCreationFlags
& CREATE_FORCEDOS
)
770 DPRINT1("CREATE_FORCEDOS not handled\n");
773 /* Fail on this flag, it's only valid with the WithLogonW function */
774 if (dwCreationFlags
& CREATE_PRESERVE_CODE_AUTHZ_LEVEL
)
776 DPRINT1("Invalid flag used\n");
777 SetLastError(ERROR_INVALID_PARAMETER
);
781 /* This combination is illegal (see MSDN) */
782 if ((dwCreationFlags
& (DETACHED_PROCESS
| CREATE_NEW_CONSOLE
)) ==
783 (DETACHED_PROCESS
| CREATE_NEW_CONSOLE
))
785 DPRINT1("Invalid flag combo used\n");
786 SetLastError(ERROR_INVALID_PARAMETER
);
790 /* Another illegal combo */
791 if ((dwCreationFlags
& (CREATE_SEPARATE_WOW_VDM
| CREATE_SHARED_WOW_VDM
)) ==
792 (CREATE_SEPARATE_WOW_VDM
| CREATE_SHARED_WOW_VDM
))
794 DPRINT1("Invalid flag combo used\n");
795 SetLastError(ERROR_INVALID_PARAMETER
);
800 * We're going to modify and mask out flags and stuff in lpStartupInfo,
801 * so we'll use our own local copy for that.
803 StartupInfo
= *lpStartupInfo
;
805 /* FIXME: Use default Separate/Shared VDM Flag */
807 /* If we are inside a Job, use Separate VDM so it won't escape the Job */
808 if (!(dwCreationFlags
& CREATE_SEPARATE_WOW_VDM
))
810 if (NtIsProcessInJob(NtCurrentProcess(), NULL
))
812 /* Remove the shared flag and add the separate flag. */
813 dwCreationFlags
= (dwCreationFlags
&~ CREATE_SHARED_WOW_VDM
) |
814 CREATE_SEPARATE_WOW_VDM
;
819 * According to some sites, ShellExecuteEx uses an undocumented flag to
820 * send private handle data (such as HMONITOR or HICON). See:
821 * www.catch22.net/tuts/undoc01.asp. This implies that we can't use the
822 * standard handles anymore since we'd be overwriting this private data
824 if ((StartupInfo
.dwFlags
& STARTF_USESTDHANDLES
) &&
825 (StartupInfo
.dwFlags
& (STARTF_USEHOTKEY
| STARTF_SHELLPRIVATE
)))
827 StartupInfo
.dwFlags
&= ~STARTF_USESTDHANDLES
;
830 /* Start by zeroing out the fields */
831 RtlZeroMemory(lpProcessInformation
, sizeof(PROCESS_INFORMATION
));
833 /* Easy stuff first, convert the process priority class */
834 PriorityClass
.Foreground
= FALSE
;
835 PriorityClass
.PriorityClass
= BasepConvertPriorityClass(dwCreationFlags
);
837 /* Convert the environment */
838 if(lpEnvironment
&& !(dwCreationFlags
& CREATE_UNICODE_ENVIRONMENT
))
840 lpEnvironment
= BasepConvertUnicodeEnvironment(lpEnvironment
);
841 if (!lpEnvironment
) return FALSE
;
844 /* Get the application name and do all the proper formating necessary */
846 /* See if we have an application name (oh please let us have one!) */
847 if (!lpApplicationName
)
850 NameBuffer
= RtlAllocateHeap(GetProcessHeap(),
852 MAX_PATH
* sizeof(WCHAR
));
854 /* This is all we have to work with :( */
855 lpApplicationName
= lpCommandLine
;
857 /* Initialize our friends at the beginning */
858 NullBuffer
= (LPWSTR
)lpApplicationName
;
859 ScanString
= (LPWSTR
)lpApplicationName
;
861 /* We will start by looking for a quote */
862 if (*ScanString
== L
'\"')
867 /* Advance past quote */
869 lpApplicationName
= ScanString
;
871 /* Find the closing quote */
874 if (*ScanString
== L
'\"')
877 NullBuffer
= ScanString
;
884 NullBuffer
= ScanString
;
889 /* No quotes, so we'll be looking for white space */
891 /* Reset the pointer */
892 lpApplicationName
= lpCommandLine
;
894 /* Find whitespace of Tab */
897 if (*ScanString
== ' ' || *ScanString
== '\t')
900 NullBuffer
= ScanString
;
906 NullBuffer
= ScanString
;
910 /* Set the Null Buffer */
911 SaveChar
= *NullBuffer
;
912 *NullBuffer
= UNICODE_NULL
;
914 /* Do a search for the file */
915 DPRINT("Ready for SearchPathW: %S\n", lpApplicationName
);
916 RetVal
= SearchPathW(NULL
,
921 NULL
) * sizeof(WCHAR
);
923 /* Did it find something? */
926 /* Get file attributes */
927 ULONG Attributes
= GetFileAttributesW(NameBuffer
);
928 if (Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
930 /* Give it a length of 0 to fail, this was a directory. */
936 RetVal
+= sizeof(WCHAR
);
940 /* Now check if we have a file, and if the path size is OK */
941 if (!RetVal
|| RetVal
>= (MAX_PATH
/ sizeof(WCHAR
)))
946 /* We failed, try to get the Path Type */
947 DPRINT("SearchPathW failed. Retval: %ld\n", RetVal
);
948 PathType
= RtlDetermineDosPathNameType_U(lpApplicationName
);
950 /* If it's not relative, try to get the error */
951 if (PathType
!= RELATIVE_PATH
)
953 /* This should fail, and give us a detailed LastError */
954 hFile
= CreateFileW(lpApplicationName
,
956 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
959 FILE_ATTRIBUTE_NORMAL
,
962 /* Did it actually NOT fail? */
963 if (hFile
!= INVALID_HANDLE_VALUE
)
967 SetLastErrorByStatus(STATUS_OBJECT_NAME_NOT_FOUND
);
972 /* Immediately set the error */
973 SetLastErrorByStatus(STATUS_OBJECT_NAME_NOT_FOUND
);
976 /* Did we already fail once? */
983 /* Not yet, cache it */
984 Error
= GetLastError();
987 /* Put back the command line */
988 *NullBuffer
= SaveChar
;
989 lpApplicationName
= NameBuffer
;
992 * If the search isn't done and we still have cmdline
993 * then start over. Ex: c:\ha ha ha\haha.exe
995 if (*ScanString
&& !SearchDone
)
997 /* Move in the buffer */
999 NullBuffer
= ScanString
;
1001 /* We will have to add a quote, since there is a space*/
1002 QuotesNeeded
= TRUE
;
1004 /* And we will also fake the fact we found one */
1011 /* We totally failed */
1015 /* Put back the command line */
1016 *NullBuffer
= SaveChar
;
1017 lpApplicationName
= NameBuffer
;
1018 DPRINT("SearchPathW suceeded (%ld): %S\n", RetVal
, NameBuffer
);
1020 else if (!lpCommandLine
|| *lpCommandLine
== UNICODE_NULL
)
1022 /* We have an app name (good!) but no command line */
1023 CmdLineIsAppName
= TRUE
;
1024 lpCommandLine
= (LPWSTR
)lpApplicationName
;
1027 /* At this point the name has been toyed with enough to be openable */
1028 Status
= BasepMapFile(lpApplicationName
, &hSection
, &ApplicationName
);
1030 /* Check for failure */
1031 if (!NT_SUCCESS(Status
))
1033 /* Could be a non-PE File */
1036 /* Check if the Kernel tells us it's not even valid MZ */
1037 case STATUS_INVALID_IMAGE_NE_FORMAT
:
1038 case STATUS_INVALID_IMAGE_PROTECT
:
1039 case STATUS_INVALID_IMAGE_NOT_MZ
:
1041 /* If it's a DOS app, use VDM
1042 if ((BasepCheckDosApp(&ApplicationName))) */
1044 DPRINT1("Launching VDM...\n");
1045 RtlFreeHeap(GetProcessHeap(), 0, NameBuffer
);
1046 RtlFreeHeap(GetProcessHeap(), 0, ApplicationName
.Buffer
);
1047 return CreateProcessW(L
"ntvdm.exe",
1048 (LPWSTR
)lpApplicationName
,
1049 lpProcessAttributes
,
1056 lpProcessInformation
);
1059 /* It's a batch file */
1060 Extension
= &ApplicationName
.Buffer
[ApplicationName
.Length
/
1063 /* Make sure the extensions are correct */
1064 if (_wcsnicmp(Extension
, L
".bat", 4) && _wcsnicmp(Extension
, L
".cmd", 4))
1066 SetLastError(ERROR_BAD_EXE_FORMAT
);
1070 /* Calculate the length of the command line */
1071 CmdLineLength
= wcslen(CMD_STRING
) + wcslen(lpCommandLine
) + 1;
1073 /* If we found quotes, then add them into the length size */
1074 if (CmdLineIsAppName
|| FoundQuotes
) CmdLineLength
+= 2;
1075 CmdLineLength
*= sizeof(WCHAR
);
1077 /* Allocate space for the new command line */
1078 BatchCommandLine
= RtlAllocateHeap(GetProcessHeap(),
1083 wcscpy(BatchCommandLine
, CMD_STRING
);
1084 if (CmdLineIsAppName
|| FoundQuotes
)
1086 wcscat(BatchCommandLine
, L
"\"");
1088 wcscat(BatchCommandLine
, lpCommandLine
);
1089 if (CmdLineIsAppName
|| FoundQuotes
)
1091 wcscat(BatchCommandLine
, L
"\"");
1094 /* Create it as a Unicode String */
1095 RtlInitUnicodeString(&CommandLineString
, BatchCommandLine
);
1097 /* Set the command line to this */
1098 lpCommandLine
= CommandLineString
.Buffer
;
1099 lpApplicationName
= NULL
;
1102 RtlFreeHeap(GetProcessHeap(), 0, TempBuffer
);
1103 RtlFreeHeap(GetProcessHeap(), 0, ApplicationName
.Buffer
);
1104 ApplicationName
.Buffer
= NULL
;
1109 case STATUS_INVALID_IMAGE_WIN_16
:
1111 /* It's a Win16 Image, use VDM */
1112 DPRINT1("Launching VDM...\n");
1113 RtlFreeHeap(GetProcessHeap(), 0, NameBuffer
);
1114 RtlFreeHeap(GetProcessHeap(), 0, ApplicationName
.Buffer
);
1115 return CreateProcessW(L
"ntvdm.exe",
1116 (LPWSTR
)lpApplicationName
,
1117 lpProcessAttributes
,
1124 lpProcessInformation
);
1127 /* Invalid Image Type */
1128 SetLastError(ERROR_BAD_EXE_FORMAT
);
1133 /* Use our desktop if we didn't get any */
1134 if (!StartupInfo
.lpDesktop
)
1136 StartupInfo
.lpDesktop
= OurPeb
->ProcessParameters
->DesktopInfo
.Buffer
;
1139 /* FIXME: Check if Application is allowed to run */
1141 /* FIXME: Allow CREATE_SEPARATE only for WOW Apps, once we have that. */
1143 /* Get some information about the executable */
1144 Status
= ZwQuerySection(hSection
,
1145 SectionImageInformation
,
1147 sizeof(SectionImageInfo
),
1149 if(!NT_SUCCESS(Status
))
1152 DPRINT1("Unable to get SectionImageInformation, status 0x%x\n", Status
);
1153 SetLastErrorByStatus(Status
);
1157 /* Don't execute DLLs */
1158 if (SectionImageInfo
.ImageCharacteristics
& IMAGE_FILE_DLL
)
1161 DPRINT1("Can't execute a DLL\n");
1162 SetLastError(ERROR_BAD_EXE_FORMAT
);
1166 /* FIXME: Check for Debugger */
1168 /* FIXME: Check if Machine Type and SubSys Version Match */
1170 /* We don't support POSIX or anything else for now */
1171 if (IMAGE_SUBSYSTEM_WINDOWS_GUI
!= SectionImageInfo
.SubsystemType
&&
1172 IMAGE_SUBSYSTEM_WINDOWS_CUI
!= SectionImageInfo
.SubsystemType
)
1175 DPRINT1("Invalid subsystem %d\n", SectionImageInfo
.SubsystemType
);
1176 SetLastError(ERROR_BAD_EXE_FORMAT
);
1180 /* Initialize the process object attributes */
1181 ObjectAttributes
= BasepConvertObjectAttributes(&LocalObjectAttributes
,
1182 lpProcessAttributes
,
1185 /* Create the Process */
1186 Status
= NtCreateProcess(&hProcess
,
1194 if(!NT_SUCCESS(Status
))
1197 DPRINT1("Unable to create process, status 0x%x\n", Status
);
1198 SetLastErrorByStatus(Status
);
1203 Status
= NtSetInformationProcess(hProcess
,
1204 ProcessPriorityClass
,
1206 sizeof(PROCESS_PRIORITY_CLASS
));
1207 if(!NT_SUCCESS(Status
))
1211 DPRINT1("Unable to set new process priority, status 0x%x\n", Status
);
1212 SetLastErrorByStatus(Status
);
1216 /* Set Error Mode */
1217 if (dwCreationFlags
& CREATE_DEFAULT_ERROR_MODE
)
1219 ULONG ErrorMode
= SEM_FAILCRITICALERRORS
;
1220 NtSetInformationProcess(hProcess
,
1221 ProcessDefaultHardErrorMode
,
1226 /* Convert the directory to a full path */
1227 if (lpCurrentDirectory
)
1229 /* Allocate a buffer */
1230 CurrentDirectory
= RtlAllocateHeap(GetProcessHeap(),
1232 MAX_PATH
* sizeof(WCHAR
) + 2);
1234 /* Get the length */
1235 if (GetFullPathNameW(lpCurrentDirectory
,
1238 &CurrentDirectoryPart
) > MAX_PATH
)
1240 DPRINT1("Directory name too long\n");
1241 SetLastError(ERROR_DIRECTORY
);
1246 /* Insert quotes if needed */
1247 if (QuotesNeeded
|| CmdLineIsAppName
)
1249 /* Allocate a buffer */
1250 QuotedCmdLine
= RtlAllocateHeap(GetProcessHeap(),
1252 (wcslen(lpCommandLine
) + 2 + 1) *
1255 /* Copy the first quote */
1256 wcscpy(QuotedCmdLine
, L
"\"");
1258 /* Save a null char */
1261 SaveChar
= *NullBuffer
;
1262 *NullBuffer
= UNICODE_NULL
;
1265 /* Add the command line and the finishing quote */
1266 wcscat(QuotedCmdLine
, lpCommandLine
);
1267 wcscat(QuotedCmdLine
, L
"\"");
1269 /* Add the null char */
1272 *NullBuffer
= SaveChar
;
1273 wcscat(QuotedCmdLine
, NullBuffer
);
1276 DPRINT("Quoted CmdLine: %S\n", QuotedCmdLine
);
1279 /* Get the Process Information */
1280 Status
= NtQueryInformationProcess(hProcess
,
1281 ProcessBasicInformation
,
1283 sizeof(ProcessBasicInfo
),
1286 /* Create Process Environment */
1287 RemotePeb
= ProcessBasicInfo
.PebBaseAddress
;
1288 Status
= BasepInitializeEnvironment(hProcess
,
1290 (LPWSTR
)lpApplicationName
,
1292 (QuotesNeeded
|| CmdLineIsAppName
) ?
1293 QuotedCmdLine
: lpCommandLine
,
1298 if (!NT_SUCCESS(Status
))
1300 DPRINT1("Could not initialize Process Environment\n");
1301 SetLastErrorByStatus(Status
);
1305 /* Close the section */
1309 /* Duplicate the handles if needed */
1310 if (!bInheritHandles
&& !(StartupInfo
.dwFlags
& STARTF_USESTDHANDLES
) &&
1311 SectionImageInfo
.SubsystemType
== IMAGE_SUBSYSTEM_WINDOWS_CUI
)
1313 PRTL_USER_PROCESS_PARAMETERS RemoteParameters
;
1315 /* Get the remote parameters */
1316 Status
= NtReadVirtualMemory(hProcess
,
1317 &RemotePeb
->ProcessParameters
,
1321 if (!NT_SUCCESS(Status
))
1323 DPRINT1("Failed to read memory\n");
1327 /* Duplicate and write the handles */
1328 BasepDuplicateAndWriteHandle(hProcess
,
1329 OurPeb
->ProcessParameters
->StandardInput
,
1330 &RemoteParameters
->StandardInput
);
1331 BasepDuplicateAndWriteHandle(hProcess
,
1332 OurPeb
->ProcessParameters
->StandardOutput
,
1333 &RemoteParameters
->StandardOutput
);
1334 BasepDuplicateAndWriteHandle(hProcess
,
1335 OurPeb
->ProcessParameters
->StandardError
,
1336 &RemoteParameters
->StandardError
);
1339 /* Create the first thread */
1340 DPRINT("Creating thread for process (EntryPoint = 0x%.08x)\n",
1341 SectionImageInfo
.TransferAddress
);
1342 hThread
= BasepCreateFirstThread(hProcess
,
1347 if (hThread
== NULL
)
1349 DPRINT1("Could not create Initial Thread\n");
1355 Status
= BasepNotifyCsrOfCreation(dwCreationFlags
,
1356 (HANDLE
)ProcessBasicInfo
.UniqueProcessId
);
1358 if (!NT_SUCCESS(Status
))
1360 DPRINT1("CSR Notification Failed");
1361 SetLastErrorByStatus(Status
);
1365 if (!(dwCreationFlags
& CREATE_SUSPENDED
))
1367 NtResumeThread(hThread
, &Dummy
);
1370 /* Cleanup Environment */
1371 if (lpEnvironment
&& !(dwCreationFlags
& CREATE_UNICODE_ENVIRONMENT
))
1373 RtlDestroyEnvironment(lpEnvironment
);
1377 lpProcessInformation
->dwProcessId
= (DWORD
)ClientId
.UniqueProcess
;
1378 lpProcessInformation
->dwThreadId
= (DWORD
)ClientId
.UniqueThread
;
1379 lpProcessInformation
->hProcess
= hProcess
;
1380 lpProcessInformation
->hThread
= hThread
;
1381 DPRINT("hThread[%lx]: %lx inside hProcess[%lx]: %lx\n", hThread
,
1382 ClientId
.UniqueThread
, ClientId
.UniqueProcess
, hProcess
);
1383 hProcess
= hThread
= NULL
;
1385 /* De-allocate heap strings */
1386 if (NameBuffer
) RtlFreeHeap(GetProcessHeap(), 0, NameBuffer
);
1387 if (ApplicationName
.Buffer
)
1388 RtlFreeHeap(GetProcessHeap(), 0, ApplicationName
.Buffer
);
1389 if (CurrentDirectory
) RtlFreeHeap(GetProcessHeap(), 0, CurrentDirectory
);
1390 if (QuotedCmdLine
) RtlFreeHeap(GetProcessHeap(), 0, QuotedCmdLine
);
1392 /* Kill any handles still alive */
1393 if (hSection
) NtClose(hSection
);
1396 /* We don't know any more details then this */
1397 NtTerminateProcess(hProcess
, STATUS_UNSUCCESSFUL
);
1400 if (hProcess
) NtClose(hProcess
);
1402 /* Return Success */