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 *******************************************************/
27 LONG
BaseExceptionFilter(EXCEPTION_POINTERS
*ExceptionInfo
)
29 LONG ExceptionDisposition
= EXCEPTION_EXECUTE_HANDLER
;
31 if (GlobalTopLevelExceptionFilter
!= NULL
)
35 ExceptionDisposition
= GlobalTopLevelExceptionFilter(ExceptionInfo
);
37 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
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 _SEH2_EXCEPT(BaseExceptionFilter(_SEH2_GetExceptionInformation()))
72 /* Get the SEH Error */
73 uExitCode
= _SEH2_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
,
163 if (!NT_SUCCESS(Status
))
173 * Converts ANSI to Unicode Environment
177 BasepConvertUnicodeEnvironment(OUT SIZE_T
* EnvSize
,
178 IN PVOID lpEnvironment
)
182 UNICODE_STRING UnicodeEnv
;
185 DPRINT("BasepConvertUnicodeEnvironment\n");
187 /* Scan the environment to calculate its Unicode size */
188 AnsiEnv
.Buffer
= pcScan
= (PCHAR
)lpEnvironment
;
191 pcScan
+= strlen(pcScan
) + 1;
194 /* Create our ANSI String */
195 if (pcScan
== (PCHAR
)lpEnvironment
)
197 AnsiEnv
.Length
= 2 * sizeof(CHAR
);
202 AnsiEnv
.Length
= (USHORT
)((ULONG_PTR
)pcScan
- (ULONG_PTR
)lpEnvironment
+ sizeof(CHAR
));
204 AnsiEnv
.MaximumLength
= AnsiEnv
.Length
+ 1;
206 /* Allocate memory for the Unicode Environment */
207 UnicodeEnv
.Buffer
= NULL
;
208 *EnvSize
= AnsiEnv
.MaximumLength
* sizeof(WCHAR
);
209 Status
= NtAllocateVirtualMemory(NtCurrentProcess(),
210 (PVOID
)&UnicodeEnv
.Buffer
,
216 if (!NT_SUCCESS(Status
))
218 SetLastError(Status
);
223 /* Use the allocated size */
224 UnicodeEnv
.MaximumLength
= (USHORT
)*EnvSize
;
227 RtlAnsiStringToUnicodeString(&UnicodeEnv
, &AnsiEnv
, FALSE
);
228 return UnicodeEnv
.Buffer
;
232 * Converts a Win32 Priority Class to NT
236 BasepConvertPriorityClass(IN ULONG dwCreationFlags
)
240 if(dwCreationFlags
& IDLE_PRIORITY_CLASS
)
242 ReturnClass
= PROCESS_PRIORITY_CLASS_IDLE
;
244 else if(dwCreationFlags
& BELOW_NORMAL_PRIORITY_CLASS
)
246 ReturnClass
= PROCESS_PRIORITY_CLASS_BELOW_NORMAL
;
248 else if(dwCreationFlags
& NORMAL_PRIORITY_CLASS
)
250 ReturnClass
= PROCESS_PRIORITY_CLASS_NORMAL
;
252 else if(dwCreationFlags
& ABOVE_NORMAL_PRIORITY_CLASS
)
254 ReturnClass
= PROCESS_PRIORITY_CLASS_ABOVE_NORMAL
;
256 else if(dwCreationFlags
& HIGH_PRIORITY_CLASS
)
258 ReturnClass
= PROCESS_PRIORITY_CLASS_HIGH
;
260 else if(dwCreationFlags
& REALTIME_PRIORITY_CLASS
)
262 /* Check for Privilege First */
263 if (BasepCheckRealTimePrivilege())
265 ReturnClass
= PROCESS_PRIORITY_CLASS_REALTIME
;
269 ReturnClass
= PROCESS_PRIORITY_CLASS_HIGH
;
274 ReturnClass
= PROCESS_PRIORITY_CLASS_INVALID
;
281 * Duplicates a standard handle and writes it where requested.
285 BasepDuplicateAndWriteHandle(IN HANDLE ProcessHandle
,
286 IN HANDLE StandardHandle
,
290 HANDLE DuplicatedHandle
;
293 DPRINT("BasepDuplicateAndWriteHandle. hProcess: %lx, Handle: %lx,"
294 "Address: %p\n", ProcessHandle
, StandardHandle
, Address
);
296 /* Don't touch Console Handles */
297 if (IsConsoleHandle(StandardHandle
))
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 DPRINT("BasepCopyHandles %p %p, %d\n", Params
, PebParams
, InheritHandles
);
336 /* Copy the handle if we are inheriting or if it's a console handle */
337 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardInput
))
339 Params
->StandardInput
= PebParams
->StandardInput
;
341 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardOutput
))
343 Params
->StandardOutput
= PebParams
->StandardOutput
;
345 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardError
))
347 Params
->StandardError
= PebParams
->StandardError
;
353 BasepInitializeEnvironment(HANDLE ProcessHandle
,
355 LPWSTR ApplicationPathName
,
356 LPWSTR lpCurrentDirectory
,
357 LPWSTR lpCommandLine
,
358 LPVOID lpEnvironment
,
360 LPSTARTUPINFOW StartupInfo
,
364 WCHAR FullPath
[MAX_PATH
];
366 LPWSTR DllPathString
;
367 PRTL_USER_PROCESS_PARAMETERS ProcessParameters
;
368 PRTL_USER_PROCESS_PARAMETERS RemoteParameters
= NULL
;
369 UNICODE_STRING DllPath
, ImageName
, CommandLine
, CurrentDirectory
;
375 UNICODE_STRING Desktop
, Shell
, Runtime
, Title
;
376 PPEB OurPeb
= NtCurrentPeb();
377 LPVOID Environment
= lpEnvironment
;
379 DPRINT("BasepInitializeEnvironment\n");
381 /* Get the full path name */
382 RetVal
= GetFullPathNameW(ApplicationPathName
,
386 DPRINT("ApplicationPathName: %S, FullPath: %S\n", ApplicationPathName
,
389 /* Get the DLL Path */
390 DllPathString
= BasepGetDllPath(FullPath
, Environment
);
392 /* Initialize Strings */
393 RtlInitUnicodeString(&DllPath
, DllPathString
);
394 RtlInitUnicodeString(&ImageName
, FullPath
);
395 RtlInitUnicodeString(&CommandLine
, lpCommandLine
);
396 RtlInitUnicodeString(&CurrentDirectory
, lpCurrentDirectory
);
398 /* Initialize more Strings from the Startup Info */
399 if (StartupInfo
->lpDesktop
)
401 RtlInitUnicodeString(&Desktop
, StartupInfo
->lpDesktop
);
405 RtlInitUnicodeString(&Desktop
, L
"");
407 if (StartupInfo
->lpReserved
)
409 RtlInitUnicodeString(&Shell
, StartupInfo
->lpReserved
);
413 RtlInitUnicodeString(&Shell
, L
"");
415 if (StartupInfo
->lpTitle
)
417 RtlInitUnicodeString(&Title
, StartupInfo
->lpTitle
);
421 RtlInitUnicodeString(&Title
, L
"");
424 /* This one is special because the length can differ */
425 Runtime
.Buffer
= (LPWSTR
)StartupInfo
->lpReserved2
;
426 Runtime
.MaximumLength
= Runtime
.Length
= StartupInfo
->cbReserved2
;
428 /* Create the Parameter Block */
429 DPRINT("Creating Process Parameters: %wZ %wZ %wZ %wZ %wZ %wZ %wZ\n",
430 &ImageName
, &DllPath
, &CommandLine
, &Desktop
, &Title
, &Shell
,
432 Status
= RtlCreateProcessParameters(&ProcessParameters
,
436 &CurrentDirectory
: NULL
,
444 if (!NT_SUCCESS(Status
))
446 DPRINT1("Failed to create process parameters!\n");
450 /* Check if we got an environment. If not, use ours */
453 /* Save pointer and start lookup */
454 Environment
= ScanChar
= ProcessParameters
->Environment
;
458 /* Save pointer and start lookup */
459 Environment
= ScanChar
= OurPeb
->ProcessParameters
->Environment
;
462 /* Find the environment size */
465 if (EnvSize
&& Environment
== lpEnvironment
)
467 /* its a converted ansi environment, bypass the length calculation */
468 EnviroSize
= EnvSize
;
474 ScanChar
+= wcslen(ScanChar
) + 1;
477 /* Calculate the size of the block */
478 if (ScanChar
== Environment
)
480 EnviroSize
= 2 * sizeof(WCHAR
);
484 EnviroSize
= (ULONG
)((ULONG_PTR
)ScanChar
- (ULONG_PTR
)Environment
+ sizeof(WCHAR
));
487 DPRINT("EnvironmentSize %ld\n", EnviroSize
);
489 /* Allocate and Initialize new Environment Block */
491 ProcessParameters
->Environment
= NULL
;
492 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
493 (PVOID
*)&ProcessParameters
->Environment
,
498 if (!NT_SUCCESS(Status
))
500 DPRINT1("Failed to allocate Environment Block\n");
504 /* Write the Environment Block */
505 ZwWriteVirtualMemory(ProcessHandle
,
506 ProcessParameters
->Environment
,
512 /* Write new parameters */
513 ProcessParameters
->StartingX
= StartupInfo
->dwX
;
514 ProcessParameters
->StartingY
= StartupInfo
->dwY
;
515 ProcessParameters
->CountX
= StartupInfo
->dwXSize
;
516 ProcessParameters
->CountY
= StartupInfo
->dwYSize
;
517 ProcessParameters
->CountCharsX
= StartupInfo
->dwXCountChars
;
518 ProcessParameters
->CountCharsY
= StartupInfo
->dwYCountChars
;
519 ProcessParameters
->FillAttribute
= StartupInfo
->dwFillAttribute
;
520 ProcessParameters
->WindowFlags
= StartupInfo
->dwFlags
;
521 ProcessParameters
->ShowWindowFlags
= StartupInfo
->wShowWindow
;
523 /* Write the handles only if we have to */
524 if (StartupInfo
->dwFlags
& STARTF_USESTDHANDLES
)
526 DPRINT("Using Standard Handles\n");
527 ProcessParameters
->StandardInput
= StartupInfo
->hStdInput
;
528 ProcessParameters
->StandardOutput
= StartupInfo
->hStdOutput
;
529 ProcessParameters
->StandardError
= StartupInfo
->hStdError
;
532 /* Use Special Flags for ConDllInitialize in Kernel32 */
533 if (CreationFlags
& DETACHED_PROCESS
)
535 ProcessParameters
->ConsoleHandle
= HANDLE_DETACHED_PROCESS
;
537 else if (CreationFlags
& CREATE_NO_WINDOW
)
539 ProcessParameters
->ConsoleHandle
= HANDLE_CREATE_NO_WINDOW
;
541 else if (CreationFlags
& CREATE_NEW_CONSOLE
)
543 ProcessParameters
->ConsoleHandle
= HANDLE_CREATE_NEW_CONSOLE
;
547 /* Inherit our Console Handle */
548 ProcessParameters
->ConsoleHandle
= OurPeb
->ProcessParameters
->ConsoleHandle
;
550 /* Is the shell trampling on our Handles? */
551 if (!(StartupInfo
->dwFlags
&
552 (STARTF_USESTDHANDLES
| STARTF_USEHOTKEY
| STARTF_SHELLPRIVATE
)))
554 /* Use handles from PEB, if inheriting or they are console */
555 DPRINT("Copying handles from parent\n");
556 BasepCopyHandles(ProcessParameters
,
557 OurPeb
->ProcessParameters
,
562 /* Also set the Console Flag */
563 if (CreationFlags
& CREATE_NEW_PROCESS_GROUP
)
565 ProcessParameters
->ConsoleFlags
= 1;
568 /* Allocate memory for the parameter block */
569 Size
= ProcessParameters
->Length
;
570 Status
= NtAllocateVirtualMemory(ProcessHandle
,
571 (PVOID
*)&RemoteParameters
,
576 if (!NT_SUCCESS(Status
))
578 DPRINT1("Failed to allocate Parameters Block\n");
582 /* Set the allocated size */
583 ProcessParameters
->MaximumLength
= Size
;
585 /* Handle some Parameter Flags */
586 ProcessParameters
->ConsoleFlags
= (CreationFlags
& CREATE_NEW_PROCESS_GROUP
);
587 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_USER
) ?
588 RTL_USER_PROCESS_PARAMETERS_PROFILE_USER
: 0;
589 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_KERNEL
) ?
590 RTL_USER_PROCESS_PARAMETERS_PROFILE_KERNEL
: 0;
591 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_SERVER
) ?
592 RTL_USER_PROCESS_PARAMETERS_PROFILE_SERVER
: 0;
593 ProcessParameters
->Flags
|= (NtCurrentPeb()->ProcessParameters
->Flags
&
594 RTL_USER_PROCESS_PARAMETERS_DISABLE_HEAP_CHECKS
);
596 /* Write the Parameter Block */
597 Status
= NtWriteVirtualMemory(ProcessHandle
,
600 ProcessParameters
->Length
,
603 /* Write the PEB Pointer */
604 Status
= NtWriteVirtualMemory(ProcessHandle
,
605 &Peb
->ProcessParameters
,
611 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath
.Buffer
);
612 RtlDestroyProcessParameters(ProcessParameters
);
614 DPRINT("Completed\n");
615 return STATUS_SUCCESS
;
618 /* FUNCTIONS ****************************************************************/
625 CreateProcessInternalW(HANDLE hToken
,
626 LPCWSTR lpApplicationName
,
627 LPWSTR lpCommandLine
,
628 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
629 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
630 BOOL bInheritHandles
,
631 DWORD dwCreationFlags
,
632 LPVOID lpEnvironment
,
633 LPCWSTR lpCurrentDirectory
,
634 LPSTARTUPINFOW lpStartupInfo
,
635 LPPROCESS_INFORMATION lpProcessInformation
,
639 PROCESS_PRIORITY_CLASS PriorityClass
;
640 BOOLEAN FoundQuotes
= FALSE
;
641 BOOLEAN QuotesNeeded
= FALSE
;
642 BOOLEAN CmdLineIsAppName
= FALSE
;
643 UNICODE_STRING ApplicationName
= { 0, 0, NULL
};
644 OBJECT_ATTRIBUTES LocalObjectAttributes
;
645 POBJECT_ATTRIBUTES ObjectAttributes
;
646 HANDLE hSection
= NULL
, hProcess
= NULL
, hThread
= NULL
, hDebug
= NULL
;
647 SECTION_IMAGE_INFORMATION SectionImageInfo
;
648 LPWSTR CurrentDirectory
= NULL
;
649 LPWSTR CurrentDirectoryPart
;
650 PROCESS_BASIC_INFORMATION ProcessBasicInfo
;
651 STARTUPINFOW StartupInfo
;
653 LPWSTR BatchCommandLine
;
655 UNICODE_STRING CommandLineString
;
657 LPWSTR QuotedCmdLine
= NULL
;
659 LPWSTR NullBuffer
= NULL
;
660 LPWSTR NameBuffer
= NULL
;
664 BOOLEAN SearchDone
= FALSE
;
665 BOOLEAN Escape
= FALSE
;
667 PPEB OurPeb
= NtCurrentPeb();
672 /* FIXME should process
673 * HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
674 * key (see http://blogs.msdn.com/oldnewthing/archive/2005/12/19/505449.aspx)
677 DPRINT("CreateProcessW: lpApplicationName: %S lpCommandLine: %S"
678 " lpEnvironment: %p lpCurrentDirectory: %S dwCreationFlags: %lx\n",
679 lpApplicationName
, lpCommandLine
, lpEnvironment
, lpCurrentDirectory
,
682 /* Flags we don't handle yet */
683 if (dwCreationFlags
& CREATE_SEPARATE_WOW_VDM
)
685 DPRINT1("CREATE_SEPARATE_WOW_VDM not handled\n");
687 if (dwCreationFlags
& CREATE_SHARED_WOW_VDM
)
689 DPRINT1("CREATE_SHARED_WOW_VDM not handled\n");
691 if (dwCreationFlags
& CREATE_FORCEDOS
)
693 DPRINT1("CREATE_FORCEDOS not handled\n");
696 /* Fail on this flag, it's only valid with the WithLogonW function */
697 if (dwCreationFlags
& CREATE_PRESERVE_CODE_AUTHZ_LEVEL
)
699 DPRINT1("Invalid flag used\n");
700 SetLastError(ERROR_INVALID_PARAMETER
);
704 /* This combination is illegal (see MSDN) */
705 if ((dwCreationFlags
& (DETACHED_PROCESS
| CREATE_NEW_CONSOLE
)) ==
706 (DETACHED_PROCESS
| CREATE_NEW_CONSOLE
))
708 DPRINT1("Invalid flag combo used\n");
709 SetLastError(ERROR_INVALID_PARAMETER
);
713 /* Another illegal combo */
714 if ((dwCreationFlags
& (CREATE_SEPARATE_WOW_VDM
| CREATE_SHARED_WOW_VDM
)) ==
715 (CREATE_SEPARATE_WOW_VDM
| CREATE_SHARED_WOW_VDM
))
717 DPRINT1("Invalid flag combo used\n");
718 SetLastError(ERROR_INVALID_PARAMETER
);
722 if (lpCurrentDirectory
)
724 if ((GetFileAttributesW(lpCurrentDirectory
) == INVALID_FILE_ATTRIBUTES
) ||
725 !(GetFileAttributesW(lpCurrentDirectory
) & FILE_ATTRIBUTE_DIRECTORY
))
727 SetLastError(ERROR_DIRECTORY
);
733 * We're going to modify and mask out flags and stuff in lpStartupInfo,
734 * so we'll use our own local copy for that.
736 StartupInfo
= *lpStartupInfo
;
738 /* FIXME: Use default Separate/Shared VDM Flag */
740 /* If we are inside a Job, use Separate VDM so it won't escape the Job */
741 if (!(dwCreationFlags
& CREATE_SEPARATE_WOW_VDM
))
743 if (NtIsProcessInJob(NtCurrentProcess(), NULL
))
745 /* Remove the shared flag and add the separate flag. */
746 dwCreationFlags
= (dwCreationFlags
&~ CREATE_SHARED_WOW_VDM
) |
747 CREATE_SEPARATE_WOW_VDM
;
752 * According to some sites, ShellExecuteEx uses an undocumented flag to
753 * send private handle data (such as HMONITOR or HICON). See:
754 * www.catch22.net/tuts/undoc01.asp. This implies that we can't use the
755 * standard handles anymore since we'd be overwriting this private data
757 if ((StartupInfo
.dwFlags
& STARTF_USESTDHANDLES
) &&
758 (StartupInfo
.dwFlags
& (STARTF_USEHOTKEY
| STARTF_SHELLPRIVATE
)))
760 StartupInfo
.dwFlags
&= ~STARTF_USESTDHANDLES
;
763 /* Start by zeroing out the fields */
764 RtlZeroMemory(lpProcessInformation
, sizeof(PROCESS_INFORMATION
));
766 /* Easy stuff first, convert the process priority class */
767 PriorityClass
.Foreground
= FALSE
;
768 PriorityClass
.PriorityClass
= (UCHAR
)BasepConvertPriorityClass(dwCreationFlags
);
772 /* Serach for escape sequences */
773 ScanString
= lpCommandLine
;
774 while (NULL
!= (ScanString
= wcschr(ScanString
, L
'^')))
777 if (*ScanString
== L
'\"' || *ScanString
== L
'^' || *ScanString
== L
'\"')
785 /* Get the application name and do all the proper formating necessary */
787 /* See if we have an application name (oh please let us have one!) */
788 if (!lpApplicationName
)
791 NameBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
793 MAX_PATH
* sizeof(WCHAR
));
794 if (NameBuffer
== NULL
)
796 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
800 /* This is all we have to work with :( */
801 lpApplicationName
= lpCommandLine
;
803 /* Initialize our friends at the beginning */
804 NullBuffer
= (LPWSTR
)lpApplicationName
;
805 ScanString
= (LPWSTR
)lpApplicationName
;
807 /* We will start by looking for a quote */
808 if (*ScanString
== L
'\"')
813 /* Advance past quote */
815 lpApplicationName
= ScanString
;
817 /* Find the closing quote */
820 if (*ScanString
== L
'\"' && *(ScanString
- 1) != L
'^')
823 NullBuffer
= ScanString
;
830 NullBuffer
= ScanString
;
835 /* No quotes, so we'll be looking for white space */
837 /* Reset the pointer */
838 lpApplicationName
= lpCommandLine
;
840 /* Find whitespace of Tab */
843 if (*ScanString
== ' ' || *ScanString
== '\t')
846 NullBuffer
= ScanString
;
852 NullBuffer
= ScanString
;
856 /* Set the Null Buffer */
857 SaveChar
= *NullBuffer
;
858 *NullBuffer
= UNICODE_NULL
;
860 /* Do a search for the file */
861 DPRINT("Ready for SearchPathW: %S\n", lpApplicationName
);
862 RetVal
= SearchPathW(NULL
,
867 NULL
) * sizeof(WCHAR
);
869 /* Did it find something? */
872 /* Get file attributes */
873 ULONG Attributes
= GetFileAttributesW(NameBuffer
);
874 if (Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
876 /* Give it a length of 0 to fail, this was a directory. */
882 RetVal
+= sizeof(WCHAR
);
886 /* Now check if we have a file, and if the path size is OK */
887 if (!RetVal
|| RetVal
>= (MAX_PATH
* sizeof(WCHAR
)))
892 /* We failed, try to get the Path Type */
893 DPRINT("SearchPathW failed. Retval: %ld\n", RetVal
);
894 PathType
= RtlDetermineDosPathNameType_U(lpApplicationName
);
896 /* If it's not relative, try to get the error */
897 if (PathType
!= RtlPathTypeRelative
)
899 /* This should fail, and give us a detailed LastError */
900 hFile
= CreateFileW(lpApplicationName
,
902 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
905 FILE_ATTRIBUTE_NORMAL
,
908 /* Did it actually NOT fail? */
909 if (hFile
!= INVALID_HANDLE_VALUE
)
913 SetLastErrorByStatus(STATUS_OBJECT_NAME_NOT_FOUND
);
918 /* Immediately set the error */
919 SetLastErrorByStatus(STATUS_OBJECT_NAME_NOT_FOUND
);
922 /* Did we already fail once? */
929 /* Not yet, cache it */
930 Error
= GetLastError();
933 /* Put back the command line */
934 *NullBuffer
= SaveChar
;
935 lpApplicationName
= NameBuffer
;
938 * If the search isn't done and we still have cmdline
939 * then start over. Ex: c:\ha ha ha\haha.exe
941 if (*ScanString
&& !SearchDone
)
943 /* Move in the buffer */
945 NullBuffer
= ScanString
;
947 /* We will have to add a quote, since there is a space*/
950 /* And we will also fake the fact we found one */
957 /* We totally failed */
961 /* Put back the command line */
962 *NullBuffer
= SaveChar
;
963 lpApplicationName
= NameBuffer
;
964 DPRINT("SearchPathW suceeded (%ld): %S\n", RetVal
, NameBuffer
);
966 else if (!lpCommandLine
|| *lpCommandLine
== UNICODE_NULL
)
968 /* We have an app name (good!) but no command line */
969 CmdLineIsAppName
= TRUE
;
970 lpCommandLine
= (LPWSTR
)lpApplicationName
;
973 /* At this point the name has been toyed with enough to be openable */
974 Status
= BasepMapFile(lpApplicationName
, &hSection
, &ApplicationName
);
976 /* Check for failure */
977 if (!NT_SUCCESS(Status
))
979 /* Could be a non-PE File */
982 /* Check if the Kernel tells us it's not even valid MZ */
983 case STATUS_INVALID_IMAGE_NE_FORMAT
:
984 case STATUS_INVALID_IMAGE_PROTECT
:
985 case STATUS_INVALID_IMAGE_NOT_MZ
:
988 /* If it's a DOS app, use VDM */
989 if ((BasepCheckDosApp(&ApplicationName
)))
991 DPRINT1("Launching VDM...\n");
992 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer
);
993 RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName
.Buffer
);
994 return CreateProcessW(L
"ntvdm.exe",
995 (LPWSTR
)((ULONG_PTR
)lpApplicationName
), /* FIXME: Buffer must be writable!!! */
1003 lpProcessInformation
);
1006 /* It's a batch file */
1007 Extension
= &ApplicationName
.Buffer
[ApplicationName
.Length
/
1010 /* Make sure the extensions are correct */
1011 if (_wcsnicmp(Extension
, L
".bat", 4) && _wcsnicmp(Extension
, L
".cmd", 4))
1013 SetLastError(ERROR_BAD_EXE_FORMAT
);
1017 /* Calculate the length of the command line */
1018 CmdLineLength
= wcslen(CMD_STRING
) + wcslen(lpCommandLine
) + 1;
1020 /* If we found quotes, then add them into the length size */
1021 if (CmdLineIsAppName
|| FoundQuotes
)
1023 CmdLineLength
*= sizeof(WCHAR
);
1025 /* Allocate space for the new command line */
1026 BatchCommandLine
= RtlAllocateHeap(RtlGetProcessHeap(),
1029 if (BatchCommandLine
== NULL
)
1031 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1036 wcscpy(BatchCommandLine
, CMD_STRING
);
1037 if (CmdLineIsAppName
|| FoundQuotes
)
1039 wcscat(BatchCommandLine
, L
"\"");
1041 wcscat(BatchCommandLine
, lpCommandLine
);
1042 if (CmdLineIsAppName
|| FoundQuotes
)
1044 wcscat(BatchCommandLine
, L
"\"");
1047 /* Create it as a Unicode String */
1048 RtlInitUnicodeString(&CommandLineString
, BatchCommandLine
);
1050 /* Set the command line to this */
1051 lpCommandLine
= CommandLineString
.Buffer
;
1052 lpApplicationName
= NULL
;
1055 RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName
.Buffer
);
1056 ApplicationName
.Buffer
= NULL
;
1060 case STATUS_INVALID_IMAGE_WIN_16
:
1062 /* It's a Win16 Image, use VDM */
1063 DPRINT1("Launching VDM...\n");
1064 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer
);
1065 RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName
.Buffer
);
1066 return CreateProcessW(L
"ntvdm.exe",
1067 (LPWSTR
)((ULONG_PTR
)lpApplicationName
), /* FIXME: Buffer must be writable!!! */
1068 lpProcessAttributes
,
1075 lpProcessInformation
);
1077 case STATUS_OBJECT_NAME_NOT_FOUND
:
1078 case STATUS_OBJECT_PATH_NOT_FOUND
:
1079 SetLastErrorByStatus(Status
);
1083 /* Invalid Image Type */
1084 SetLastError(ERROR_BAD_EXE_FORMAT
);
1089 /* Use our desktop if we didn't get any */
1090 if (!StartupInfo
.lpDesktop
)
1092 StartupInfo
.lpDesktop
= OurPeb
->ProcessParameters
->DesktopInfo
.Buffer
;
1095 /* FIXME: Check if Application is allowed to run */
1097 /* FIXME: Allow CREATE_SEPARATE only for WOW Apps, once we have that. */
1099 /* Get some information about the executable */
1100 Status
= ZwQuerySection(hSection
,
1101 SectionImageInformation
,
1103 sizeof(SectionImageInfo
),
1105 if(!NT_SUCCESS(Status
))
1107 DPRINT1("Unable to get SectionImageInformation, status 0x%x\n", Status
);
1108 SetLastErrorByStatus(Status
);
1112 /* Don't execute DLLs */
1113 if (SectionImageInfo
.ImageCharacteristics
& IMAGE_FILE_DLL
)
1115 DPRINT1("Can't execute a DLL\n");
1116 SetLastError(ERROR_BAD_EXE_FORMAT
);
1120 /* FIXME: Check for Debugger */
1122 /* FIXME: Check if Machine Type and SubSys Version Match */
1124 /* We don't support POSIX or anything else for now */
1125 if (IMAGE_SUBSYSTEM_WINDOWS_GUI
!= SectionImageInfo
.SubSystemType
&&
1126 IMAGE_SUBSYSTEM_WINDOWS_CUI
!= SectionImageInfo
.SubSystemType
)
1128 DPRINT1("Invalid subsystem %d\n", SectionImageInfo
.SubSystemType
);
1129 SetLastError(ERROR_BAD_EXE_FORMAT
);
1133 if (IMAGE_SUBSYSTEM_WINDOWS_GUI
== SectionImageInfo
.SubSystemType
)
1135 /* Do not create a console for GUI applications */
1136 dwCreationFlags
&= ~CREATE_NEW_CONSOLE
;
1137 dwCreationFlags
|= DETACHED_PROCESS
;
1140 /* Initialize the process object attributes */
1141 ObjectAttributes
= BasepConvertObjectAttributes(&LocalObjectAttributes
,
1142 lpProcessAttributes
,
1145 /* Check if we're going to be debugged */
1146 if (dwCreationFlags
& DEBUG_PROCESS
)
1148 /* FIXME: Set process flag */
1151 /* Check if we're going to be debugged */
1152 if (dwCreationFlags
& (DEBUG_PROCESS
| DEBUG_ONLY_THIS_PROCESS
))
1154 /* Connect to DbgUi */
1155 Status
= DbgUiConnectToDbg();
1156 if (!NT_SUCCESS(Status
))
1158 DPRINT1("Failed to connect to DbgUI!\n");
1159 SetLastErrorByStatus(Status
);
1163 /* Get the debug object */
1164 hDebug
= DbgUiGetThreadDebugObject();
1166 /* Check if only this process will be debugged */
1167 if (dwCreationFlags
& DEBUG_ONLY_THIS_PROCESS
)
1169 /* FIXME: Set process flag */
1173 /* Create the Process */
1174 Status
= NtCreateProcess(&hProcess
,
1178 (BOOLEAN
)bInheritHandles
,
1182 if (!NT_SUCCESS(Status
))
1184 DPRINT1("Unable to create process, status 0x%x\n", Status
);
1185 SetLastErrorByStatus(Status
);
1189 if (PriorityClass
.PriorityClass
!= PROCESS_PRIORITY_CLASS_INVALID
)
1192 Status
= NtSetInformationProcess(hProcess
,
1193 ProcessPriorityClass
,
1195 sizeof(PROCESS_PRIORITY_CLASS
));
1196 if(!NT_SUCCESS(Status
))
1198 DPRINT1("Unable to set new process priority, status 0x%x\n", Status
);
1199 SetLastErrorByStatus(Status
);
1204 /* Set Error Mode */
1205 if (dwCreationFlags
& CREATE_DEFAULT_ERROR_MODE
)
1207 ULONG ErrorMode
= SEM_FAILCRITICALERRORS
;
1208 NtSetInformationProcess(hProcess
,
1209 ProcessDefaultHardErrorMode
,
1214 /* Convert the directory to a full path */
1215 if (lpCurrentDirectory
)
1217 /* Allocate a buffer */
1218 CurrentDirectory
= RtlAllocateHeap(RtlGetProcessHeap(),
1220 (MAX_PATH
+ 1) * sizeof(WCHAR
));
1221 if (CurrentDirectory
== NULL
)
1223 DPRINT1("Cannot allocate memory for directory name\n");
1224 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1228 /* Get the length */
1229 if (GetFullPathNameW(lpCurrentDirectory
,
1232 &CurrentDirectoryPart
) > MAX_PATH
)
1234 DPRINT1("Directory name too long\n");
1235 SetLastError(ERROR_DIRECTORY
);
1240 /* Insert quotes if needed */
1241 if (QuotesNeeded
|| CmdLineIsAppName
)
1243 /* Allocate a buffer */
1244 QuotedCmdLine
= RtlAllocateHeap(RtlGetProcessHeap(),
1246 (wcslen(lpCommandLine
) + 2 + 1) *
1248 if (QuotedCmdLine
== NULL
)
1250 DPRINT1("Cannot allocate memory for quoted command line\n");
1251 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
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
);
1281 if (QuotedCmdLine
== NULL
)
1283 QuotedCmdLine
= RtlAllocateHeap(RtlGetProcessHeap(),
1285 (wcslen(lpCommandLine
) + 1) * sizeof(WCHAR
));
1286 if (QuotedCmdLine
== NULL
)
1288 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1291 wcscpy(QuotedCmdLine
, lpCommandLine
);
1294 ScanString
= QuotedCmdLine
;
1295 while (NULL
!= (ScanString
= wcschr(ScanString
, L
'^')))
1298 if (*ScanString
== L
'\"' || *ScanString
== L
'^' || *ScanString
== L
'\\')
1300 memmove(ScanString
-1, ScanString
, wcslen(ScanString
) * sizeof(WCHAR
) + sizeof(WCHAR
));
1305 /* Get the Process Information */
1306 Status
= NtQueryInformationProcess(hProcess
,
1307 ProcessBasicInformation
,
1309 sizeof(ProcessBasicInfo
),
1312 /* Convert the environment */
1313 if(lpEnvironment
&& !(dwCreationFlags
& CREATE_UNICODE_ENVIRONMENT
))
1315 lpEnvironment
= BasepConvertUnicodeEnvironment(&EnvSize
, lpEnvironment
);
1320 /* Create Process Environment */
1321 RemotePeb
= ProcessBasicInfo
.PebBaseAddress
;
1322 Status
= BasepInitializeEnvironment(hProcess
,
1324 (LPWSTR
)lpApplicationName
,
1326 (QuotesNeeded
|| CmdLineIsAppName
|| Escape
) ?
1327 QuotedCmdLine
: lpCommandLine
,
1334 /* Cleanup Environment */
1335 if (lpEnvironment
&& !(dwCreationFlags
& CREATE_UNICODE_ENVIRONMENT
))
1337 RtlDestroyEnvironment(lpEnvironment
);
1340 if (!NT_SUCCESS(Status
))
1342 DPRINT1("Could not initialize Process Environment\n");
1343 SetLastErrorByStatus(Status
);
1347 /* Close the section */
1351 /* Duplicate the handles if needed */
1352 if (!bInheritHandles
&& !(StartupInfo
.dwFlags
& STARTF_USESTDHANDLES
) &&
1353 SectionImageInfo
.SubSystemType
== IMAGE_SUBSYSTEM_WINDOWS_CUI
)
1355 PRTL_USER_PROCESS_PARAMETERS RemoteParameters
;
1357 /* Get the remote parameters */
1358 Status
= NtReadVirtualMemory(hProcess
,
1359 &RemotePeb
->ProcessParameters
,
1363 if (!NT_SUCCESS(Status
))
1365 DPRINT1("Failed to read memory\n");
1369 /* Duplicate and write the handles */
1370 BasepDuplicateAndWriteHandle(hProcess
,
1371 OurPeb
->ProcessParameters
->StandardInput
,
1372 &RemoteParameters
->StandardInput
);
1373 BasepDuplicateAndWriteHandle(hProcess
,
1374 OurPeb
->ProcessParameters
->StandardOutput
,
1375 &RemoteParameters
->StandardOutput
);
1376 BasepDuplicateAndWriteHandle(hProcess
,
1377 OurPeb
->ProcessParameters
->StandardError
,
1378 &RemoteParameters
->StandardError
);
1381 /* Create the first thread */
1382 DPRINT("Creating thread for process (EntryPoint = 0x%p)\n",
1383 SectionImageInfo
.TransferAddress
);
1384 hThread
= BasepCreateFirstThread(hProcess
,
1389 if (hThread
== NULL
)
1391 DPRINT1("Could not create Initial Thread\n");
1392 /* FIXME - set last error code */
1397 Status
= BasepNotifyCsrOfCreation(dwCreationFlags
,
1398 (HANDLE
)ProcessBasicInfo
.UniqueProcessId
,
1401 if (!NT_SUCCESS(Status
))
1403 DPRINT1("CSR Notification Failed");
1404 SetLastErrorByStatus(Status
);
1408 if (!(dwCreationFlags
& CREATE_SUSPENDED
))
1410 NtResumeThread(hThread
, &Dummy
);
1414 lpProcessInformation
->dwProcessId
= (DWORD
)ClientId
.UniqueProcess
;
1415 lpProcessInformation
->dwThreadId
= (DWORD
)ClientId
.UniqueThread
;
1416 lpProcessInformation
->hProcess
= hProcess
;
1417 lpProcessInformation
->hThread
= hThread
;
1418 DPRINT("hThread[%p]: %p inside hProcess[%p]: %p\n", hThread
,
1419 ClientId
.UniqueThread
, ClientId
.UniqueProcess
, hProcess
);
1420 hProcess
= hThread
= NULL
;
1424 /* De-allocate heap strings */
1426 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer
);
1428 if (ApplicationName
.Buffer
)
1429 RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName
.Buffer
);
1431 if (CurrentDirectory
)
1432 RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory
);
1435 RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine
);
1437 /* Kill any handles still alive */
1443 /* We don't know any more details then this */
1444 NtTerminateProcess(hProcess
, STATUS_UNSUCCESSFUL
);
1451 /* Return Success */
1460 CreateProcessW(LPCWSTR lpApplicationName
,
1461 LPWSTR lpCommandLine
,
1462 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
1463 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
1464 BOOL bInheritHandles
,
1465 DWORD dwCreationFlags
,
1466 LPVOID lpEnvironment
,
1467 LPCWSTR lpCurrentDirectory
,
1468 LPSTARTUPINFOW lpStartupInfo
,
1469 LPPROCESS_INFORMATION lpProcessInformation
)
1471 /* Call the internal (but exported) version */
1472 return CreateProcessInternalW(0,
1475 lpProcessAttributes
,
1482 lpProcessInformation
,
1491 CreateProcessInternalA(HANDLE hToken
,
1492 LPCSTR lpApplicationName
,
1493 LPSTR lpCommandLine
,
1494 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
1495 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
1496 BOOL bInheritHandles
,
1497 DWORD dwCreationFlags
,
1498 LPVOID lpEnvironment
,
1499 LPCSTR lpCurrentDirectory
,
1500 LPSTARTUPINFOA lpStartupInfo
,
1501 LPPROCESS_INFORMATION lpProcessInformation
,
1504 PUNICODE_STRING CommandLine
= NULL
;
1505 UNICODE_STRING DummyString
;
1506 UNICODE_STRING LiveCommandLine
;
1507 UNICODE_STRING ApplicationName
;
1508 UNICODE_STRING CurrentDirectory
;
1510 STARTUPINFOW StartupInfo
;
1512 DPRINT("dwCreationFlags %x, lpEnvironment %x, lpCurrentDirectory %x, "
1513 "lpStartupInfo %x, lpProcessInformation %x\n",
1514 dwCreationFlags
, lpEnvironment
, lpCurrentDirectory
,
1515 lpStartupInfo
, lpProcessInformation
);
1517 /* Copy Startup Info */
1518 RtlMoveMemory(&StartupInfo
, lpStartupInfo
, sizeof(*lpStartupInfo
));
1520 /* Initialize all strings to nothing */
1521 LiveCommandLine
.Buffer
= NULL
;
1522 DummyString
.Buffer
= NULL
;
1523 ApplicationName
.Buffer
= NULL
;
1524 CurrentDirectory
.Buffer
= NULL
;
1525 StartupInfo
.lpDesktop
= NULL
;
1526 StartupInfo
.lpReserved
= NULL
;
1527 StartupInfo
.lpTitle
= NULL
;
1529 /* Convert the Command line */
1532 /* If it's too long, then we'll have a problem */
1533 if ((strlen(lpCommandLine
) + 1) * sizeof(WCHAR
) <
1534 NtCurrentTeb()->StaticUnicodeString
.MaximumLength
)
1536 /* Cache it in the TEB */
1537 CommandLine
= Basep8BitStringToCachedUnicodeString(lpCommandLine
);
1541 /* Use a dynamic version */
1542 Basep8BitStringToHeapUnicodeString(&LiveCommandLine
,
1548 /* The logic below will use CommandLine, so we must make it valid */
1549 CommandLine
= &DummyString
;
1552 /* Convert the Name and Directory */
1553 if (lpApplicationName
)
1555 Basep8BitStringToHeapUnicodeString(&ApplicationName
,
1558 if (lpCurrentDirectory
)
1560 Basep8BitStringToHeapUnicodeString(&CurrentDirectory
,
1561 lpCurrentDirectory
);
1564 /* Now convert Startup Strings */
1565 if (lpStartupInfo
->lpReserved
)
1567 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpReserved
,
1568 &StartupInfo
.lpReserved
);
1570 if (lpStartupInfo
->lpDesktop
)
1572 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpDesktop
,
1573 &StartupInfo
.lpDesktop
);
1575 if (lpStartupInfo
->lpTitle
)
1577 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpTitle
,
1578 &StartupInfo
.lpTitle
);
1581 /* Call the Unicode function */
1582 bRetVal
= CreateProcessInternalW(hToken
,
1583 ApplicationName
.Buffer
,
1584 LiveCommandLine
.Buffer
?
1585 LiveCommandLine
.Buffer
: CommandLine
->Buffer
,
1586 lpProcessAttributes
,
1591 CurrentDirectory
.Buffer
,
1593 lpProcessInformation
,
1597 RtlFreeUnicodeString(&ApplicationName
);
1598 RtlFreeUnicodeString(&LiveCommandLine
);
1599 RtlFreeUnicodeString(&CurrentDirectory
);
1600 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo
.lpDesktop
);
1601 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo
.lpReserved
);
1602 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo
.lpTitle
);
1604 /* Return what Unicode did */
1609 * FUNCTION: The CreateProcess function creates a new process and its
1610 * primary thread. The new process executes the specified executable file
1613 * lpApplicationName = Pointer to name of executable module
1614 * lpCommandLine = Pointer to command line string
1615 * lpProcessAttributes = Process security attributes
1616 * lpThreadAttributes = Thread security attributes
1617 * bInheritHandles = Handle inheritance flag
1618 * dwCreationFlags = Creation flags
1619 * lpEnvironment = Pointer to new environment block
1620 * lpCurrentDirectory = Pointer to current directory name
1621 * lpStartupInfo = Pointer to startup info
1622 * lpProcessInformation = Pointer to process information
1628 CreateProcessA(LPCSTR lpApplicationName
,
1629 LPSTR lpCommandLine
,
1630 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
1631 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
1632 BOOL bInheritHandles
,
1633 DWORD dwCreationFlags
,
1634 LPVOID lpEnvironment
,
1635 LPCSTR lpCurrentDirectory
,
1636 LPSTARTUPINFOA lpStartupInfo
,
1637 LPPROCESS_INFORMATION lpProcessInformation
)
1639 /* Call the internal (but exported) version */
1640 return CreateProcessInternalA(0,
1643 lpProcessAttributes
,
1650 lpProcessInformation
,