CreateProcessA 函数 (processthreadsapi.h)
创建新进程及其主线程。 新进程在调用进程的安全上下文中运行。
如果调用进程正在模拟其他用户,则新进程将令牌用于调用进程,而不是模拟令牌。 若要在模拟令牌表示的用户的安全上下文中运行新进程,请使用 CreateProcessAsUser 或 CreateProcessWithLogonW 函数。
语法
BOOL CreateProcessA(
[in, optional] LPCSTR lpApplicationName,
[in, out, optional] LPSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
参数
[in, optional] lpApplicationName
要执行的模块的名称。 此模块可以是基于 Windows 的应用程序。 它可以是某种其他类型的模块 (例如 MS-DOS 或 OS/2) (如果本地计算机上提供了相应的子系统)。
字符串可以指定要执行的模块的完整路径和文件名,也可以指定部分名称。 对于部分名称,函数使用当前驱动器和当前目录来完成规范。 函数不会使用搜索路径。 此参数必须包含文件扩展名;不采用默认扩展名。
lpApplicationName 参数可以为 NULL。 在这种情况下,模块名称必须是 lpCommandLine 字符串中第一个空格分隔的标记。 如果使用包含空格的长文件名,请使用带引号的字符串来指示文件名结束和参数开始的位置;否则,文件名不明确。 例如,请考虑字符串“c:\program files\sub dir\program name”。 可以通过多种方式解释此字符串。 系统尝试按以下顺序解释可能性:
- c:\program.exe
- c:\program files\sub.exe
- c:\program files\sub dir\program.exe
- c:\program files\sub dir\program name.exe
如果可执行模块是 16 位应用程序, lpApplicationName 应为 NULL, lpCommandLine 指向的字符串应指定可执行模块及其参数。
若要运行批处理文件,必须启动命令解释器;将 lpApplicationName 设置为 cmd.exe并将 lpCommandLine 设置为以下参数:/c 加上批处理文件的名称。
[in, out, optional] lpCommandLine
要执行的命令行。
此字符串的最大长度为 32,767 个字符,包括 Unicode 终止 null 字符。 如果 lpApplicationName 为 NULL,则 lpCommandLine 的模块名称部分限制为 MAX_PATH 个字符。
此函数的 Unicode 版本 CreateProcessW 可以修改此字符串的内容。 因此,此参数不能是指向只读内存 (的指针,例如 const 变量或文本字符串) 。 如果此参数是常量字符串,该函数可能会导致访问冲突。
lpCommandLine 参数可以为 NULL。 在这种情况下,函数使用 lpApplicationName 指向的字符串作为命令行。
如果 lpApplicationName 和 lpCommandLine 均为非 NULL,则 lpApplicationName 指向的以 null 结尾的字符串将指定要执行的模块, 而 lpCommandLine 指向的以 null 结尾的字符串将指定命令行。 新进程可以使用 GetCommandLine 检索整个命令行。 用 C 编写的控制台进程可以使用 argc 和 argv 参数来分析命令行。 由于 argv[0] 是模块名称,因此 C 程序员通常将模块名称重复为命令行中的第一个标记。
如果 lpApplicationName 为 NULL,则命令行的第一个空格分隔标记将指定模块名称。 如果使用包含空格的长文件名,请使用带引号的字符串来指示文件名结束和参数开始的位置 (请参阅 lpApplicationName 参数) 的说明。 如果文件名不包含扩展名,则追加.exe。 因此,如果文件扩展名为 .com,则此参数必须包含 .com 扩展名。 如果文件名以不带扩展名的句点 (.) 结尾,或者文件名包含路径,则不会追加.exe。 如果文件名不包含目录路径,系统会按以下顺序搜索可执行文件:
- 从中加载应用程序的目录。
- 父进程的当前目录。
- 32 位 Windows 系统目录。 使用 GetSystemDirectory 函数获取此目录的路径。
- 16 位 Windows 系统目录。 没有获取此目录的路径的函数,但会对其进行搜索。 此目录的名称为 System。
- Windows 目录。 使用 GetWindowsDirectory 函数获取此目录的路径。
- PATH 环境变量中列出的目录。 请注意,此函数不会搜索应用程序路径注册表项指定的每个 应用程序 路径。 若要在搜索序列中包含此每个应用程序的路径,请使用 ShellExecute 函数。
[in, optional] lpProcessAttributes
指向 SECURITY_ATTRIBUTES 结构的指针,该结构确定返回的新进程对象的句柄是否可以由子进程继承。 如果 lpProcessAttributes 为 NULL,则不能继承句柄。
结构的 lpSecurityDescriptor 成员为新进程指定安全描述符。 如果 lpProcessAttributes 为 NULL 或 lpSecurityDescriptor 为 NULL,则进程将获取默认安全描述符。 进程的默认安全描述符中的 ACL 来自创建者的主令牌。Windowsxp: 进程的默认安全描述符中的 ACL 来自创建者的主要令牌或模拟令牌。 此行为随 Windows XP SP2 和 Windows Server 2003 更改。
[in, optional] lpThreadAttributes
指向 SECURITY_ATTRIBUTES 结构的指针,该结构确定返回的新线程对象的句柄是否可以由子进程继承。 如果 lpThreadAttributes 为 NULL,则不能继承句柄。
结构的 lpSecurityDescriptor 成员指定主线程的安全描述符。 如果 lpThreadAttributes 为 NULL 或 lpSecurityDescriptor 为 NULL,则线程获取默认安全描述符。 线程的默认安全描述符中的 ACL 来自进程令牌。Windowsxp: 线程的默认安全描述符中的 ACL 来自创建者的主令牌或模拟令牌。 此行为随 Windows XP SP2 和 Windows Server 2003 更改。
[in] bInheritHandles
如果此参数为 TRUE,则调用进程中的每个可继承句柄都由新进程继承。 如果参数为 FALSE,则不继承句柄。 请注意,继承的句柄与原始句柄具有相同的值和访问权限。 有关可继承句柄的其他讨论,请参阅备注。
终端服务: 不能跨会话继承句柄。 此外,如果此参数为 TRUE,则必须在与调用方相同的会话中创建进程。
受保护的流程灯 (PPL) 进程: 当 PPL 进程创建非 PPL 进程时,将阻止泛型句柄继承,因为不允许将PROCESS_DUP_HANDLE从非 PPL 进程转换为 PPL 进程。 请参阅 进程安全性和访问权限
[in] dwCreationFlags
控制优先级类和进程的创建的标志。 有关值的列表,请参阅 进程创建标志。
此参数还控制新进程的优先级类,该类用于确定进程线程的计划优先级。 有关值的列表,请参阅 GetPriorityClass。 如果未指定任何优先级类标志,则优先级类默认为 NORMAL_PRIORITY_CLASS ,除非创建过程的优先级类 IDLE_PRIORITY_CLASS 或 BELOW_NORMAL_PRIORITY_CLASS。 在这种情况下,子进程接收调用进程的默认优先级类。
如果 dwCreationFlags 参数的值为 0:
- 进程同时继承调用方和父级控制台的错误模式。
- 假定新进程的环境块包含 ANSI 字符 (请参阅 lpEnvironment 参数以获取) 的其他信息。
- 基于 16 位 Windows 的应用程序在共享的虚拟 DOS 计算机中运行, (VDM) 。
[in, optional] lpEnvironment
指向新进程的环境块的指针。 如果此参数为 NULL,则新进程使用调用进程的 环境。
环境块由以 null 结尾的字符串的以 null 结尾的块组成。 每个字符串采用以下格式:
名字=value\0
由于等号用作分隔符,因此不得在环境变量的名称中使用。
环境块可以包含 Unicode 或 ANSI 字符。 如果 lpEnvironment 指向的环境块包含 Unicode 字符,请确保 dwCreationFlags 包含 CREATE_UNICODE_ENVIRONMENT。
如果进程的环境块的总大小超过 32,767 个字符,则此函数的 ANSI 版本 CreateProcessA 将失败。
请注意,ANSI 环境块以两个零字节结尾:一个字节用于最后一个字符串,另一个用于终止该块。 Unicode 环境块以四个零字节结尾:两个作为最后一个字符串,另外两个用于终止该块。
[in, optional] lpCurrentDirectory
进程当前目录的完整路径。 字符串还可以指定 UNC 路径。
如果此参数为 NULL,则新进程将具有与调用进程相同的当前驱动器和目录。 (此功能主要用于需要启动应用程序并指定其初始驱动器和工作目录的 shell。)
[in] lpStartupInfo
指向 STARTUPINFO 或 STARTUPINFOEX 结构的指针。
若要设置扩展属性,请使用 STARTUPINFOEX 结构并在 dwCreationFlags 参数中指定EXTENDED_STARTUPINFO_PRESENT。
当不再需要 STARTUPINFO 或 STARTUPINFOEX 中的句柄时,必须使用 CloseHandle 关闭它们。
[out] lpProcessInformation
指向 PROCESS_INFORMATION 结构的指针,该结构接收有关新进程的标识信息。
当不再需要 PROCESS_INFORMATION 中的句柄时,必须使用 CloseHandle 将其关闭。
返回值
如果该函数成功,则返回值为非零值。
如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。
请注意,函数在进程完成初始化之前返回 。 如果找不到所需的 DLL 或无法初始化,则进程将终止。 若要获取进程的终止状态,请调用 GetExitCodeProcess。
注解
为进程分配一个进程标识符。 标识符在进程终止之前有效。 它可用于标识进程,或在 OpenProcess 函数中指定以打开进程的句柄。 进程中的初始线程也分配有一个线程标识符。 可以在 OpenThread 函数中指定它以打开线程的句柄。 标识符在线程终止之前有效,可用于唯一标识系统中的线程。 这些标识符在 PROCESS_INFORMATION 结构中返回。
操作系统提供给进程的命令行中可执行文件的名称不一定与调用进程提供给 CreateProcess 函数的命令行中的名称相同。 操作系统可能会在未提供完全限定路径的情况下提供的可执行文件名称前面附加一个完全限定的路径。
调用线程可以使用 WaitForInputIdle 函数等待,直到新进程完成初始化,并且正在等待用户输入,而没有挂起的输入。 这对于父进程和子进程之间的同步非常有用,因为 CreateProcess 返回时不会等待新进程完成初始化。 例如,创建进程会在尝试查找与新进程关联的窗口之前使用 WaitForInputIdle 。
关闭进程的首选方法是使用 ExitProcess 函数,因为此函数会向附加到进程的所有 DLL 发送即将终止的通知。 关闭进程的其他方式不会通知附加的 DLL。 请注意,当线程调用 ExitProcess 时,进程的其他线程将终止,没有机会执行任何其他代码 (包括附加 DLL 的线程终止代码) 。 有关详细信息,请参阅 终止进程。
父进程可以在创建进程期间直接更改子进程的环境变量。 这是一个进程可以直接更改另一个进程的环境设置的唯一情况。 有关详细信息,请参阅 更改环境变量。
如果应用程序提供环境块,系统驱动器的当前目录信息不会自动传播到新进程。 例如,有一个名为 =C 的环境变量,其值为驱动器 C 上的当前目录。应用程序必须手动将当前目录信息传递到新进程。 为此,应用程序必须显式创建这些环境变量字符串, (按字母顺序对它们进行排序,因为系统使用排序的环境) ,并将其放入环境块中。 通常,由于环境块排序顺序,它们将位于环境块的前面。
获取驱动器 X 的当前目录信息的一种方法是进行以下调用: GetFullPathName("X:", ...)
。 这避免了应用程序必须扫描环境块。 如果返回的完整路径为 X:,则无需将该值作为环境数据传递,因为根目录是新进程的驱动器 X 的默认当前目录。
使用指定 CREATE_NEW_PROCESS_GROUP 创建进程时,将代表新进程对 SetConsoleCtrlHandler 进行隐式调用, (NULL,TRUE) ;这意味着新进程已禁用 CTRL+C。 这允许 shell 自行处理 CTRL+C,并有选择地将该信号传递到子进程。 CTRL+BREAK 未禁用,可用于中断进程/进程组。
默认情况下,将 TRUE 作为 bInheritHandles 参数的值传递会导致新进程继承所有可继承的句柄。 如果应用程序同时从多个线程创建进程,但希望每个进程继承不同的句柄,这可能会有问题。 应用程序可以使用 UpdateProcThreadAttributeList 函数和 PROC_THREAD_ATTRIBUTE_HANDLE_LIST 参数来提供特定进程要继承的句柄列表。
安全备注
第一个参数 lpApplicationName 可以为 NULL,在这种情况下,可执行文件名称必须位于 lpCommandLine 指向的空格分隔字符串中。 如果可执行文件或路径名称中有一个空格,则存在运行其他可执行文件的风险,因为函数分析空格的方式。 以下示例很危险,因为函数将尝试运行“Program.exe”(如果存在),而不是“MyApp.exe”。 LPTSTR szCmdline = _tcsdup(TEXT("C:\\Program Files\\MyApp -L -S"));
CreateProcess(NULL, szCmdline, /* ... */);
如果恶意用户在系统上创建名为“Program.exe”的应用程序,则使用 Program Files 目录错误调用 CreateProcess 的任何程序都将运行此应用程序,而不是预期的应用程序。
若要避免此问题,请不要为 lpApplicationName 传递 NULL。 如果确实为 lpApplicationName 传递 NULL,请在 lpCommandLine 中的可执行路径周围使用引号,如以下示例所示。
LPTSTR szCmdline[] = _tcsdup(TEXT("\"C:\\Program Files\\MyApp\" -L -S"));
CreateProcess(NULL, szCmdline, /*...*/);
示例
有关示例,请参阅 创建进程。
注意
processthreadsapi.h 标头将 CreateProcess 定义为别名,该别名根据 UNICODE 预处理器常量的定义自动选择此函数的 ANSI 或 Unicode 版本。 将非特定编码别名的使用与非非特定编码的代码混合使用可能会导致不匹配,从而导致编译或运行时错误。 有关详细信息,请参阅 函数原型的约定。
要求
最低受支持的客户端 | Windows XP [桌面应用 |UWP 应用] |
最低受支持的服务器 | Windows Server 2003 [桌面应用 |UWP 应用] |
目标平台 | Windows |
标头 | processthreadsapi.h (包括 Windows Server 2003 上的 Windows.h、Windows Vista、Windows 7、Windows Server 2008 Windows Server 2008 R2) |
Library | Kernel32.lib |
DLL | Kernel32.dll |