single.php

C# ASP.NET WebからProcess.Startで実行可能ファイル(.exe)を起動できない場合の対処法

Visual StudioでASP.NET WebプロジェクトでProcess.Startメソッドで実行可能ファイル(拡張子 .exe)が起動しない場合の対処法を備忘録的に投稿します。

Process.Startで別アプリの起動

Visual Studioのデバッグ時には次のコードで、別アプリの起動できてもWindowsサービスやIISにホスティングした際に動作しない場合があります。

public void Execute(string filename)
{
  ProcessStartInfo pi = new ProcessStartInfo();
  pi.UseShellExecute = true;
  pi.FileName = filename;
  Process.Start(pi);
}

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

原因はアプリが動作しているのがシステム(LocalSystemなど)の場合に、ユーザーインターフェイスを持つアプリが起動できないような制限があるためです。

なので、Win32APIの[CreateProcessAsUser]関数を利用すると起動が出来る場合があります。

具体的には、次のような構造体とWin32APIを利用するための[DllImport]を追加します。

[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 サービス]からユーザーインタフェイスを持つアプリが表示されるようになります。

まとめ

Visual StudioでASP.NET WebプロジェクトでProcess.Startメソッドで実行可能ファイル(拡張子 .exe)が起動しない場合の対処法について書きました。

WindowsサービスやIISなどでホスティングしたASP.NET Core Webアプリは、ユーザーとは別のプロセス(システムなど)で動作しているため、ユーザーインターフェイスを持つアプリの起動が制限されます。

そのため[CreateProcessAsUser]などのWin32APIでユーザーセッションで動作するように起動する必要があります。

ASP.NET WebプロジェクトでProcess.Startメソッドで実行可能ファイル(拡張子 .exe)が起動しない人の参考になれば幸いです。

スポンサーリンク

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

コメントを残す

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