CreateProcessW 函数 (processthreadsapi.h)

创建新进程及其主线程。 新进程在调用进程的安全上下文中运行。

如果调用进程正在模拟其他用户,则新进程将令牌用于调用进程,而不是模拟令牌。 若要在模拟令牌表示的用户的安全上下文中运行新进程,请使用 CreateProcessAsUserCreateProcessWithLogonW 函数。

语法

BOOL CreateProcessW(
  [in, optional]      LPCWSTR               lpApplicationName,
  [in, out, optional] LPWSTR                lpCommandLine,
  [in, optional]      LPSECURITY_ATTRIBUTES lpProcessAttributes,
  [in, optional]      LPSECURITY_ATTRIBUTES lpThreadAttributes,
  [in]                BOOL                  bInheritHandles,
  [in]                DWORD                 dwCreationFlags,
  [in, optional]      LPVOID                lpEnvironment,
  [in, optional]      LPCWSTR               lpCurrentDirectory,
  [in]                LPSTARTUPINFOW        lpStartupInfo,
  [out]               LPPROCESS_INFORMATION lpProcessInformation
);

参数

[in, optional] lpApplicationName

要执行的模块的名称。 此模块可以是基于 Windows 的应用程序。 它可以是某种其他类型的模块 (例如 MS-DOS 或 OS/2) (如果本地计算机上提供了相应的子系统)。

字符串可以指定要执行的模块的完整路径和文件名,也可以指定部分名称。 对于部分名称,函数使用当前驱动器和当前目录来完成规范。 函数不会使用搜索路径。 此参数必须包含文件扩展名;不假定默认扩展。

lpApplicationName 参数可以为 NULL。 在这种情况下,模块名称必须是 lpCommandLine 字符串中第一个空格分隔的标记。 如果使用包含空格的长文件名,请使用带引号的字符串来指示文件名结束和参数开始的位置;否则,文件名不明确。 例如,请考虑字符串“c:\program files\sub dir\program name”。 可以通过多种方式解释此字符串。 系统尝试按以下顺序解释可能性:

  1. c:\program.exe
  2. c:\program files\sub.exe
  3. c:\program files\sub dir\program.exe
  4. 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 字符。 如果 lpApplicationNameNULL,则 lpCommandLine 的模块名称部分限制为 MAX_PATH 个字符。

此函数的 Unicode 版本 CreateProcessW 可以修改此字符串的内容。 因此,此参数不能是指向只读内存 (的指针,例如 const 变量或文本字符串) 。 如果此参数是常量字符串,则函数可能会导致访问冲突。

lpCommandLine 参数可以为 NULL。 在这种情况下,函数使用 lpApplicationName 指向的字符串作为命令行。

如果 lpApplicationNamelpCommandLine 都是非 NULL 的,则 lpApplicationName 指向的以 null 结尾的字符串将指定要执行的模块, lpCommandLine 指向的以 null 结尾的字符串指定命令行。 新进程可以使用 GetCommandLine 检索整个命令行。 用 C 编写的控制台进程可以使用 argcargv 参数来分析命令行。 由于 argv[0] 是模块名称,因此 C 程序员通常会将模块名称重复为命令行中的第一个标记。

如果 lpApplicationName 为 NULL,则命令行的第一个空格分隔标记将指定模块名称。 如果使用包含空格的长文件名,请使用带引号的字符串来指示文件名结束的位置和参数开始的位置 (请参阅 ) lpApplicationName 参数的说明。 如果文件名不包含扩展名,则追加 .exe。 因此,如果文件扩展名为 .com,则此参数必须包含 .com 扩展名。 如果文件名以没有扩展名的句点 (.) 结尾,或者文件名包含路径,则不追加 .exe。 如果文件名不包含目录路径,系统会按以下顺序搜索可执行文件:

  1. 从中加载应用程序的目录。
  2. 父进程的当前目录。
  3. 32 位 Windows 系统目录。 使用 GetSystemDirectory 函数获取此目录的路径。
  4. 16 位 Windows 系统目录。 没有获取此目录路径的函数,但会对其进行搜索。 此目录的名称为 System。
  5. Windows 目录。 使用 GetWindowsDirectory 函数获取此目录的路径。
  6. PATH 环境变量中列出的目录。 请注意,此函数不会搜索由应用路径注册表项指定的每个 应用程序路径 。 若要在搜索序列中包含此每个应用程序的路径,请使用 ShellExecute 函数。
系统将终止 null 字符添加到命令行字符串,以将文件名与参数分开。 这会将原始字符串划分为两个字符串以供内部处理。

[in, optional] lpProcessAttributes

指向 SECURITY_ATTRIBUTES 结构的指针,该结构确定子进程是否可以继承新进程对象的返回句柄。 如果 lpProcessAttributesNULL,则无法继承句柄。

结构的 lpSecurityDescriptor 成员为新进程指定安全描述符。 如果 lpProcessAttributes 为 NULL 或 lpSecurityDescriptorNULL,则进程将获取默认的安全描述符。 进程的默认安全描述符中的 ACL 来自创建者的主令牌。Windowsxp: 进程的默认安全描述符中的 ACL 来自创建者的主要令牌或模拟令牌。 此行为随 WINDOWS XP SP2 和 Windows Server 2003 而更改。

[in, optional] lpThreadAttributes

指向 SECURITY_ATTRIBUTES 结构的指针,该结构确定子进程是否可以继承新线程对象的返回句柄。 如果 lpThreadAttributes 为 NULL,则无法继承句柄。

结构的 lpSecurityDescriptor 成员指定main线程的安全描述符。 如果 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_CLASSBELOW_NORMAL_PRIORITY_CLASS。 在这种情况下,子进程接收调用进程的默认优先级类。

如果 dwCreationFlags 参数的值为 0:

  • 进程同时继承调用方和父级控制台的错误模式。
  • 假定新进程的环境块包含 ANSI 字符, (请参阅 lpEnvironment 参数,了解) 的其他信息。
  • 基于 16 位 Windows 的应用程序 (VDM) 在共享的虚拟 DOS 计算机中运行。

[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

指向 STARTUPINFOSTARTUPINFOEX 结构的指针。

若要设置扩展属性,请使用 STARTUPINFOEX 结构,并在 dwCreationFlags 参数中指定EXTENDED_STARTUPINFO_PRESENT。

当不再需要 STARTUPINFOSTARTUPINFOEX 中的句柄时,必须使用 CloseHandle 关闭它们。

重要 调用方负责确保 STARTUPINFO 中的标准句柄字段包含有效的句柄值。 即使 dwFlags 成员指定了STARTF_USESTDHANDLES,这些字段也会保持不变地复制到子进程 ,而无需验证。 不正确的值可能会导致子进程出现错误或崩溃。 使用 应用程序验证程序 运行时验证工具检测无效句柄。
 

[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 Vista、Windows 7、Windows Server 2008 Windows Server 2008 R2)
Library Kernel32.lib
DLL Kernel32.dll

另请参阅

CloseHandle

ShellExecuteW

CreateProcessAsUser

CreateProcessWithLogonW

ExitProcess

GetCommandLine

GetEnvironmentStrings

GetExitCodeProcess

GetFullPathName

GetStartupInfo

OpenProcess

PROCESS_INFORMATION

进程和线程函数

进程

SECURITY_ATTRIBUTES

STARTUPINFO

STARTUPINFOEX

SetErrorMode

TerminateProcess

WaitForInputIdle