single.php

IISでCreateProcessAsUserが動作しない場合の対処法

ASP.NET WebプロジェクトをIISでホスティングした際に、別の実行可能ファイルを起動するためにCreateProcessAsUserを利用する場合に動作しない場合の対処法を備忘録的に投稿します。

ユーザーセッションで起動

Win32APIの[CreateProcessAsUser]関数を利用するとログインしているユーザーセッションでプロセスを作成して別アプリの実行ができます。

例えばC#の場合は、こんな感じで構造体とWin32APIのインポートを行います。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
  public int cb;
  public IntPtr lpReserved;
  public IntPtr lpDesktop;
  public IntPtr lpTitle;
  public uint dwX;
  public uint dwY;
  public uint dwXSize;
  public uint dwYSize;
  public uint dwXCountChars;
  public uint dwYCountChars;
  public uint dwFillAttribute;
  public uint dwFlags;
  public ushort wShowWindow;
  public ushort cbReserved2;
  public IntPtr lpReserved2;
  public IntPtr hStdInput;
  public IntPtr hStdOutput;
  public IntPtr hStdError;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct PROCESS_INFORMATION
{
  public IntPtr hProcess;
  public IntPtr hThread;
  public uint dwProcessId;
  public uint dwThreadId;
}

[DllImport(@"ADVAPI32.dll", CharSet = CharSet.Unicode)]
public extern static Int32 CreateProcessAsUser(IntPtr hToken, IntPtr lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, Int32 bInheritHandles, UInt32 dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

[DllImport(@"Kernel32.dll")]
public extern static Int32 CloseHandle(IntPtr hObject);

[DllImport(@"Kernel32.dll")]
public extern static UInt32 WTSGetActiveConsoleSessionId();

[DllImport(@"WTSAPI32.dll")]
public extern static Int32 WTSQueryUserToken(UInt32 SessionId, out IntPtr phToken);

実際にアプリを起動する場合は、こんな感じで呼び出します。

public void Execute(string filename)
{
  uint sessionId = WTSGetActiveConsoleSessionId();
  IntPtr userToken;

  if (WTSQueryUserToken(sessionId, out userToken) == 0)
  {
    return;
  }

  STARTUPINFO startupInfo = new STARTUPINFO();
  startupInfo.cb = Marshal.SizeOf(startupInfo);

  PROCESS_INFORMATION pi;
  int result = CreateProcessAsUser(
        userToken,
        IntPtr.Zero,
        filename,
        IntPtr.Zero,
        IntPtr.Zero,
        0,
        0,
        IntPtr.Zero,
        System.IO.Directory.GetCurrentDirectory(),
        ref startupInfo,
        out pi);

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

実行するとログインしているユーザーのプロセスでアプリが実行されるので、[Windows サービス]などシステムアカウントが実行しているプロセス内からユーザーセッションでアプリの起動が可能になります。

IISのアプリケーションプールから実行

[CreateProcessAsUser]関数を、IISで利用したところ動作しない場合がありました。

アプリケーションプールの既定値で設定されるアカウント[ApplicationPoolIdentity]の場合にはアプリが起動できませんでした。

[詳細設定]画面で[アプリケーション プール ID]を[ビルドイン アカウント]の一覧から[LocalSystem]に変更すると、アプリが起動可能です。

まとめ

ASP.NET WebプロジェクトをIISでホスティングした際に別の実行可能ファイルを起動するためにCreateProcessAsUserを利用する場合に動作しない場合の対処法をについて書きました。

WindowsサービスやIISなどでホスティングするような場合、アプリケーションプールの実行アカウントによって[CreateProcessAsUser]関数で別アプリを起動が制限される場合があります。

そのため[アプリケーションプール]の実行アカウントを[LocalSystem]などに変更する必要があります。

IISでホスティングした状態で[CreateProcessAsUser]関数で別アプリが起動しない人の参考になれば幸いです。

スポンサーリンク

最後までご覧いただき、ありがとうございます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です