CreateProcessWithLogonW 函数 (winbase.h)

创建新进程及其主线程。 然后,新进程在指定凭据的安全上下文中运行指定的可执行文件, (用户、域和密码) 。 可以选择加载指定用户的用户配置文件。

此函数类似于 CreateProcessAsUserCreateProcessWithTokenW 函数,只是调用方不需要调用 LogonUser 函数即可对用户进行身份验证并获取令牌。

语法

BOOL CreateProcessWithLogonW(
  [in]                LPCWSTR               lpUsername,
  [in, optional]      LPCWSTR               lpDomain,
  [in]                LPCWSTR               lpPassword,
  [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] lpUsername

用户的名称。 这是要登录的用户帐户的名称。 如果使用 UPN 格式、 用户@DNS_domain_name则 lpDomain 参数必须为 NULL。

用户帐户必须具有本地计算机上的“本地登录”权限。 此权限授予工作站和服务器上的所有用户,但仅授予域控制器上的管理员。

[in, optional] lpDomain

其帐户数据库包含 lpUsername 帐户的域或服务器的名称。 如果此参数为 NULL,则必须以 UPN 格式指定用户名。

[in] lpPassword

lpUsername 帐户的明文密码。

[in] dwLogonFlags

登录选项。 此参数可以为 0 (零) 或以下值之一。

含义
LOGON_WITH_PROFILE
0x00000001
登录,然后在 HKEY_USERS 注册表项中加载用户配置文件。 函数在加载配置文件后返回。 加载配置文件可能很耗时,因此最好仅在必须访问 HKEY_CURRENT_USER 注册表项中的信息时才使用此值。

Windows Server 2003: 在新进程终止后,无论它是否创建了子进程,配置文件都将被卸载。

Windowsxp: 新进程和它创建的所有子进程终止后,将卸载配置文件。

LOGON_NETCREDENTIALS_ONLY
0x00000002
登录,但仅在网络上使用指定的凭据。 新进程使用与调用方相同的令牌,但系统会在 LSA 中创建新的登录会话,并且进程使用指定的凭据作为默认凭据。

此值可用于创建一个进程,该进程在本地使用一组与远程凭据不同的凭据。 这在没有信任关系的域间方案中很有用。

系统不验证指定的凭据。 因此,进程可以启动,但它可能无权访问网络资源。

[in, optional] lpApplicationName

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

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

lpApplicationName 参数可以为 NULL,模块名称必须是 lpCommandLine 字符串中第一个空格分隔的标记。 如果使用包含空格的长文件名,请使用带引号的字符串来指示文件名结束和参数开始的位置;否则,文件名不明确。

例如,可以通过不同的方式解释以下字符串:

“c:\program files\sub dir\program name”

系统尝试按以下顺序解释可能性:

  1. c:\program.exe files\sub dir\program name
  2. c:\program files\sub.exe dir\program name
  3. c:\program files\sub dir\program.exe name
  4. c:\program files\sub dir\program name.exe

如果可执行模块是 16 位应用程序, lpApplicationName 应为 NULL, 并且 lpCommandLine 指向的字符串应指定可执行模块及其参数。

[in, out, optional] lpCommandLine

要执行的命令行。 此字符串的最大长度为 1024 个字符。 如果 lpApplicationNameNULL,则 lpCommandLine 的模块名称部分限制为 MAX_PATH 个字符。

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

lpCommandLine 参数可以为 NULL,函数使用 lpApplicationName 指向的字符串作为命令行。

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

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

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

[in] dwCreationFlags

控制如何创建进程的标志。 默认情况下, CREATE_DEFAULT_ERROR_MODECREATE_NEW_CONSOLECREATE_NEW_PROCESS_GROUP 标志处于启用状态。 有关值的列表,请参阅 进程创建标志

此参数还控制新进程的优先级类,该类用于确定进程线程的计划优先级。 有关值的列表,请参阅 GetPriorityClass。 如果未指定任何优先级类标志,则优先级类默认为 NORMAL_PRIORITY_CLASS ,除非创建过程的优先级类 IDLE_PRIORITY_CLASSBELOW_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 环境块以两个 0 (零个字节) 字节终止:一个用于最后一个字符串,再一个用于终止块。 Unicode 环境块以 4 个零字节结尾:两个字节作为最后一个字符串,另外两个用于终止该块。

若要检索特定用户的环境块的副本,请使用 CreateEnvironmentBlock 函数。

[in, optional] lpCurrentDirectory

进程当前目录的完整路径。 字符串还可以指定 UNC 路径。

如果此参数为 NULL,则新进程具有与调用进程相同的当前驱动器和目录。 此功能主要用于需要启动应用程序并指定其初始驱动器和工作目录的 shell。

[in] lpStartupInfo

指向 STARTUPINFO 结构的指针。

应用程序必须将指定用户帐户的权限添加到指定的窗口工作站和桌面,甚至对于 WinSta0\Default 也是如此。

如果 lpDesktop 成员为 NULL 或空字符串,则新进程将继承其父进程的桌面和窗口工作站。 应用程序必须将指定用户帐户的权限添加到继承的窗口工作站和桌面。

Windows XP: CreateProcessWithLogonW 将指定用户帐户的权限添加到继承的窗口工作站和桌面。

当不再需要 STARTUPINFO 中的句柄时,必须使用 CloseHandle 将其关闭。

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

[out] lpProcessInformation

指向 PROCESS_INFORMATION 结构的指针,该结构接收新进程的标识信息,包括进程的句柄。

当不需要时,必须使用 CloseHandle 函数关闭PROCESS_INFORMATION中的句柄。

返回值

如果该函数成功,则返回值为非零值。

如果函数失败,则返回值为 0(零)。 要获得更多的错误信息,请调用 GetLastError。

请注意,函数在进程完成初始化之前返回 。 如果找不到所需的 DLL 或无法初始化,则进程将终止。 若要获取进程的终止状态,请调用 GetExitCodeProcess

注解

默认情况下, CreateProcessWithLogonW 不会将指定的用户配置文件加载到 HKEY_USERS 注册表项中。 这意味着访问 HKEY_CURRENT_USER 注册表项中的信息可能不会生成与正常交互式登录一致的结果。 在调用 CreateProcessWithLogonW 之前,你有责任使用 LOGON_WITH_PROFILE 或调用 LoadUserProfile 函数将用户注册表配置单元加载到 HKEY_USERS

如果 lpEnvironment 参数为 NULL,则新进程使用根据 lpUserName 指定的用户的配置文件创建的环境块。 如果未设置 HOMEDRIVE 和 HOMEPATH 变量, CreateProcessWithLogonW 将修改环境块以使用用户工作目录的驱动器和路径。

创建新进程和线程句柄后, (PROCESS_ALL_ACCESSTHREAD_ALL_ACCESS) 接收完全访问权限。 对于任一句柄,如果未提供安全描述符,则可以在需要该类型对象句柄的任何函数中使用句柄。 提供安全描述符时,在授予访问权限之前,对句柄的所有后续使用执行访问检查。 如果拒绝访问,则请求进程无法使用句柄获取对进程或线程的访问权限。

若要检索安全令牌,请将 PROCESS_INFORMATION 结构中的进程句柄传递给 OpenProcessToken 函数。

为进程分配一个进程标识符。 标识符在进程终止之前有效。 它可用于标识进程,也可以在 OpenProcess 函数中指定它以打开进程的句柄。 进程中的初始线程也分配有一个线程标识符。 可以在 OpenThread 函数中指定它以打开线程的句柄。 标识符在线程终止之前有效,可用于唯一标识系统中的线程。 这些标识符在 PROCESS_INFORMATION 中返回。

调用线程可以使用 WaitForInputIdle 函数等待,直到新进程完成初始化并等待用户输入,而没有挂起的输入。 这对于父进程和子进程之间的同步非常有用,因为 CreateProcessWithLogonW 返回时不会等待新进程完成初始化。 例如,创建进程会在尝试查找与新进程关联的窗口之前使用 WaitForInputIdle

关闭进程的首选方法是使用 ExitProcess 函数,因为此函数会向附加到进程的所有 DLL 发送即将终止的通知。 关闭进程的其他方式不会通知附加的 DLL。 请注意,当线程调用 ExitProcess 时,进程的其他线程将终止,没有机会执行任何其他代码 (包括附加 DLL 的线程终止代码) 。 有关详细信息,请参阅 终止进程

CreateProcessWithLogonW 在目标用户的安全上下文中访问指定的目录和可执行映像。 如果可执行映像位于网络上,并且路径中指定了网络驱动器号,则网络驱动器号对目标用户不可用,因为每次登录都可以分配网络驱动器号。 如果指定了网络驱动器号,则此函数将失败。 如果可执行映像位于网络上,请使用 UNC 路径。

此函数可以创建并同时运行的子进程数有限制。 例如,在 Windows XP 上,此限制为 MAXIMUM_WAIT_OBJECTS*4。 但是,由于系统范围的配额限制,可能无法创建如此多的进程。

具有 SP2、Windows Server 2003 或更高版本的 Windows XP: 不能从“LocalSystem”帐户下运行的进程调用 CreateProcessWithLogonW ,因为该函数在调用方令牌中使用登录 SID,并且“LocalSystem”帐户的令牌不包含此 SID。 作为替代方法,请使用 CreateProcessAsUserLogonUser 函数。

若要编译使用此函数的应用程序, 请将_WIN32_WINNT 定义为 0x0500 或更高版本。 有关详细信息,请参阅 使用 Windows 标头

安全备注

lpApplicationName 参数可以为 NULL,可执行文件名称必须是 lpCommandLine 中第一个空格分隔的字符串。 如果可执行文件或路径名称中有一个空格,则存在运行其他可执行文件的风险,因为函数分析空格的方式。 避免使用以下示例,因为该函数尝试运行“Program.exe”(如果存在),而不是“MyApp.exe”。
LPTSTR szCmdline[]=_tcsdup(TEXT("C:\\Program Files\\MyApp"));
CreateProcessWithLogonW(..., szCmdline, ...)

如果恶意用户在系统上创建名为“Program.exe”的应用程序,则使用 Program Files 目录错误地调用 CreateProcessWithLogonW 的任何程序将运行恶意用户应用程序,而不是预期的应用程序。

若要避免此问题,请不要为 lpApplicationName 传递 NULL。 如果为 lpApplicationName 传递 NULL,请在 lpCommandLine 中的可执行路径周围使用引号,如以下示例所示:

LPTSTR szCmdline[]=_tcsdup(TEXT("\"C:\\Program Files\\MyApp\""));
CreateProcessWithLogonW(..., szCmdline, ...)

示例

以下示例演示如何调用此函数。


#include <windows.h>
#include <stdio.h>
#include <userenv.h>

void DisplayError(LPWSTR pszAPI)
{
    LPVOID lpvMessageBuffer;

    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, GetLastError(), 
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
        (LPWSTR)&lpvMessageBuffer, 0, NULL);

    //
    //... now display this string
    //
    wprintf(L"ERROR: API        = %s.\n", pszAPI);
    wprintf(L"       error code = %d.\n", GetLastError());
    wprintf(L"       message    = %s.\n", (LPWSTR)lpvMessageBuffer);

    //
    // Free the buffer allocated by the system
    //
    LocalFree(lpvMessageBuffer);

    ExitProcess(GetLastError());
}

void wmain(int argc, WCHAR *argv[])
{
    DWORD     dwSize;
    HANDLE    hToken;
    LPVOID    lpvEnv;
    PROCESS_INFORMATION pi = {0};
    STARTUPINFO         si = {0};
    WCHAR               szUserProfile[256] = L"";

    si.cb = sizeof(STARTUPINFO);
    
    if (argc != 4)
    {
        wprintf(L"Usage: %s [user@domain] [password] [cmd]", argv[0]);
        wprintf(L"\n\n");
        return;
    }

    //
    // TO DO: change NULL to '.' to use local account database
    //
    if (!LogonUser(argv[1], NULL, argv[2], LOGON32_LOGON_INTERACTIVE, 
            LOGON32_PROVIDER_DEFAULT, &hToken))
        DisplayError(L"LogonUser");

    if (!CreateEnvironmentBlock(&lpvEnv, hToken, TRUE))
        DisplayError(L"CreateEnvironmentBlock");

    dwSize = sizeof(szUserProfile)/sizeof(WCHAR);

    if (!GetUserProfileDirectory(hToken, szUserProfile, &dwSize))
        DisplayError(L"GetUserProfileDirectory");

    //
    // TO DO: change NULL to '.' to use local account database
    //
    if (!CreateProcessWithLogonW(argv[1], NULL, argv[2], 
            LOGON_WITH_PROFILE, NULL, argv[3], 
            CREATE_UNICODE_ENVIRONMENT, lpvEnv, szUserProfile, 
            &si, &pi))
        DisplayError(L"CreateProcessWithLogonW");

    if (!DestroyEnvironmentBlock(lpvEnv))
        DisplayError(L"DestroyEnvironmentBlock");

    CloseHandle(hToken);
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
}

要求

要求
最低受支持的客户端 Windows XP [仅限桌面应用]
最低受支持的服务器 Windows Server 2003 [仅限桌面应用]
目标平台 Windows
标头 winbase.h (包括 Windows.h)
Library Advapi32.lib
DLL Advapi32.dll

另请参阅

CloseHandle

CreateEnvironmentBlock

CreateProcessAsUser

ExitProcess

GetEnvironmentStrings

GetExitCodeProcess

OpenProcess

PROCESS_INFORMATION

进程和线程函数

进程

STARTUPINFO

SetErrorMode

WaitForInputIdle