Platform SDK: DLLs, Processes, and Threads

CreateProcessAsUser

The CreateProcessAsUser function creates a new process and its primary thread. The new process then runs the specified executable file. The CreateProcessAsUser function is similar to the CreateProcess function, except that the new process runs in the security context of the user represented by the hToken parameter. By default, the new process is noninteractive, that is, it runs on a desktop that is not visible and cannot receive user input. Also, by default, the new process inherits the environment of the calling process, rather than the environment associated with the specified user.

The CreateProcessWithLogonW function is similar to CreateProcessAsUser, except that the caller does not need to call the LogonUser function to authenticate the user and get a token.

This function is also similar to the SHCreateProcessAsUserW function.

BOOL CreateProcessAsUser(
  HANDLE hToken,                             // handle to user token
  LPCTSTR lpApplicationName,                 // name of executable module
  LPTSTR lpCommandLine,                      // command-line string
  LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // SD
  BOOL bInheritHandles,                      // inheritance option
  DWORD dwCreationFlags,                     // creation flags
  LPVOID lpEnvironment,                      // new environment block
  LPCTSTR lpCurrentDirectory,                // current directory name
  LPSTARTUPINFO lpStartupInfo,               // startup information
  LPPROCESS_INFORMATION lpProcessInformation // process information
);

Parameters

hToken
[in] Handle to a primary token that represents a user. The handle must have TOKEN_QUERY, TOKEN_DUPLICATE, and TOKEN_ASSIGN_PRIMARY access. For more information, see Access Rights for Access-Token Objects. The user represented by the token must have read and execute access to the application specified by the lpApplicationName or the lpCommandLine parameter.

If your process has the SE_TCB_NAME privilege, it can call the LogonUser function to get a primary token that represents the specified user.

Alternatively, you can call the DuplicateTokenEx function to convert an impersonation token into a primary token. This allows a server application that is impersonating a client to create a process that has the security context of the client.

Terminal Services: The process is run in the session specified in this token.

lpApplicationName
[in] Pointer to a null-terminated string that specifies the module to execute.

The string can specify the full path and file name of the module to execute or it can specify a partial name. In the case of a partial name, the function uses the current drive and current directory to complete the specification. The function will not use the search path.

This parameter can be NULL. In that case, the module name must be the first white space-delimited token in the lpCommandLine string. If you are using a long file name that contains a space, use quoted strings to indicate where the file name ends and the arguments begin; otherwise, the file name is ambiguous. For example, consider the string "c:\program files\sub dir\program name". This string can be interpreted in a number of ways. The system tries to interpret the possibilities in the following order:

c:\program.exe files\sub dir\program name
c:\program files\sub.exe dir\program name
c:\program files\sub dir\program.exe name
c:\program files\sub dir\program name.exe

The specified module can be a Win32-based application. It can be some other type of module (for example, MS-DOS or OS/2) if the appropriate subsystem is available on the local computer. If the executable module is a 16-bit application, lpApplicationName should be NULL, and the string pointed to by lpCommandLine should specify the executable module as well as its arguments. By default, all 16-bit Windows-based applications created by CreateProcessAsUser are run in a separate VDM (equivalent to CREATE_SEPARATE_WOW_VDM in CreateProcess).

lpCommandLine
[in] Pointer to a null-terminated string that specifies the command line to execute. The system adds a null character to the command line, trimming the string if necessary, to indicate which file was actually used.

The lpCommandLine parameter can be NULL. In that case, the function uses the string pointed to by lpApplicationName as the command line.

If both lpApplicationName and lpCommandLine are non-NULL, *lpApplicationName specifies the module to execute, and *lpCommandLine specifies the command line. The new process can use GetCommandLine to retrieve the entire command line. C runtime processes can use the argc and argv arguments. Note that it is a common practice to repeat the module name as the first token in the command line.

If lpApplicationName is NULL, the first white-space – delimited token of the command line specifies the module name. If you are using a long file name that contains a space, use quoted strings to indicate where the file name ends and the arguments begin (see the explanation for the lpApplicationName parameter). If the file name does not contain an extension, .exe is appended. If the file name ends in a period (.) with no extension, or if the file name contains a path, .exe is not appended. If the file name does not contain a directory path, the system searches for the executable file in the following sequence:

  1. The directory from which the application loaded.
  2. The current directory.
  3. The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of this directory. The name of this directory is System32.
  4. The 16-bit Windows system directory. There is no Win32 function that obtains the path of this directory, but it is searched. The name of this directory is System.
  5. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
  6. The directories that are listed in the PATH environment variable.
lpProcessAttributes
[in] Pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new process and determines whether child processes can inherit the returned handle. If lpProcessAttributes is NULL, the process gets a default security descriptor and the handle cannot be inherited.
lpThreadAttributes
[in] Pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new process and determines whether child processes can inherit the returned handle. If lpThreadAttributes is NULL, the thread gets a default security descriptor and the handle cannot be inherited.
bInheritHandles
[in] Indicates whether the new process inherits handles from the calling process. If TRUE, each inheritable open handle in the calling process is inherited by the new process. Inherited handles have the same value and access privileges as the original handles.
dwCreationFlags
[in] Specifies additional flags that control the priority class and the creation of the process. The following creation flags can be specified in any combination, except as noted.
Value Meaning
CREATE_BREAKAWAY_FROM_JOB Windows 2000: The child processes of a process associated with a job are not associated with the job.

If the calling process is not associated with a job, this flag has no effect. If the calling process is associated with a job, the job must set the JOB_OBJECT_LIMIT_BREAKAWAY_OK limit or CreateProcess will fail.

CREATE_DEFAULT_ERROR_MODE The new process does not inherit the error mode of the calling process. Instead, CreateProcessAsUser gives the new process the current default error mode. An application sets the current default error mode by calling SetErrorMode.

This flag is particularly useful for multi-threaded shell applications that run with hard errors disabled.

The default behavior for CreateProcessAsUser is for the new process to inherit the error mode of the caller. Setting this flag changes that default behavior.

CREATE_NEW_CONSOLE The new process has a new console, instead of inheriting the parent's console. This flag cannot be used with the DETACHED_PROCESS flag.
CREATE_NEW_PROCESS_GROUP The new process is the root process of a new process group. The process group includes all processes that are descendants of this root process. The process identifier of the new process group is the same as the process identifier, which is returned in the lpProcessInformation parameter. Process groups are used by the GenerateConsoleCtrlEvent function to enable sending a CTRL+C or CTRL+BREAK signal to a group of console processes.
CREATE_SUSPENDED The primary thread of the new process is created in a suspended state, and does not run until the ResumeThread function is called.
CREATE_UNICODE_ENVIRONMENT Indicates the format of the lpEnvironment parameter. If this flag is set, the environment block pointed to by lpEnvironment uses Unicode characters. Otherwise, the environment block uses ANSI characters.
DEBUG_ONLY_THIS_PROCESS If this flag is not set and the calling process is being debugged, the new process becomes another process being debugged by the calling process's debugger. If the calling process is not a process being debugged, no debugging-related actions occur.
DEBUG_PROCESS If this flag is set, the calling process is treated as a debugger, and the new process is debugged. The system notifies the debugger of all debug events that occur in the process being debugged.
DETACHED_PROCESS For console processes, the new process does not have access to the console of the parent process. The new process can call the AllocConsole function later to create a new console. This flag cannot be used with the CREATE_NEW_CONSOLE flag.

The dwCreationFlags parameter also controls the new process's priority class, which is used to determine the scheduling priorities of the process's threads. If none of the following priority class flags is specified, the priority class is NORMAL_PRIORITY_CLASS by default unless the priority class of the creating process is IDLE_PRIORITY_CLASS or BELOW_NORMAL_PRIORITY_CLASS. In this case, the child process receives the default priority class of the calling process. One of the following flags can be specified.
Priority Meaning
ABOVE_NORMAL_PRIORITY_CLASS Windows 2000: Indicates a process that has priority higher than NORMAL_PRIORITY_CLASS but lower than HIGH_PRIORITY_CLASS.
BELOW_NORMAL_PRIORITY_CLASS Windows 2000: Indicates a process that has priority higher than IDLE_PRIORITY_CLASS but lower than NORMAL_PRIORITY_CLASS.
HIGH_PRIORITY_CLASS Indicates a process that performs time-critical tasks. The threads of a high-priority class process preempt the threads of normal-priority or idle-priority class processes. An example is the Task List, which must respond quickly when called by the user, regardless of the load on the operating system. Use extreme care when using the high-priority class, because a CPU-bound application with a high-priority class can use nearly all available cycles.
IDLE_PRIORITY_CLASS Indicates a process whose threads run only when the system is idle and are preempted by the threads of any process running in a higher priority class. An example is a screen saver. The idle priority class is inherited by child processes.
NORMAL_PRIORITY_CLASS Indicates a normal process with no special scheduling needs.
REALTIME_PRIORITY_CLASS Indicates a process that has the highest possible priority. The threads of a real-time priority class process preempt the threads of all other processes, including operating system processes performing important tasks. For example, a real-time process that executes for more than a very brief interval can cause disk caches not to flush or cause the mouse to be unresponsive.

lpEnvironment
[in] Pointer to an environment block for the new process. If this parameter is NULL, the new process uses the environment of the calling process.

If an application provides an environment block, rather than passing NULL for this parameter, the current directory information of the system drives is not automatically propagated to the new process. For a discussion of this situation and how to handle it, see the following Remarks section.

An environment block can contain either Unicode or ANSI characters. If the environment block pointed to by lpEnvironment contains Unicode characters, set the dwCreationFlags field's CREATE_UNICODE_ENVIRONMENT flag. Otherwise, do not set this flag.

Note that an ANSI environment block is terminated by two zero bytes: one for the last string, one more to terminate the block. A Unicode environment block is terminated by four zero bytes: two for the last string, two more to terminate the block.

To retrieve a copy of the environment block for a given user, use the CreateEnvironmentBlock function.

lpCurrentDirectory
[in] Pointer to a null-terminated string that specifies the current drive and directory for the new process. The string must be a full path and file name that includes a drive letter. If this parameter is NULL, the new process will have the same current drive and directory as the calling process. This option is provided primarily for shells that need to start an application and specify its initial drive and working directory.
lpStartupInfo
[in] Pointer to a STARTUPINFO structure that specifies how the main window for the new process should appear.
lpProcessInformation
[out] Pointer to a PROCESS_INFORMATION structure that receives identification information about the new process.

Return Values

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call GetLastError.

Remarks

Typically, the process that calls the CreateProcessAsUser function must have the SE_ASSIGNPRIMARYTOKEN_NAME and SE_INCREASE_QUOTA_NAME privileges. However, if hToken is a restricted version of the caller's primary token, the SE_ASSIGNPRIMARYTOKEN_NAME privilege is not required. If the necessary privileges are not already enabled, CreateProcessAsUser enables them for the duration of the call.

CreateProcessAsUser must be able to open the primary token of the calling process for TOKEN_DUPLICATE and TOKEN_IMPERSONATE access.

By default, CreateProcessAsUser creates the new process on a noninteractive window station with a desktop that is not visible and cannot receive user input. To enable user interaction with the new process, you must specify the name of the default interactive window station and desktop, "winsta0\default", in the lpDesktop member of the STARTUPINFO structure. In addition, before calling CreateProcessAsUser, you must change the discretionary access control list (DACL) of both the default interactive window station and the default desktop. The DACLs for the window station and desktop must grant access to the user or the logon session represented by the hToken parameter.

CreateProcessAsUser does not load the specified user's profile into the HKEY_USERS registry key. This means that access to information in the HKEY_CURRENT_USER registry key may not produce results consistent with a normal interactive logon. It is your responsibility to load the user's registry hive into HKEY_USERS with the LoadUserProfile function before calling CreateProcessAsUser.

If the lpEnvironment parameter is NULL, the new process inherits the environment of the calling process. CreateProcessAsUser does not automatically modify the environment block to include environment variables specific to the user represented by hToken. For example, the USERNAME and USERDOMAIN variables are inherited from the calling process if lpEnvironment is NULL. It is your responsibility to prepare the environment block for the new process and specify it in lpEnvironment.

CreateProcessAsUser allows you to access the specified directory and executable image in the security context of the caller or the target user. By default, CreateProcessAsUser accesses the directory and executable image in the security context of the caller. In this case, if the caller does not have access to the directory and executable image, the function fails. To access the directory and executable image using the security context of the target user, specify hToken in a call to the ImpersonateLoggedOnUser function before calling CreateProcessAsUser.

When created, the new process and the new thread handles receive full access rights (PROCESS_ALL_ACCESS and THREAD_ALL_ACCESS). For either handle, if a security descriptor is not provided, the handle can be used in any function that requires an object handle of that type. When a security descriptor is provided, an access check is performed on all subsequent uses of the handle before access is granted. If access is denied, the requesting process is not able to use the handle to gain access to the process or thread.

If the lpProcessAttributes parameter is NULL, the default security descriptor for the user referenced in the hToken parameter will be used. This security descriptor may not allow access for the caller, in which case the process may not be opened again once it is run. The handle returned in the PROCESS_INFORMATION structure is valid and will continue to have full access rights. This is also true for thread attributes.

Handles in PROCESS_INFORMATION must be closed with CloseHandle when they are no longer needed.

The process is assigned a process identifier. The identifier is valid until the process terminates. It can be used to identify the process, or specified in the OpenProcess function to open a handle to the process. The initial thread in the process is also assigned a thread identifier. The identifier is valid until the thread terminates and can be used to uniquely identify the thread within the system. These identifiers are returned in the PROCESS_INFORMATION structure.

When specifying an application name in the lpApplicationName or lpCommandLine strings, it doesn't matter whether the application name includes the file name extension, with one exception: an MS-DOS – based or Windows-based application whose file name extension is .com must include the .com extension.

The calling thread can use the WaitForInputIdle function to wait until the new process has finished its initialization and is waiting for user input with no input pending. This can be useful for synchronization between parent and child processes, because CreateProcessAsUser returns without waiting for the new process to finish its initialization. For example, the creating process would use WaitForInputIdle before trying to find a window associated with the new process.

The preferred way to shut down a process is by using the ExitProcess function, because this function sends notification of approaching termination to all DLLs attached to the process. Other means of shutting down a process do not notify the attached DLLs. Note that when a thread calls ExitProcess, other threads of the process are terminated without an opportunity to execute any additional code (including the thread termination code of attached DLLs).

ExitProcess, ExitThread, CreateThread, CreateRemoteThread, and a process that is starting (as the result of a call by CreateProcessAsUser) are serialized between each other within a process. Only one of these events can happen at a time, and the following restrictions apply:

Requirements

  Windows NT/2000: Requires Windows NT 3.51 or later.
  Windows 95/98: Unsupported.
  Header: Declared in Winbase.h; include Windows.h.
  Library: Use Advapi32.lib.
  Unicode: Implemented as Unicode and ANSI versions on Windows NT/2000.

See Also

Processes and Threads Overview, Process and Thread Functions, CloseHandle, CreateEnvironmentBlock, CreateProcess, CreateProcessWithLogonW, CreateRemoteThread, CreateThread, ExitProcess, ExitThread, GetEnvironmentStrings, GetExitCodeProcess, GetStartupInfo, ImpersonateLoggedOnUser, PROCESS_INFORMATION, SECURITY_ATTRIBUTES, SetErrorMode, SHCreateProcessAsUserW, STARTUPINFO, WaitForInputIdle