CreateProcessAsUserA 関数 (processthreadsapi.h)
新しいプロセスとそのプライマリ スレッドを作成します。 新しいプロセスは、指定されたトークンによって表されるユーザーのセキュリティ コンテキストで実行されます。
通常、 CreateProcessAsUser 関数を呼び出すプロセスには SE_INCREASE_QUOTA_NAME 特権が必要であり、トークンが割り当てできない場合は SE_ASSIGNPRIMARYTOKEN_NAME 特権が必要になる場合があります。 この関数が ERROR_PRIVILEGE_NOT_HELD (1314) で失敗する場合は、代わりに CreateProcessWithLogonW 関数を使用します。 CreateProcessWithLogonW には特別な特権は必要ありませんが、指定されたユーザー アカウントで対話形式でログオンできるようにする必要があります。 一般に、 CreateProcessWithLogonW を使用して、代替資格情報を使用してプロセスを作成することをお勧めします。
構文
BOOL CreateProcessAsUserA(
[in, optional] HANDLE hToken,
[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] hToken
ユーザーを表すプライマリ トークンへのハンドル。 ハンドルには、TOKEN_QUERY、 TOKEN_DUPLICATE、 およびTOKEN_ASSIGN_PRIMARYアクセス権 が 必要です。 詳細については、「 Access-Token オブジェクトのアクセス権」を参照してください。 トークンで表されるユーザーは、 lpApplicationName または lpCommandLine パラメーターで指定されたアプリケーションへの読み取りと実行アクセス権を持っている必要があります。
指定したユーザーを表すプライマリ トークンを取得するには、 LogonUser 関数を呼び出します。 または、 DuplicateTokenEx 関数を呼び出して、偽装トークンをプライマリ トークンに変換することもできます。 これにより、クライアントを偽装しているサーバー アプリケーションは、クライアントのセキュリティ コンテキストを持つプロセスを作成できます。
hToken が呼び出し元のプライマリ トークンの制限付きバージョンである場合、SE_ASSIGNPRIMARYTOKEN_NAME特権は必要ありません。 必要な特権がまだ有効になっていない場合は、 CreateProcessAsUser によって呼び出しの間有効になります。 詳細については、「特別な特権を使用して実行する」を参照してください。
ターミナル サービス: このプロセスは、トークンで指定されたセッションで実行されます。 既定では、これは LogonUser と呼ばれるセッションと同じです。 セッションを変更するには、 SetTokenInformation 関数を使用します。
[in, optional] lpApplicationName
実行するモジュールの名前。 このモジュールは、Windows ベースのアプリケーションにすることができます。 適切なサブシステムがローカル コンピューターで使用できる場合は、他の種類のモジュール (MS-DOS や OS/2 など) を使用できます。
この文字列では、実行するモジュールの完全なパスとファイル名を指定することも、部分的な名前を指定することもできます。 部分名の場合、関数は現在のドライブと現在のディレクトリを使用して仕様を完了します。 関数は検索パスを使用しません。 このパラメーターには、ファイル名拡張子を含める必要があります。既定の拡張機能は想定されません。
lpApplicationName パラメーターには NULL を指定できます。 その場合、モジュール名は lpCommandLine 文字列の最初の空白で区切られたトークンである必要があります。 スペースを含む長いファイル名を使用している場合は、引用符で囲まれた文字列を使用して、ファイル名の末尾と引数の開始位置を示します。それ以外の場合、ファイル名はあいまいです。 たとえば、文字列 "c:\program files\sub dir\program name" を考えてみましょう。 この文字列は、さまざまな方法で解釈できます。 システムは、次の順序で可能性を解釈しようとします。
c :\program files\sub.exec:\program files\sub dir\program.exec:\program files\sub dir\program name.exec:\program.exe 実行可能モジュールが 16 ビット アプリケーションの場合、 lpApplicationName は NULL、 lpCommandLine が指す文字列は実行可能モジュールとその引数を指定する必要があります。 既定では、CreateProcessAsUser によって作成されたすべての 16 ビット Windows ベースのアプリケーションは、別の VDM (CreateProcess のCREATE_SEPARATE_WOW_VDMと同等) で実行されます。
[in, out, optional] lpCommandLine
実行するコマンド ライン。 この文字列の最大長は 32K 文字です。 lpApplicationName が NULL の場合、lpCommandLine のモジュール名の部分はMAX_PATH文字に制限されます。
この関数の Unicode バージョン である CreateProcessAsUserW は、この文字列の内容を変更できます。 したがって、このパラメーターを読み取り専用メモリ ( 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, optional] lpProcessAttributes
新しいプロセス オブジェクトのセキュリティ記述子を指定し、子プロセスが返されたハンドルをプロセスに継承できるかどうかを判断する、 SECURITY_ATTRIBUTES 構造体へのポインター。 lpProcessAttributes が NULL または lpSecurityDescriptor が NULL の場合、プロセスは既定のセキュリティ記述子を取得し、ハンドルを継承できません。 既定のセキュリティ記述子は、 hToken パラメーターで参照されているユーザーのセキュリティ記述子です。 このセキュリティ記述子は、呼び出し元へのアクセスを許可しない場合があります。その場合、プロセスの実行後にプロセスを再度開くことができない可能性があります。 プロセス ハンドルは有効であり、引き続きフル アクセス権を持ちます。
[in, optional] lpThreadAttributes
新しいスレッド オブジェクトのセキュリティ記述子を指定し、子プロセスが返されたハンドルをスレッドに継承できるかどうかを判断する、 SECURITY_ATTRIBUTES 構造体へのポインター。 lpThreadAttributes が NULL または lpSecurityDescriptor が NULL の場合、スレッドは既定のセキュリティ記述子を取得し、ハンドルを継承できません。 既定のセキュリティ記述子は、 hToken パラメーターで参照されているユーザーのセキュリティ記述子です。 このセキュリティ記述子は、呼び出し元へのアクセスを許可しない場合があります。
[in] bInheritHandles
このパラメーターが TRUE の場合、呼び出し元プロセス内の継承可能な各ハンドルは、新しいプロセスによって継承されます。 パラメーターが FALSE の場合、ハンドルは継承されません。 継承されたハンドルには、元のハンドルと同じ値とアクセス権があることに注意してください。 継承可能なハンドルの詳細については、「解説」を参照してください。
ターミナル サービス: セッション間でハンドルを継承することはできません。 さらに、このパラメーターが TRUE の場合は、呼び出し元と同じセッションでプロセスを作成する必要があります。
プロテクト プロセス ライト (PPL) プロセス: 非 PPL プロセスから PPL プロセスへのPROCESS_DUP_HANDLEが許可されていないため、PPL プロセスが非 PPL プロセスを作成すると、汎用ハンドルの継承がブロックされます。 「プロセスのセキュリティとアクセス権」を参照してください
[in] dwCreationFlags
優先度クラスとプロセスの作成を制御するフラグ。 値の一覧については、「 プロセス作成フラグ」を参照してください。
このパラメーターは、新しいプロセスの優先度クラスも制御します。これは、プロセスのスレッドのスケジュールの優先順位を決定するために使用されます。 値の一覧については、「 GetPriorityClass」を参照してください。 優先度クラス フラグが指定されていない場合、作成プロセスの優先度クラスがIDLE_PRIORITY_CLASSまたはBELOW_NORMAL_PRIORITY_CLASSでない限り、優先度クラスの既定値は NORMAL_PRIORITY_CLASS になります。 この場合、子プロセスは呼び出し元プロセスの既定の優先度クラスを受け取ります。
dwCreationFlags パラメーターの値が 0 の場合:
- プロセスは、呼び出し元のエラー モードと親のコンソールの両方を継承します。
- 新しいプロセスの環境ブロックには ANSI 文字が含まれていると見なされます (追加情報については 、lpEnvironment パラメーターを参照してください)。
- 16 ビット Windows ベースのアプリケーションは、共有 Virtual DOS マシン (VDM) で実行されます。
[in, optional] lpEnvironment
新しいプロセスの環境ブロックへのポインター。 このパラメーターが NULL の場合、新しいプロセスは呼び出し元プロセスの環境を使用します。
環境ブロックは、null で終わる文字列の null で終わるブロックで構成されます。 各文字列の形式は次のとおりです。
名前=value\0
等号は区切り記号として使用されるため、環境変数の名前には使用しないでください。
環境ブロックには、Unicode 文字または ANSI 文字を含めることができます。 lpEnvironment が指す環境ブロックに Unicode 文字が含まれている場合は、dwCreationFlags にCREATE_UNICODE_ENVIRONMENTが含まれていることを確認してください。
この関数の ANSI バージョン である CreateProcessAsUserA は、プロセスの環境ブロックの合計サイズが 32,767 文字を超えると失敗します。
ANSI 環境ブロックは 2 つのゼロ バイトで終了されることに注意してください。最後の文字列の場合は 1 つ、もう 1 つはブロックを終了します。 Unicode 環境ブロックは 4 つのゼロ バイトで終了します。最後の文字列の場合は 2 バイト、ブロックを終了するにはさらに 2 バイトです。
Windows Server 2003 および Windows XP: 組み合わせたユーザー環境変数とシステム環境変数のサイズが 8192 バイトを超える場合、 CreateProcessAsUser によって作成されたプロセスは、親プロセスによって関数に渡された環境ブロックで実行されなくなります。 代わりに、子プロセスは CreateEnvironmentBlock 関数によって返される環境ブロックで実行されます。
特定のユーザーの環境ブロックのコピーを取得するには、 CreateEnvironmentBlock 関数を使用します。
[in, optional] lpCurrentDirectory
プロセスの現在のディレクトリへの完全なパス。 この文字列では、UNC パスを指定することもできます。
このパラメーターが NULL の場合、新しいプロセスは呼び出し元プロセスと同じ現在のドライブとディレクトリを持つことになります。 (この機能は、主にアプリケーションを起動し、初期ドライブと作業ディレクトリを指定する必要があるシェル用に提供されます。
[in] lpStartupInfo
STARTUPINFO または STARTUPINFOEX 構造体へのポインター。
ユーザーは、指定されたウィンドウ ステーションとデスクトップの両方にフル アクセスできる必要があります。 プロセスを対話形式にする場合は、winsta0\default を指定します。 lpDesktop メンバーが NULL の場合、新しいプロセスは親プロセスのデスクトップステーションとウィンドウステーションを継承します。 このメンバーが空の文字列 "" の場合、新しいプロセスは、「ウィンドウ ステーションへの接続を処理する」で説明されている規則を使用して ウィンドウ ステーションに接続します。
拡張属性を設定するには、STARTUPINFOEX 構造体を使用し、dwCreationFlags パラメーターにEXTENDED_STARTUPINFO_PRESENTを指定します。
STARTUPINFO または STARTUPINFOEX のハンドルは、必要なくなったら CloseHandle で閉じる必要があります。
[out] lpProcessInformation
新しいプロセスに関する識別情報を受け取る PROCESS_INFORMATION 構造体へのポインター。
PROCESS_INFORMATIONのハンドルは、必要なくなったときに CloseHandle で閉じる必要があります。
戻り値
関数が成功すると、戻り値は 0 以外になります。
関数が失敗した場合は、0 を返します。 詳細なエラー情報を得るには、GetLastError を呼び出します。
関数は、プロセスの初期化が完了する前に が返されることに注意してください。 必要な DLL が見つからないか、初期化に失敗した場合、プロセスは終了します。 プロセスの終了状態を取得するには、 GetExitCodeProcess を呼び出します。
注釈
CreateProcessAsUser は、 TOKEN_DUPLICATEとTOKEN_IMPERSONATE アクセス権を使用して、呼び出し元プロセスのプライマリ トークン を 開くことができる必要があります。
既定では、 CreateProcessAsUser は、表示されず、ユーザー入力を受信できないデスクトップを使用して、非対話型ウィンドウ ステーションに新しいプロセスを作成します。 新しいプロセスとのユーザー操作を有効にするには、STARTUPINFO 構造体の lpDesktop メンバーに、既定の対話型ウィンドウ ステーションとデスクトップの名前 "winsta0\default" を指定する必要があります。 さらに、 CreateProcessAsUser を呼び出す前に、既定の対話型ウィンドウ ステーションと既定のデスクトップの両方の随意アクセス制御リスト (DACL) を変更する必要があります。 ウィンドウ ステーションとデスクトップの DACL は、 hToken パラメーターで表されるユーザーまたはログオン セッションへのアクセス権を付与する必要があります。
CreateProcessAsUser では、指定したユーザーのプロファイルが HKEY_USERS レジストリ キーに読み込まれません。 したがって、HKEY_CURRENT_USERレジストリ キーの情報にアクセスするには、CreateProcessAsUser を呼び出す前に、LoadUserProfile 関数を使用してユーザーのプロファイル情報をHKEY_USERSに読み込む必要があります。 新しいプロセスが終了した後は、 必ず UnloadUserProfile を呼び出してください。
lpEnvironment パラメーターが NULL の場合、新しいプロセスは呼び出し元プロセスの環境を継承します。 CreateProcessAsUser は、 hToken で表されるユーザーに固有の環境変数を含むように環境ブロックを自動的に変更することはありません。 たとえば、 lpEnvironment が NULL の場合、USERNAME 変数と USERDOMAIN 変数は呼び出し元プロセスから継承されます。 新しいプロセス用に環境ブロックを準備し、 lpEnvironment で指定するのはユーザーの責任です。
CreateProcessWithLogonW 関数と CreateProcessWithTokenW 関数は CreateProcessAsUser に似ていますが、呼び出し元が LogonUser 関数を呼び出してユーザーを認証し、トークンを取得する必要はありません。
CreateProcessAsUser を使用すると、呼び出し元またはターゲット ユーザーのセキュリティ コンテキストで、指定されたディレクトリと実行可能イメージにアクセスできます。 既定では、 CreateProcessAsUser は 、呼び出し元のセキュリティ コンテキスト内のディレクトリと実行可能イメージにアクセスします。 この場合、呼び出し元がディレクトリと実行可能イメージにアクセスできない場合、関数は失敗します。 ターゲット ユーザーのセキュリティ コンテキストを使用してディレクトリと実行可能イメージにアクセスするには、CreateProcessAsUser を呼び出す前に ImpersonateLoggedOnUser 関数の呼び出しで hToken を指定します。
プロセスにはプロセス識別子が割り当てられます。 識別子は、プロセスが終了するまで有効です。 これを使用してプロセスを識別するか、 OpenProcess 関数で指定してプロセスへのハンドルを開くことができます。 プロセス内の初期スレッドにもスレッド識別子が割り当てられます。 スレッドへのハンドルを開くには、 OpenThread 関数で指定できます。 識別子は、スレッドが終了するまで有効であり、システム内のスレッドを一意に識別するために使用できます。 これらの識別子は、 PROCESS_INFORMATION 構造体で返されます。
呼び出し元のスレッドは WaitForInputIdle 関数を使用して、新しいプロセスの初期化が完了し、入力が保留されていないユーザー入力を待機しています。 これは、親プロセスと子プロセスの間の同期に役立ちます。 CreateProcessAsUser は 、新しいプロセスが初期化を完了するのを待たずに を返します。 たとえば、作成プロセスでは、新しいプロセスに関連付けられているウィンドウを検索する前に WaitForInputIdle を使用します。
プロセスをシャットダウンする推奨される方法は 、ExitProcess 関数を使用することです。この関数は、プロセスにアタッチされているすべての DLL に終了に近づくという通知を送信するためです。 プロセスをシャットダウンするその他の方法では、アタッチされている DLL は通知されません。 スレッドが ExitProcess を呼び出すと、プロセスの他のスレッドは、追加のコード (アタッチされた DLL のスレッド終了コードを含む) を実行する機会なしに終了されることに注意してください。 詳細については、「 プロセスの終了」を参照してください。
既定では、bInheritHandles パラメーターの値として TRUE を渡すと、継承可能なすべてのハンドルが新しいプロセスによって継承されます。 これは、複数のスレッドから同時にプロセスを作成するが、各プロセスが異なるハンドルを継承することを望むアプリケーションでは問題になる可能性があります。 アプリケーションでは、 UpdateProcThreadAttributeList 関数を PROC_THREAD_ATTRIBUTE_HANDLE_LIST パラメーターと共に使用して、特定のプロセスによって継承されるハンドルの一覧を提供できます。
セキュリティに関する備考
lpApplicationName パラメーターには NULL を指定できます。この場合、実行可能ファイル名は lpCommandLine の最初の空白で区切られた文字列である必要があります。 実行可能ファイルまたはパス名にスペースが含まれている場合、関数がスペースを解析する方法により、別の実行可能ファイルが実行される可能性があります。 次の例は、関数が "MyApp.exe" ではなく "Program.exe" を実行しようとするため、危険です。 LPTSTR szCmdline[] = _tcsdup(TEXT("C:\\Program Files\\MyApp"));
CreateProcessAsUser(hToken, NULL, szCmdline, /*...*/ );
悪意のあるユーザーがシステムで "Program.exe" というアプリケーションを作成した場合、Program Files ディレクトリを使用して CreateProcessAsUser を 誤って呼び出すプログラムは、目的のアプリケーションではなく、このアプリケーションを実行します。
この問題を回避するには、 lpApplicationName に NULL を渡さないでください。 lpApplicationName に NULL を渡す場合は、次の例に示すように、lpCommandLine の実行可能パスを引用符で囲みます。
LPTSTR szCmdline[] = _tcsdup(TEXT("\"C:\\Program Files\\MyApp\""));
CreateProcessAsUser(hToken, NULL, szCmdline, /*...*/);
Powershell: CreateProcessAsUser 関数を使用して PowerShell バージョン 2.0 でコマンドレットを実装すると、このコマンドレットはファンインとファンアウトの両方のリモート セッションに対して正しく動作します。 ただし、特定のセキュリティ シナリオのため、 CreateProcessAsUser で実装されたコマンドレットは、ファンイン リモート セッションの PowerShell バージョン 3.0 でのみ正しく動作します。クライアント セキュリティ特権が不十分なため、ファンアウト リモート セッションは失敗します。 PowerShell バージョン 3.0 のファンイン リモート セッションとファンアウト リモート セッションの両方で機能するコマンドレットを実装するには、 CreateProcess 関数を使用します。
例
例については、「 対話型クライアント プロセスの開始」を参照してください。
注意
processthreadsapi.h ヘッダーは、CreateProcessAsUser をエイリアスとして定義し、UNICODE プリプロセッサ定数の定義に基づいて、この関数の ANSI または Unicode バージョンを自動的に選択します。 エンコードに依存しないエイリアスをエンコードニュートラルでないコードと組み合わせて使用すると、コンパイルまたはランタイム エラーが発生する不一致が発生する可能性があります。 詳細については、「 関数プロトタイプの規則」を参照してください。
要件
要件 | 値 |
---|---|
サポートされている最小のクライアント | Windows XP (デスクトップ アプリのみ) |
サポートされている最小のサーバー | Windows Server 2003 (デスクトップ アプリのみ) |
対象プラットフォーム | Windows |
ヘッダー | processthreadsapi.h (Windows.h を含む) |
Library | Advapi32.lib |
[DLL] | Advapi32.dll |