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
)) return;
299 /* Duplicate the handle */
300 Status
= NtDuplicateObject(NtCurrentProcess(),
304 DUPLICATE_SAME_ACCESS
| DUPLICATE_SAME_ATTRIBUTES
,
307 if (NT_SUCCESS(Status
))
310 NtWriteVirtualMemory(ProcessHandle
,
320 BasepGetDllPath(LPWSTR FullPath
,
323 /* FIXME: Not yet implemented */
329 BasepCopyHandles(IN PRTL_USER_PROCESS_PARAMETERS Params
,
330 IN PRTL_USER_PROCESS_PARAMETERS PebParams
,
331 IN BOOL InheritHandles
)
333 DPRINT("BasepCopyHandles %p %p, %d\n", Params
, PebParams
, InheritHandles
);
335 /* Copy the handle if we are inheriting or if it's a console handle */
336 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardInput
))
338 Params
->StandardInput
= PebParams
->StandardInput
;
340 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardOutput
))
342 Params
->StandardOutput
= PebParams
->StandardOutput
;
344 if (InheritHandles
|| IsConsoleHandle(PebParams
->StandardError
))
346 Params
->StandardError
= PebParams
->StandardError
;
352 BasepInitializeEnvironment(HANDLE ProcessHandle
,
354 LPWSTR ApplicationPathName
,
355 LPWSTR lpCurrentDirectory
,
356 LPWSTR lpCommandLine
,
357 LPVOID lpEnvironment
,
359 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();
376 LPVOID Environment
= lpEnvironment
;
378 DPRINT("BasepInitializeEnvironment\n");
380 /* Get the full path name */
381 RetVal
= GetFullPathNameW(ApplicationPathName
,
385 DPRINT("ApplicationPathName: %S, FullPath: %S\n", ApplicationPathName
,
388 /* Get the DLL Path */
389 DllPathString
= BasepGetDllPath(FullPath
, Environment
);
391 /* Initialize Strings */
392 RtlInitUnicodeString(&DllPath
, DllPathString
);
393 RtlInitUnicodeString(&ImageName
, FullPath
);
394 RtlInitUnicodeString(&CommandLine
, lpCommandLine
);
395 RtlInitUnicodeString(&CurrentDirectory
, lpCurrentDirectory
);
397 /* Initialize more Strings from the Startup Info */
398 if (StartupInfo
->lpDesktop
)
400 RtlInitUnicodeString(&Desktop
, StartupInfo
->lpDesktop
);
404 RtlInitUnicodeString(&Desktop
, L
"");
406 if (StartupInfo
->lpReserved
)
408 RtlInitUnicodeString(&Shell
, StartupInfo
->lpReserved
);
412 RtlInitUnicodeString(&Shell
, L
"");
414 if (StartupInfo
->lpTitle
)
416 RtlInitUnicodeString(&Title
, StartupInfo
->lpTitle
);
420 RtlInitUnicodeString(&Title
, L
"");
423 /* This one is special because the length can differ */
424 Runtime
.Buffer
= (LPWSTR
)StartupInfo
->lpReserved2
;
425 Runtime
.MaximumLength
= Runtime
.Length
= StartupInfo
->cbReserved2
;
427 /* Create the Parameter Block */
428 DPRINT("Creating Process Parameters: %wZ %wZ %wZ %wZ %wZ %wZ %wZ\n",
429 &ImageName
, &DllPath
, &CommandLine
, &Desktop
, &Title
, &Shell
,
431 Status
= RtlCreateProcessParameters(&ProcessParameters
,
435 &CurrentDirectory
: NULL
,
443 if (!NT_SUCCESS(Status
))
445 DPRINT1("Failed to create process parameters!\n");
449 /* Check if we got an environment. If not, use ours */
452 /* Save pointer and start lookup */
453 Environment
= ScanChar
= ProcessParameters
->Environment
;
457 /* Save pointer and start lookup */
458 Environment
= ScanChar
= OurPeb
->ProcessParameters
->Environment
;
461 /* Find the environment size */
464 if (EnvSize
&& Environment
== lpEnvironment
)
466 /* its a converted ansi environment, bypass the length calculation */
467 EnviroSize
= EnvSize
;
473 ScanChar
+= wcslen(ScanChar
) + 1;
476 /* Calculate the size of the block */
477 if (ScanChar
== Environment
)
479 EnviroSize
= 2 * sizeof(WCHAR
);
483 EnviroSize
= (ULONG
)((ULONG_PTR
)ScanChar
- (ULONG_PTR
)Environment
+ sizeof(WCHAR
));
486 DPRINT("EnvironmentSize %ld\n", EnviroSize
);
488 /* Allocate and Initialize new Environment Block */
490 ProcessParameters
->Environment
= NULL
;
491 Status
= ZwAllocateVirtualMemory(ProcessHandle
,
492 (PVOID
*)&ProcessParameters
->Environment
,
497 if (!NT_SUCCESS(Status
))
499 DPRINT1("Failed to allocate Environment Block\n");
503 /* Write the Environment Block */
504 ZwWriteVirtualMemory(ProcessHandle
,
505 ProcessParameters
->Environment
,
511 /* Write new parameters */
512 ProcessParameters
->StartingX
= StartupInfo
->dwX
;
513 ProcessParameters
->StartingY
= StartupInfo
->dwY
;
514 ProcessParameters
->CountX
= StartupInfo
->dwXSize
;
515 ProcessParameters
->CountY
= StartupInfo
->dwYSize
;
516 ProcessParameters
->CountCharsX
= StartupInfo
->dwXCountChars
;
517 ProcessParameters
->CountCharsY
= StartupInfo
->dwYCountChars
;
518 ProcessParameters
->FillAttribute
= StartupInfo
->dwFillAttribute
;
519 ProcessParameters
->WindowFlags
= StartupInfo
->dwFlags
;
520 ProcessParameters
->ShowWindowFlags
= StartupInfo
->wShowWindow
;
522 /* Write the handles only if we have to */
523 if (StartupInfo
->dwFlags
& STARTF_USESTDHANDLES
)
525 DPRINT("Using Standard Handles\n");
526 ProcessParameters
->StandardInput
= StartupInfo
->hStdInput
;
527 ProcessParameters
->StandardOutput
= StartupInfo
->hStdOutput
;
528 ProcessParameters
->StandardError
= StartupInfo
->hStdError
;
531 /* Use Special Flags for ConDllInitialize in Kernel32 */
532 if (CreationFlags
& DETACHED_PROCESS
)
534 ProcessParameters
->ConsoleHandle
= HANDLE_DETACHED_PROCESS
;
536 else if (CreationFlags
& CREATE_NO_WINDOW
)
538 ProcessParameters
->ConsoleHandle
= HANDLE_CREATE_NO_WINDOW
;
540 else if (CreationFlags
& CREATE_NEW_CONSOLE
)
542 ProcessParameters
->ConsoleHandle
= HANDLE_CREATE_NEW_CONSOLE
;
546 /* Inherit our Console Handle */
547 ProcessParameters
->ConsoleHandle
= OurPeb
->ProcessParameters
->ConsoleHandle
;
549 /* Is the shell trampling on our Handles? */
550 if (!(StartupInfo
->dwFlags
&
551 (STARTF_USESTDHANDLES
| STARTF_USEHOTKEY
| STARTF_SHELLPRIVATE
)))
553 /* Use handles from PEB, if inheriting or they are console */
554 DPRINT("Copying handles from parent\n");
555 BasepCopyHandles(ProcessParameters
,
556 OurPeb
->ProcessParameters
,
561 /* Also set the Console Flag */
562 if (CreationFlags
& CREATE_NEW_PROCESS_GROUP
)
564 ProcessParameters
->ConsoleFlags
= 1;
567 /* Allocate memory for the parameter block */
568 Size
= ProcessParameters
->Length
;
569 Status
= NtAllocateVirtualMemory(ProcessHandle
,
570 (PVOID
*)&RemoteParameters
,
575 if (!NT_SUCCESS(Status
))
577 DPRINT1("Failed to allocate Parameters Block\n");
581 /* Set the allocated size */
582 ProcessParameters
->MaximumLength
= Size
;
584 /* Handle some Parameter Flags */
585 ProcessParameters
->ConsoleFlags
= (CreationFlags
& CREATE_NEW_PROCESS_GROUP
);
586 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_USER
) ?
587 RTL_USER_PROCESS_PARAMETERS_PROFILE_USER
: 0;
588 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_KERNEL
) ?
589 RTL_USER_PROCESS_PARAMETERS_PROFILE_KERNEL
: 0;
590 ProcessParameters
->Flags
|= (CreationFlags
& PROFILE_SERVER
) ?
591 RTL_USER_PROCESS_PARAMETERS_PROFILE_SERVER
: 0;
592 ProcessParameters
->Flags
|= (NtCurrentPeb()->ProcessParameters
->Flags
&
593 RTL_USER_PROCESS_PARAMETERS_DISABLE_HEAP_CHECKS
);
595 /* Write the Parameter Block */
596 Status
= NtWriteVirtualMemory(ProcessHandle
,
599 ProcessParameters
->Length
,
602 /* Write the PEB Pointer */
603 Status
= NtWriteVirtualMemory(ProcessHandle
,
604 &Peb
->ProcessParameters
,
610 RtlFreeHeap(RtlGetProcessHeap(), 0, DllPath
.Buffer
);
611 RtlDestroyProcessParameters(ProcessParameters
);
613 DPRINT("Completed\n");
614 return STATUS_SUCCESS
;
617 /* FUNCTIONS ****************************************************************/
624 CreateProcessInternalW(HANDLE hToken
,
625 LPCWSTR lpApplicationName
,
626 LPWSTR lpCommandLine
,
627 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
628 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
629 BOOL bInheritHandles
,
630 DWORD dwCreationFlags
,
631 LPVOID lpEnvironment
,
632 LPCWSTR lpCurrentDirectory
,
633 LPSTARTUPINFOW lpStartupInfo
,
634 LPPROCESS_INFORMATION lpProcessInformation
,
638 PROCESS_PRIORITY_CLASS PriorityClass
;
639 BOOLEAN FoundQuotes
= FALSE
;
640 BOOLEAN QuotesNeeded
= FALSE
;
641 BOOLEAN CmdLineIsAppName
= FALSE
;
642 UNICODE_STRING ApplicationName
= { 0, 0, NULL
};
643 OBJECT_ATTRIBUTES LocalObjectAttributes
;
644 POBJECT_ATTRIBUTES ObjectAttributes
;
645 HANDLE hSection
= NULL
, hProcess
= NULL
, hThread
= NULL
, hDebug
= NULL
;
646 SECTION_IMAGE_INFORMATION SectionImageInfo
;
647 LPWSTR CurrentDirectory
= NULL
;
648 LPWSTR CurrentDirectoryPart
;
649 PROCESS_BASIC_INFORMATION ProcessBasicInfo
;
650 STARTUPINFOW StartupInfo
;
652 LPWSTR BatchCommandLine
;
654 UNICODE_STRING CommandLineString
;
656 LPWSTR QuotedCmdLine
= NULL
;
658 LPWSTR NullBuffer
= NULL
;
659 LPWSTR NameBuffer
= NULL
;
663 BOOLEAN SearchDone
= FALSE
;
664 BOOLEAN Escape
= FALSE
;
666 PPEB OurPeb
= NtCurrentPeb();
671 /* FIXME should process
672 * HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
673 * key (see http://blogs.msdn.com/oldnewthing/archive/2005/12/19/505449.aspx)
676 DPRINT("CreateProcessW: lpApplicationName: %S lpCommandLine: %S"
677 " lpEnvironment: %p lpCurrentDirectory: %S dwCreationFlags: %lx\n",
678 lpApplicationName
, lpCommandLine
, lpEnvironment
, lpCurrentDirectory
,
681 /* Flags we don't handle yet */
682 if (dwCreationFlags
& CREATE_SEPARATE_WOW_VDM
)
684 DPRINT1("CREATE_SEPARATE_WOW_VDM not handled\n");
686 if (dwCreationFlags
& CREATE_SHARED_WOW_VDM
)
688 DPRINT1("CREATE_SHARED_WOW_VDM not handled\n");
690 if (dwCreationFlags
& CREATE_FORCEDOS
)
692 DPRINT1("CREATE_FORCEDOS not handled\n");
695 /* Fail on this flag, it's only valid with the WithLogonW function */
696 if (dwCreationFlags
& CREATE_PRESERVE_CODE_AUTHZ_LEVEL
)
698 DPRINT1("Invalid flag used\n");
699 SetLastError(ERROR_INVALID_PARAMETER
);
703 /* This combination is illegal (see MSDN) */
704 if ((dwCreationFlags
& (DETACHED_PROCESS
| CREATE_NEW_CONSOLE
)) ==
705 (DETACHED_PROCESS
| CREATE_NEW_CONSOLE
))
707 DPRINT1("Invalid flag combo used\n");
708 SetLastError(ERROR_INVALID_PARAMETER
);
712 /* Another illegal combo */
713 if ((dwCreationFlags
& (CREATE_SEPARATE_WOW_VDM
| CREATE_SHARED_WOW_VDM
)) ==
714 (CREATE_SEPARATE_WOW_VDM
| CREATE_SHARED_WOW_VDM
))
716 DPRINT1("Invalid flag combo used\n");
717 SetLastError(ERROR_INVALID_PARAMETER
);
721 if (lpCurrentDirectory
)
723 if ((GetFileAttributesW(lpCurrentDirectory
) == INVALID_FILE_ATTRIBUTES
) ||
724 !(GetFileAttributesW(lpCurrentDirectory
) & FILE_ATTRIBUTE_DIRECTORY
))
726 SetLastError(ERROR_DIRECTORY
);
732 * We're going to modify and mask out flags and stuff in lpStartupInfo,
733 * so we'll use our own local copy for that.
735 StartupInfo
= *lpStartupInfo
;
737 /* FIXME: Use default Separate/Shared VDM Flag */
739 /* If we are inside a Job, use Separate VDM so it won't escape the Job */
740 if (!(dwCreationFlags
& CREATE_SEPARATE_WOW_VDM
))
742 if (NtIsProcessInJob(NtCurrentProcess(), NULL
))
744 /* Remove the shared flag and add the separate flag. */
745 dwCreationFlags
= (dwCreationFlags
&~ CREATE_SHARED_WOW_VDM
) |
746 CREATE_SEPARATE_WOW_VDM
;
751 * According to some sites, ShellExecuteEx uses an undocumented flag to
752 * send private handle data (such as HMONITOR or HICON). See:
753 * www.catch22.net/tuts/undoc01.asp. This implies that we can't use the
754 * standard handles anymore since we'd be overwriting this private data
756 if ((StartupInfo
.dwFlags
& STARTF_USESTDHANDLES
) &&
757 (StartupInfo
.dwFlags
& (STARTF_USEHOTKEY
| STARTF_SHELLPRIVATE
)))
759 StartupInfo
.dwFlags
&= ~STARTF_USESTDHANDLES
;
762 /* Start by zeroing out the fields */
763 RtlZeroMemory(lpProcessInformation
, sizeof(PROCESS_INFORMATION
));
765 /* Easy stuff first, convert the process priority class */
766 PriorityClass
.Foreground
= FALSE
;
767 PriorityClass
.PriorityClass
= (UCHAR
)BasepConvertPriorityClass(dwCreationFlags
);
771 /* Serach for escape sequences */
772 ScanString
= lpCommandLine
;
773 while (NULL
!= (ScanString
= wcschr(ScanString
, L
'^')))
776 if (*ScanString
== L
'\"' || *ScanString
== L
'^' || *ScanString
== L
'\"')
784 /* Get the application name and do all the proper formating necessary */
786 /* See if we have an application name (oh please let us have one!) */
787 if (!lpApplicationName
)
790 NameBuffer
= RtlAllocateHeap(RtlGetProcessHeap(),
792 MAX_PATH
* sizeof(WCHAR
));
793 if (NameBuffer
== NULL
)
795 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
799 /* This is all we have to work with :( */
800 lpApplicationName
= lpCommandLine
;
802 /* Initialize our friends at the beginning */
803 NullBuffer
= (LPWSTR
)lpApplicationName
;
804 ScanString
= (LPWSTR
)lpApplicationName
;
806 /* We will start by looking for a quote */
807 if (*ScanString
== L
'\"')
812 /* Advance past quote */
814 lpApplicationName
= ScanString
;
816 /* Find the closing quote */
819 if (*ScanString
== L
'\"' && *(ScanString
- 1) != L
'^')
822 NullBuffer
= ScanString
;
829 NullBuffer
= ScanString
;
834 /* No quotes, so we'll be looking for white space */
836 /* Reset the pointer */
837 lpApplicationName
= lpCommandLine
;
839 /* Find whitespace of Tab */
842 if (*ScanString
== ' ' || *ScanString
== '\t')
845 NullBuffer
= ScanString
;
851 NullBuffer
= ScanString
;
855 /* Set the Null Buffer */
856 SaveChar
= *NullBuffer
;
857 *NullBuffer
= UNICODE_NULL
;
859 /* Do a search for the file */
860 DPRINT("Ready for SearchPathW: %S\n", lpApplicationName
);
861 RetVal
= SearchPathW(NULL
,
866 NULL
) * sizeof(WCHAR
);
868 /* Did it find something? */
871 /* Get file attributes */
872 ULONG Attributes
= GetFileAttributesW(NameBuffer
);
873 if (Attributes
& FILE_ATTRIBUTE_DIRECTORY
)
875 /* Give it a length of 0 to fail, this was a directory. */
881 RetVal
+= sizeof(WCHAR
);
885 /* Now check if we have a file, and if the path size is OK */
886 if (!RetVal
|| RetVal
>= (MAX_PATH
* sizeof(WCHAR
)))
891 /* We failed, try to get the Path Type */
892 DPRINT("SearchPathW failed. Retval: %ld\n", RetVal
);
893 PathType
= RtlDetermineDosPathNameType_U(lpApplicationName
);
895 /* If it's not relative, try to get the error */
896 if (PathType
!= RtlPathTypeRelative
)
898 /* This should fail, and give us a detailed LastError */
899 hFile
= CreateFileW(lpApplicationName
,
901 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
904 FILE_ATTRIBUTE_NORMAL
,
907 /* Did it actually NOT fail? */
908 if (hFile
!= INVALID_HANDLE_VALUE
)
912 SetLastErrorByStatus(STATUS_OBJECT_NAME_NOT_FOUND
);
917 /* Immediately set the error */
918 SetLastErrorByStatus(STATUS_OBJECT_NAME_NOT_FOUND
);
921 /* Did we already fail once? */
928 /* Not yet, cache it */
929 Error
= GetLastError();
932 /* Put back the command line */
933 *NullBuffer
= SaveChar
;
934 lpApplicationName
= NameBuffer
;
937 * If the search isn't done and we still have cmdline
938 * then start over. Ex: c:\ha ha ha\haha.exe
940 if (*ScanString
&& !SearchDone
)
942 /* Move in the buffer */
944 NullBuffer
= ScanString
;
946 /* We will have to add a quote, since there is a space*/
949 /* And we will also fake the fact we found one */
956 /* We totally failed */
960 /* Put back the command line */
961 *NullBuffer
= SaveChar
;
962 lpApplicationName
= NameBuffer
;
963 DPRINT("SearchPathW suceeded (%ld): %S\n", RetVal
, NameBuffer
);
965 else if (!lpCommandLine
|| *lpCommandLine
== UNICODE_NULL
)
967 /* We have an app name (good!) but no command line */
968 CmdLineIsAppName
= TRUE
;
969 lpCommandLine
= (LPWSTR
)lpApplicationName
;
972 /* At this point the name has been toyed with enough to be openable */
973 Status
= BasepMapFile(lpApplicationName
, &hSection
, &ApplicationName
);
975 /* Check for failure */
976 if (!NT_SUCCESS(Status
))
978 /* Could be a non-PE File */
981 /* Check if the Kernel tells us it's not even valid MZ */
982 case STATUS_INVALID_IMAGE_NE_FORMAT
:
983 case STATUS_INVALID_IMAGE_PROTECT
:
984 case STATUS_INVALID_IMAGE_NOT_MZ
:
987 /* If it's a DOS app, use VDM */
988 if ((BasepCheckDosApp(&ApplicationName
)))
990 DPRINT1("Launching VDM...\n");
991 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer
);
992 RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName
.Buffer
);
993 return CreateProcessW(L
"ntvdm.exe",
994 (LPWSTR
)((ULONG_PTR
)lpApplicationName
), /* FIXME: Buffer must be writable!!! */
1002 lpProcessInformation
);
1005 /* It's a batch file */
1006 Extension
= &ApplicationName
.Buffer
[ApplicationName
.Length
/
1009 /* Make sure the extensions are correct */
1010 if (_wcsnicmp(Extension
, L
".bat", 4) && _wcsnicmp(Extension
, L
".cmd", 4))
1012 SetLastError(ERROR_BAD_EXE_FORMAT
);
1016 /* Calculate the length of the command line */
1017 CmdLineLength
= wcslen(CMD_STRING
) + wcslen(lpCommandLine
) + 1;
1019 /* If we found quotes, then add them into the length size */
1020 if (CmdLineIsAppName
|| FoundQuotes
) CmdLineLength
+= 2;
1021 CmdLineLength
*= sizeof(WCHAR
);
1023 /* Allocate space for the new command line */
1024 BatchCommandLine
= RtlAllocateHeap(RtlGetProcessHeap(),
1027 if (BatchCommandLine
== NULL
)
1029 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1034 wcscpy(BatchCommandLine
, CMD_STRING
);
1035 if (CmdLineIsAppName
|| FoundQuotes
)
1037 wcscat(BatchCommandLine
, L
"\"");
1039 wcscat(BatchCommandLine
, lpCommandLine
);
1040 if (CmdLineIsAppName
|| FoundQuotes
)
1042 wcscat(BatchCommandLine
, L
"\"");
1045 /* Create it as a Unicode String */
1046 RtlInitUnicodeString(&CommandLineString
, BatchCommandLine
);
1048 /* Set the command line to this */
1049 lpCommandLine
= CommandLineString
.Buffer
;
1050 lpApplicationName
= NULL
;
1053 RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName
.Buffer
);
1054 ApplicationName
.Buffer
= NULL
;
1058 case STATUS_INVALID_IMAGE_WIN_16
:
1060 /* It's a Win16 Image, use VDM */
1061 DPRINT1("Launching VDM...\n");
1062 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer
);
1063 RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName
.Buffer
);
1064 return CreateProcessW(L
"ntvdm.exe",
1065 (LPWSTR
)((ULONG_PTR
)lpApplicationName
), /* FIXME: Buffer must be writable!!! */
1066 lpProcessAttributes
,
1073 lpProcessInformation
);
1075 case STATUS_OBJECT_NAME_NOT_FOUND
:
1076 case STATUS_OBJECT_PATH_NOT_FOUND
:
1077 SetLastErrorByStatus(Status
);
1081 /* Invalid Image Type */
1082 SetLastError(ERROR_BAD_EXE_FORMAT
);
1087 /* Use our desktop if we didn't get any */
1088 if (!StartupInfo
.lpDesktop
)
1090 StartupInfo
.lpDesktop
= OurPeb
->ProcessParameters
->DesktopInfo
.Buffer
;
1093 /* FIXME: Check if Application is allowed to run */
1095 /* FIXME: Allow CREATE_SEPARATE only for WOW Apps, once we have that. */
1097 /* Get some information about the executable */
1098 Status
= ZwQuerySection(hSection
,
1099 SectionImageInformation
,
1101 sizeof(SectionImageInfo
),
1103 if(!NT_SUCCESS(Status
))
1105 DPRINT1("Unable to get SectionImageInformation, status 0x%x\n", Status
);
1106 SetLastErrorByStatus(Status
);
1110 /* Don't execute DLLs */
1111 if (SectionImageInfo
.ImageCharacteristics
& IMAGE_FILE_DLL
)
1113 DPRINT1("Can't execute a DLL\n");
1114 SetLastError(ERROR_BAD_EXE_FORMAT
);
1118 /* FIXME: Check for Debugger */
1120 /* FIXME: Check if Machine Type and SubSys Version Match */
1122 /* We don't support POSIX or anything else for now */
1123 if (IMAGE_SUBSYSTEM_WINDOWS_GUI
!= SectionImageInfo
.SubSystemType
&&
1124 IMAGE_SUBSYSTEM_WINDOWS_CUI
!= SectionImageInfo
.SubSystemType
)
1126 DPRINT1("Invalid subsystem %d\n", SectionImageInfo
.SubSystemType
);
1127 SetLastError(ERROR_BAD_EXE_FORMAT
);
1131 if (IMAGE_SUBSYSTEM_WINDOWS_GUI
== SectionImageInfo
.SubSystemType
)
1133 /* Do not create a console for GUI applications */
1134 dwCreationFlags
&= ~CREATE_NEW_CONSOLE
;
1135 dwCreationFlags
|= DETACHED_PROCESS
;
1138 /* Initialize the process object attributes */
1139 ObjectAttributes
= BasepConvertObjectAttributes(&LocalObjectAttributes
,
1140 lpProcessAttributes
,
1143 /* Check if we're going to be debugged */
1144 if (dwCreationFlags
& DEBUG_PROCESS
)
1146 /* FIXME: Set process flag */
1149 /* Check if we're going to be debugged */
1150 if (dwCreationFlags
& (DEBUG_PROCESS
| DEBUG_ONLY_THIS_PROCESS
))
1152 /* Connect to DbgUi */
1153 Status
= DbgUiConnectToDbg();
1154 if (!NT_SUCCESS(Status
))
1156 DPRINT1("Failed to connect to DbgUI!\n");
1157 SetLastErrorByStatus(Status
);
1161 /* Get the debug object */
1162 hDebug
= DbgUiGetThreadDebugObject();
1164 /* Check if only this process will be debugged */
1165 if (dwCreationFlags
& DEBUG_ONLY_THIS_PROCESS
)
1167 /* FIXME: Set process flag */
1171 /* Create the Process */
1172 Status
= NtCreateProcess(&hProcess
,
1176 (BOOLEAN
)bInheritHandles
,
1180 if (!NT_SUCCESS(Status
))
1182 DPRINT1("Unable to create process, status 0x%x\n", Status
);
1183 SetLastErrorByStatus(Status
);
1187 if (PriorityClass
.PriorityClass
!= PROCESS_PRIORITY_CLASS_INVALID
)
1190 Status
= NtSetInformationProcess(hProcess
,
1191 ProcessPriorityClass
,
1193 sizeof(PROCESS_PRIORITY_CLASS
));
1194 if(!NT_SUCCESS(Status
))
1196 DPRINT1("Unable to set new process priority, status 0x%x\n", Status
);
1197 SetLastErrorByStatus(Status
);
1202 /* Set Error Mode */
1203 if (dwCreationFlags
& CREATE_DEFAULT_ERROR_MODE
)
1205 ULONG ErrorMode
= SEM_FAILCRITICALERRORS
;
1206 NtSetInformationProcess(hProcess
,
1207 ProcessDefaultHardErrorMode
,
1212 /* Convert the directory to a full path */
1213 if (lpCurrentDirectory
)
1215 /* Allocate a buffer */
1216 CurrentDirectory
= RtlAllocateHeap(RtlGetProcessHeap(),
1218 (MAX_PATH
+ 1) * sizeof(WCHAR
));
1219 if (CurrentDirectory
== NULL
)
1221 DPRINT1("Cannot allocate memory for directory name\n");
1222 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1226 /* Get the length */
1227 if (GetFullPathNameW(lpCurrentDirectory
,
1230 &CurrentDirectoryPart
) > MAX_PATH
)
1232 DPRINT1("Directory name too long\n");
1233 SetLastError(ERROR_DIRECTORY
);
1238 /* Insert quotes if needed */
1239 if (QuotesNeeded
|| CmdLineIsAppName
)
1241 /* Allocate a buffer */
1242 QuotedCmdLine
= RtlAllocateHeap(RtlGetProcessHeap(),
1244 (wcslen(lpCommandLine
) + 2 + 1) *
1246 if (QuotedCmdLine
== NULL
)
1248 DPRINT1("Cannot allocate memory for quoted command line\n");
1249 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1253 /* Copy the first quote */
1254 wcscpy(QuotedCmdLine
, L
"\"");
1256 /* Save a null char */
1259 SaveChar
= *NullBuffer
;
1260 *NullBuffer
= UNICODE_NULL
;
1263 /* Add the command line and the finishing quote */
1264 wcscat(QuotedCmdLine
, lpCommandLine
);
1265 wcscat(QuotedCmdLine
, L
"\"");
1267 /* Add the null char */
1270 *NullBuffer
= SaveChar
;
1271 wcscat(QuotedCmdLine
, NullBuffer
);
1274 DPRINT("Quoted CmdLine: %S\n", QuotedCmdLine
);
1279 if (QuotedCmdLine
== NULL
)
1281 QuotedCmdLine
= RtlAllocateHeap(RtlGetProcessHeap(),
1283 (wcslen(lpCommandLine
) + 1) * sizeof(WCHAR
));
1284 if (QuotedCmdLine
== NULL
)
1286 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1289 wcscpy(QuotedCmdLine
, lpCommandLine
);
1292 ScanString
= QuotedCmdLine
;
1293 while (NULL
!= (ScanString
= wcschr(ScanString
, L
'^')))
1296 if (*ScanString
== L
'\"' || *ScanString
== L
'^' || *ScanString
== L
'\\')
1298 memmove(ScanString
-1, ScanString
, wcslen(ScanString
) * sizeof(WCHAR
) + sizeof(WCHAR
));
1303 /* Get the Process Information */
1304 Status
= NtQueryInformationProcess(hProcess
,
1305 ProcessBasicInformation
,
1307 sizeof(ProcessBasicInfo
),
1310 /* Convert the environment */
1311 if(lpEnvironment
&& !(dwCreationFlags
& CREATE_UNICODE_ENVIRONMENT
))
1313 lpEnvironment
= BasepConvertUnicodeEnvironment(&EnvSize
, lpEnvironment
);
1314 if (!lpEnvironment
) goto Cleanup
;
1317 /* Create Process Environment */
1318 RemotePeb
= ProcessBasicInfo
.PebBaseAddress
;
1319 Status
= BasepInitializeEnvironment(hProcess
,
1321 (LPWSTR
)lpApplicationName
,
1323 (QuotesNeeded
|| CmdLineIsAppName
|| Escape
) ?
1324 QuotedCmdLine
: lpCommandLine
,
1331 /* Cleanup Environment */
1332 if (lpEnvironment
&& !(dwCreationFlags
& CREATE_UNICODE_ENVIRONMENT
))
1334 RtlDestroyEnvironment(lpEnvironment
);
1337 if (!NT_SUCCESS(Status
))
1339 DPRINT1("Could not initialize Process Environment\n");
1340 SetLastErrorByStatus(Status
);
1344 /* Close the section */
1348 /* Duplicate the handles if needed */
1349 if (!bInheritHandles
&& !(StartupInfo
.dwFlags
& STARTF_USESTDHANDLES
) &&
1350 SectionImageInfo
.SubSystemType
== IMAGE_SUBSYSTEM_WINDOWS_CUI
)
1352 PRTL_USER_PROCESS_PARAMETERS RemoteParameters
;
1354 /* Get the remote parameters */
1355 Status
= NtReadVirtualMemory(hProcess
,
1356 &RemotePeb
->ProcessParameters
,
1360 if (!NT_SUCCESS(Status
))
1362 DPRINT1("Failed to read memory\n");
1366 /* Duplicate and write the handles */
1367 BasepDuplicateAndWriteHandle(hProcess
,
1368 OurPeb
->ProcessParameters
->StandardInput
,
1369 &RemoteParameters
->StandardInput
);
1370 BasepDuplicateAndWriteHandle(hProcess
,
1371 OurPeb
->ProcessParameters
->StandardOutput
,
1372 &RemoteParameters
->StandardOutput
);
1373 BasepDuplicateAndWriteHandle(hProcess
,
1374 OurPeb
->ProcessParameters
->StandardError
,
1375 &RemoteParameters
->StandardError
);
1378 /* Create the first thread */
1379 DPRINT("Creating thread for process (EntryPoint = 0x%p)\n",
1380 SectionImageInfo
.TransferAddress
);
1381 hThread
= BasepCreateFirstThread(hProcess
,
1386 if (hThread
== NULL
)
1388 DPRINT1("Could not create Initial Thread\n");
1389 /* FIXME - set last error code */
1394 Status
= BasepNotifyCsrOfCreation(dwCreationFlags
,
1395 (HANDLE
)ProcessBasicInfo
.UniqueProcessId
,
1398 if (!NT_SUCCESS(Status
))
1400 DPRINT1("CSR Notification Failed");
1401 SetLastErrorByStatus(Status
);
1405 if (!(dwCreationFlags
& CREATE_SUSPENDED
))
1407 NtResumeThread(hThread
, &Dummy
);
1411 lpProcessInformation
->dwProcessId
= (DWORD
)ClientId
.UniqueProcess
;
1412 lpProcessInformation
->dwThreadId
= (DWORD
)ClientId
.UniqueThread
;
1413 lpProcessInformation
->hProcess
= hProcess
;
1414 lpProcessInformation
->hThread
= hThread
;
1415 DPRINT("hThread[%p]: %p inside hProcess[%p]: %p\n", hThread
,
1416 ClientId
.UniqueThread
, ClientId
.UniqueProcess
, hProcess
);
1417 hProcess
= hThread
= NULL
;
1421 /* De-allocate heap strings */
1422 if (NameBuffer
) RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer
);
1423 if (ApplicationName
.Buffer
)
1424 RtlFreeHeap(RtlGetProcessHeap(), 0, ApplicationName
.Buffer
);
1425 if (CurrentDirectory
) RtlFreeHeap(RtlGetProcessHeap(), 0, CurrentDirectory
);
1426 if (QuotedCmdLine
) RtlFreeHeap(RtlGetProcessHeap(), 0, QuotedCmdLine
);
1428 /* Kill any handles still alive */
1429 if (hSection
) NtClose(hSection
);
1432 /* We don't know any more details then this */
1433 NtTerminateProcess(hProcess
, STATUS_UNSUCCESSFUL
);
1436 if (hProcess
) NtClose(hProcess
);
1438 /* Return Success */
1447 CreateProcessW(LPCWSTR lpApplicationName
,
1448 LPWSTR lpCommandLine
,
1449 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
1450 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
1451 BOOL bInheritHandles
,
1452 DWORD dwCreationFlags
,
1453 LPVOID lpEnvironment
,
1454 LPCWSTR lpCurrentDirectory
,
1455 LPSTARTUPINFOW lpStartupInfo
,
1456 LPPROCESS_INFORMATION lpProcessInformation
)
1458 /* Call the internal (but exported) version */
1459 return CreateProcessInternalW(0,
1462 lpProcessAttributes
,
1469 lpProcessInformation
,
1478 CreateProcessInternalA(HANDLE hToken
,
1479 LPCSTR lpApplicationName
,
1480 LPSTR lpCommandLine
,
1481 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
1482 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
1483 BOOL bInheritHandles
,
1484 DWORD dwCreationFlags
,
1485 LPVOID lpEnvironment
,
1486 LPCSTR lpCurrentDirectory
,
1487 LPSTARTUPINFOA lpStartupInfo
,
1488 LPPROCESS_INFORMATION lpProcessInformation
,
1491 PUNICODE_STRING CommandLine
= NULL
;
1492 UNICODE_STRING DummyString
;
1493 UNICODE_STRING LiveCommandLine
;
1494 UNICODE_STRING ApplicationName
;
1495 UNICODE_STRING CurrentDirectory
;
1497 STARTUPINFOW StartupInfo
;
1499 DPRINT("dwCreationFlags %x, lpEnvironment %x, lpCurrentDirectory %x, "
1500 "lpStartupInfo %x, lpProcessInformation %x\n",
1501 dwCreationFlags
, lpEnvironment
, lpCurrentDirectory
,
1502 lpStartupInfo
, lpProcessInformation
);
1504 /* Copy Startup Info */
1505 RtlMoveMemory(&StartupInfo
, lpStartupInfo
, sizeof(*lpStartupInfo
));
1507 /* Initialize all strings to nothing */
1508 LiveCommandLine
.Buffer
= NULL
;
1509 DummyString
.Buffer
= NULL
;
1510 ApplicationName
.Buffer
= NULL
;
1511 CurrentDirectory
.Buffer
= NULL
;
1512 StartupInfo
.lpDesktop
= NULL
;
1513 StartupInfo
.lpReserved
= NULL
;
1514 StartupInfo
.lpTitle
= NULL
;
1516 /* Convert the Command line */
1519 /* If it's too long, then we'll have a problem */
1520 if ((strlen(lpCommandLine
) + 1) * sizeof(WCHAR
) <
1521 NtCurrentTeb()->StaticUnicodeString
.MaximumLength
)
1523 /* Cache it in the TEB */
1524 CommandLine
= Basep8BitStringToCachedUnicodeString(lpCommandLine
);
1528 /* Use a dynamic version */
1529 Basep8BitStringToHeapUnicodeString(&LiveCommandLine
,
1535 /* The logic below will use CommandLine, so we must make it valid */
1536 CommandLine
= &DummyString
;
1539 /* Convert the Name and Directory */
1540 if (lpApplicationName
)
1542 Basep8BitStringToHeapUnicodeString(&ApplicationName
,
1545 if (lpCurrentDirectory
)
1547 Basep8BitStringToHeapUnicodeString(&CurrentDirectory
,
1548 lpCurrentDirectory
);
1551 /* Now convert Startup Strings */
1552 if (lpStartupInfo
->lpReserved
)
1554 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpReserved
,
1555 &StartupInfo
.lpReserved
);
1557 if (lpStartupInfo
->lpDesktop
)
1559 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpDesktop
,
1560 &StartupInfo
.lpDesktop
);
1562 if (lpStartupInfo
->lpTitle
)
1564 BasepAnsiStringToHeapUnicodeString(lpStartupInfo
->lpTitle
,
1565 &StartupInfo
.lpTitle
);
1568 /* Call the Unicode function */
1569 bRetVal
= CreateProcessInternalW(hToken
,
1570 ApplicationName
.Buffer
,
1571 LiveCommandLine
.Buffer
?
1572 LiveCommandLine
.Buffer
: CommandLine
->Buffer
,
1573 lpProcessAttributes
,
1578 CurrentDirectory
.Buffer
,
1580 lpProcessInformation
,
1584 RtlFreeUnicodeString(&ApplicationName
);
1585 RtlFreeUnicodeString(&LiveCommandLine
);
1586 RtlFreeUnicodeString(&CurrentDirectory
);
1587 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo
.lpDesktop
);
1588 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo
.lpReserved
);
1589 RtlFreeHeap(RtlGetProcessHeap(), 0, StartupInfo
.lpTitle
);
1591 /* Return what Unicode did */
1596 * FUNCTION: The CreateProcess function creates a new process and its
1597 * primary thread. The new process executes the specified executable file
1600 * lpApplicationName = Pointer to name of executable module
1601 * lpCommandLine = Pointer to command line string
1602 * lpProcessAttributes = Process security attributes
1603 * lpThreadAttributes = Thread security attributes
1604 * bInheritHandles = Handle inheritance flag
1605 * dwCreationFlags = Creation flags
1606 * lpEnvironment = Pointer to new environment block
1607 * lpCurrentDirectory = Pointer to current directory name
1608 * lpStartupInfo = Pointer to startup info
1609 * lpProcessInformation = Pointer to process information
1615 CreateProcessA(LPCSTR lpApplicationName
,
1616 LPSTR lpCommandLine
,
1617 LPSECURITY_ATTRIBUTES lpProcessAttributes
,
1618 LPSECURITY_ATTRIBUTES lpThreadAttributes
,
1619 BOOL bInheritHandles
,
1620 DWORD dwCreationFlags
,
1621 LPVOID lpEnvironment
,
1622 LPCSTR lpCurrentDirectory
,
1623 LPSTARTUPINFOA lpStartupInfo
,
1624 LPPROCESS_INFORMATION lpProcessInformation
)
1626 /* Call the internal (but exported) version */
1627 return CreateProcessInternalA(0,
1630 lpProcessAttributes
,
1637 lpProcessInformation
,