CreateProcessWithTokenW 函数 (winbase.h)
创建新进程及其主线程。 新进程在指定令牌的安全上下文中运行。 它可以选择性地加载指定用户的用户配置文件。
调用 CreateProcessWithTokenW 的进程必须具有SE_IMPERSONATE_NAME特权。 如果此函数失败并出现 ERROR_PRIVILEGE_NOT_HELD (1314) ,请改用 CreateProcessAsUser 或 CreateProcessWithLogonW 函数。 通常,调用的进程
CreateProcessAsUser 必须具有SE_INCREASE_QUOTA_NAME特权,并且如果令牌不可分配,则可能需要SE_ASSIGNPRIMARYTOKEN_NAME特权。 CreateProcessWithLogonW 不需要特殊权限,但必须允许指定的用户帐户以交互方式登录。 通常,最好使用 CreateProcessWithLogonW 创建具有备用凭据的进程。
语法
BOOL CreateProcessWithTokenW(
[in] HANDLE hToken,
[in] DWORD dwLogonFlags,
[in, optional] LPCWSTR lpApplicationName,
[in, out, optional] LPWSTR lpCommandLine,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCWSTR lpCurrentDirectory,
[in] LPSTARTUPINFOW lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
参数
[in] hToken
表示用户的主令牌的句柄。 句柄必须具有TOKEN_QUERY、TOKEN_DUPLICATE和TOKEN_ASSIGN_PRIMARY访问权限。 有关详细信息,请参阅 Access-Token 对象的访问权限。 令牌表示的用户必须对 lpApplicationName 或 lpCommandLine 参数指定的应用程序具有读取和执行访问权限。
若要获取表示指定用户的主令牌,请调用 LogonUser 函数。 或者,可以调用 DuplicateTokenEx 函数,将模拟令牌转换为主令牌。 这允许模拟客户端的服务器应用程序创建具有客户端安全上下文的进程。
终端服务: 调用方的进程始终在调用方会话中运行,而不是在令牌中指定的会话中运行。 若要在令牌中指定的会话中运行进程,请使用 CreateProcessAsUser 函数。
[in] dwLogonFlags
登录选项。 此参数可以是零或以下值之一。
[in, optional] lpApplicationName
要执行的模块的名称。 此模块可以是基于 Windows 的应用程序。 它可以是某种其他类型的模块 (例如 MS-DOS 或 OS/2) (如果本地计算机上提供了相应的子系统)。
字符串可以指定要执行的模块的完整路径和文件名,也可以指定部分名称。 对于部分名称,函数使用当前驱动器和当前目录来完成规范。 函数不会使用搜索路径。 此参数必须包含文件扩展名;不采用默认扩展名。
lpApplicationName 参数可以为 NULL。 在这种情况下,模块名称必须是 lpCommandLine 字符串中第一个空格分隔的标记。 如果使用包含空格的长文件名,请使用带引号的字符串来指示文件名结束和参数开始的位置;否则,文件名不明确。 例如,请考虑字符串“c:\program files\sub dir\program name”。 可以通过多种方式解释此字符串。 系统尝试按以下顺序解释可能性:
c:\program.exec:\program files\sub.exec:\program files\sub dir\program.exec:\program files\sub dir\program name.exe 如果可执行模块是 16 位应用程序, lpApplicationName 应为 NULL,并且 lpCommandLine 指向的字符串应指定可执行模块及其参数。
[in, out, optional] lpCommandLine
要执行的命令行。
此字符串的最大长度为 1024 个字符。 如果 lpApplicationName 为 NULL,则 lpCommandLine 的模块名称部分限制为MAX_PATH个字符。
函数可以修改此字符串的内容。 因此,此参数不能是指向只读内存 (的指针,例如 const 变量或文本字符串) 。 如果此参数是常量字符串,该函数可能会导致访问冲突。
lpCommandLine 参数可以为 NULL。 在这种情况下,函数使用 lpApplicationName 指向的字符串作为命令行。
如果 lpApplicationName 和 lpCommandLine 均为非 NULL,则 *lpApplicationName 指定要执行的模块,*lpCommandLine 指定命令行。 新进程可以使用 GetCommandLine 检索整个命令行。 用 C 编写的控制台进程可以使用 argc 和 argv 参数来分析命令行。 由于 argv[0] 是模块名称,因此 C 程序员通常将模块名称重复为命令行中的第一个标记。
如果 lpApplicationName 为 NULL,则命令行的第一个空格分隔标记将指定模块名称。 如果使用包含空格的长文件名,请使用带引号的字符串来指示文件名结束和参数开始的位置 (请参阅 lpApplicationName 参数) 的说明。 如果文件名不包含扩展名,则追加 .exe。 因此,如果文件扩展名.com,则此参数必须包含.com扩展名。 如果文件名以不带扩展名的句点 (.) 结尾,或者文件名包含路径,则不会追加 .exe。 如果文件名不包含目录路径,系统会按以下顺序搜索可执行文件:
- 从中加载应用程序的目录。
- 父进程的当前目录。
- 32 位 Windows 系统目录。 使用 GetSystemDirectory 函数获取此目录的路径。
- 16 位 Windows 系统目录。 没有获取此目录的路径的函数,但会对其进行搜索。
- Windows 目录。 使用 GetWindowsDirectory 函数获取此目录的路径。
- PATH 环境变量中列出的目录。 请注意,此函数不会搜索由 应用路径 注册表项指定的每个应用程序路径。 若要在搜索序列中包含此每个应用程序的路径,请使用 ShellExecute 函数。
[in] dwCreationFlags
控制如何创建进程的标志。 默认情况下,CREATE_DEFAULT_ERROR_MODE、CREATE_NEW_CONSOLE和CREATE_NEW_PROCESS_GROUP标志处于启用状态。 有关值的列表,请参阅 进程创建标志。
此参数还控制新进程的优先级类,该类用于确定进程线程的计划优先级。 有关值的列表,请参阅 GetPriorityClass。 如果未指定任何优先级类标志,则优先级类默认为 NORMAL_PRIORITY_CLASS,除非创建过程的优先级类IDLE_PRIORITY_CLASS或BELOW_NORMAL_PRIORITY_CLASS。 在这种情况下,子进程接收调用进程的默认优先级类。
如果 dwCreationFlags 参数的值为 0:
- 进程获取默认错误模式,创建新的控制台并创建新的进程组。
- 假定新进程的环境块包含 ANSI 字符 (请参阅 lpEnvironment 参数以获取) 的其他信息。
- 基于 16 位 Windows 的应用程序 (VDM) 在共享虚拟 DOS 计算机中运行。
[in, optional] lpEnvironment
指向新进程的环境块的指针。 如果此参数为 NULL,则新进程使用根据 lpUsername 指定的用户的配置文件创建的环境。
环境块由以 null 结尾的字符串组成的以 null 结尾的块组成。 每个字符串采用以下形式:
名字=价值
由于等号 (=) 用作分隔符,因此不得将其用于环境变量的名称中。
环境块可以包含 Unicode 或 ANSI 字符。 如果 lpEnvironment 指向的环境块包含 Unicode 字符,请确保 dwCreationFlags 包含CREATE_UNICODE_ENVIRONMENT。
ANSI 环境块以两个零字节结尾:一个字节用于最后一个字符串,再一个用于终止该块。 Unicode 环境块以 4 个零字节结尾:两个字节作为最后一个字符串,另外两个用于终止该块。
若要检索特定用户的环境块的副本,请使用 CreateEnvironmentBlock 函数。
[in, optional] lpCurrentDirectory
进程当前目录的完整路径。 字符串还可以指定 UNC 路径。
如果此参数为 NULL,则新进程将采用与调用方进程相同的当前驱动器和目录。 (此功能主要用于需要启动应用程序并指定其初始驱动器和工作目录的 shell。)
[in] lpStartupInfo
指向 STARTUPINFO 或 STARTUPINFOEX 结构的指针。
如果 lpDesktop 成员为 NULL 或空字符串,则新进程将继承其父进程的桌面和窗口工作站。 函数将指定用户帐户的权限添加到继承的窗口工作站和桌面。 否则,如果此成员指定桌面,则应用程序负责将指定用户帐户的权限添加到指定的窗口工作站和桌面,即使对于 WinSta0\Default 也是如此。
当不再需要 STARTUPINFO 或 STARTUPINFOEX 中的句柄时,必须使用 CloseHandle 关闭它们。
[out] lpProcessInformation
指向 PROCESS_INFORMATION 结构的指针,该结构接收新进程的标识信息,包括进程的句柄。
不再需要时,必须使用CloseHandle 函数关闭PROCESS_INFORMATION中的句柄。
返回值
如果该函数成功,则返回值为非零值。
如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。
请注意,函数在进程完成初始化之前返回 。 如果找不到所需的 DLL 或无法初始化,则进程将终止。 若要获取进程的终止状态,请调用 GetExitCodeProcess。
注解
默认情况下, CreateProcessWithTokenW 不会将指定用户的配置文件加载到 HKEY_USERS 注册表项中。 这意味着访问 HKEY_CURRENT_USER 注册表项中的信息可能不会生成与正常交互式登录一致的结果。 你有责任通过使用 LOGON_WITH_PROFILE 或在调用此函数之前调用 LoadUserProfile 函数将用户的注册表配置单元加载到 HKEY_USERS。
如果 lpEnvironment 参数为 NULL,则新进程使用根据 lpUserName 指定的用户的配置文件创建的环境块。 如果未设置 HOMEDRIVE 和 HOMEPATH 变量, CreateProcessWithTokenW 将修改环境块,以使用用户工作目录的驱动器和路径。
创建新进程和线程句柄后, (PROCESS_ALL_ACCESS 和THREAD_ALL_ACCESS) 接收完全访问权限。 对于任一句柄,如果未提供安全描述符,则可以在需要该类型对象句柄的任何函数中使用句柄。 提供安全描述符时,在授予访问权限之前,对句柄的所有后续使用执行访问检查。 如果拒绝访问,则请求进程无法使用句柄获取对进程或线程的访问权限。
若要检索安全令牌,请将 PROCESS_INFORMATION 结构中的进程句柄传递给 OpenProcessToken 函数。
为进程分配一个进程标识符。 标识符在进程终止之前有效。 它可用于标识进程,或在 OpenProcess 函数中指定以打开进程的句柄。 进程中的初始线程也分配有一个线程标识符。 可以在 OpenThread 函数中指定它以打开线程的句柄。 标识符在线程终止之前有效,可用于唯一标识系统中的线程。 这些标识符在 PROCESS_INFORMATION 中返回。
调用线程可以使用 WaitForInputIdle 函数等待,直到新进程完成初始化,并且正在等待用户输入,而没有挂起的输入。 这对于父进程与子进程之间的同步非常有用,因为 CreateProcessWithTokenW 返回时无需等待新进程完成初始化。 例如,创建进程会在尝试查找与新进程关联的窗口之前使用 WaitForInputIdle 。
关闭进程的首选方法是使用 ExitProcess 函数,因为此函数会向附加到进程的所有 DLL 发送即将终止的通知。 关闭进程的其他方式不会通知附加的 DLL。 请注意,当线程调用 ExitProcess 时,进程的其他线程将终止,没有机会执行任何其他代码 (包括附加 DLL 的线程终止代码) 。 有关详细信息,请参阅 终止进程。
若要编译使用此函数的应用程序,请将_WIN32_WINNT定义为 0x0500 或更高版本。 有关详细信息,请参阅 使用 Windows 标头。
安全备注
lpApplicationName 参数可以为 NULL,在这种情况下,可执行文件名称必须是 lpCommandLine 中第一个空格分隔的字符串。 如果可执行文件或路径名称中有一个空格,则存在运行其他可执行文件的风险,因为函数分析空格的方式。 以下示例很危险,因为函数将尝试运行“Program.exe”(如果存在),而不是“MyApp.exe”。 LPTSTR szCmdline = L"C:\\Program Files\\MyApp";
CreateProcessWithTokenW(/*...*/, szCmdline, /*...*/);
如果恶意用户在系统上创建名为“Program.exe”的应用程序,则使用 Program Files 目录错误地调用 CreateProcessWithTokenW 的任何程序都将运行此应用程序,而不是预期的应用程序。
若要避免此问题,请不要为 lpApplicationName 传递 NULL。 如果确实为 lpApplicationName 传递 NULL,请在 lpCommandLine 中的可执行路径周围使用引号,如以下示例所示。
LPTSTR szCmdline = L"\"C:\\Program Files\\MyApp\"";
CreateProcessWithTokenW(/*...*/, szCmdline, /*...*/);
要求
要求 | 值 |
---|---|
最低受支持的客户端 | Windows Vista [仅限桌面应用] |
最低受支持的服务器 | Windows Server 2003 [仅限桌面应用] |
目标平台 | Windows |
标头 | winbase.h (包括 Windows.h) |
Library | Advapi32.lib |
DLL | Advapi32.dll |