.NET TIPS

多重起動禁止時に実行中のWindowsアプリケーションを最前面に表示するには?

デジタルアドバンテージ
2004/04/23

 別稿「TIPS:Windowsアプリケーションの多重起動を禁止するには?」で、まったく同じWindowsアプリケーションを同時に複数起動するのを禁止する方法を紹介した。この方法では、すでに同じWindowsアプリケーションが実行中の場合、2番目以降に起動しようとしたWindowsアプリケーションは警告メッセージを表示して終了するようにしている。しかし、この警告メッセージの代わりに、すでに実行中のアプリケーションを最前面に表示した方が、より親切でユーザビリティが高いといえる。本稿ではその実装方法について解説する。

実行中のWindowsアプリケーションを最前面に表示する方法について

 実行中のWindowsアプリケーションを最前面に表示するには、そのウィンドウをアクティブ化する必要がある。これを行うには、通常ならばWindowsフォームであるFormオブジェクト(System.Windows.Forms名前空間)のActivateメソッドを呼び出せばよいが、多重起動時のWindowsアプリケーションのような、別プロセスのウィンドウに対しては、このメソッドが利用できない。

 そこで、別プロセスのWindowsアプリケーションを最前面に表示するために、Win32 APIのSetForegroundWindow関数を使用する。また、Windowsアプリケーションが「最小化」されている場合には、それを「元に戻して」から最前面に表示する方が好ましい。この「最小化」されているかどうかを判定するためにはWin32 APIのIsIconic関数を、「元に戻す」ためにShowWindowAsync関数を使用する。なお、.NETプログラムからWin32 APIを呼び出す方法については、別稿「TIPS:Win32 APIやDLL関数を呼び出すには?」を参照していただきたい。

 Win32 APIを使ったウィンドウを最前面にする処理を、次に示すWakeupWindowメソッドにまとめた。このメソッドは、メイン・ウィンドウのハンドル(ウィンドウを識別するための番号)をパラメータに指定する仕様になっている。

// 外部プロセスのウィンドウを起動する
public static void WakeupWindow(IntPtr hWnd)
{
  // メイン・ウィンドウが最小化されていれば元に戻す
  if (IsIconic(hWnd))
  {
    ShowWindowAsync(hWnd, SW_RESTORE);
  }

  // メイン・ウィンドウを最前面に表示する
  SetForegroundWindow(hWnd);
}
// 外部プロセスのメイン・ウィンドウを起動するためのWin32 API
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd,int nCmdShow);
[DllImport("user32.dll")]
private static extern bool IsIconic(IntPtr hWnd);
// ShowWindowAsync関数のパラメータに渡す定義値
private const int SW_RESTORE = 9;  // 画面を元の大きさに戻す
別プロセスのWindowsアプリケーションを最前面に表示するメソッド(C#)
 
' 外部プロセスのウィンドウを起動する
Public Shared Function WakeupWindow(ByVal hWnd As IntPtr)
  ' メイン・ウィンドウが最小化されていれば元に戻す
  If IsIconic(hWnd) Then
    ShowWindowAsync(hWnd, SW_RESTORE)
  End If

  ' メイン・ウィンドウを最前面に表示する
  SetForegroundWindow(hWnd)
End Function
' 外部プロセスのメイン・ウィンドウを起動するためのWin32 API
<DllImport("user32.dll")> _
Private Shared Function _
  SetForegroundWindow(ByVal hWnd As IntPtr) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function _
  ShowWindowAsync(ByVal hWnd As IntPtr, ByVal nCmdShow As Integer) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function _
  IsIconic(ByVal hWnd As IntPtr) As Boolean
End Function
' ShowWindowAsync関数のパラメータに渡す定義値
Private Const SW_RESTORE As Integer = 9 ' 画面を元の大きさに戻す
別プロセスのWindowsアプリケーションを最前面に表示するメソッド(VB.NET)

 このメソッドを使えば、別プロセスのWindowsアプリケーションを最前面に表示できる。しかしこのメソッドを実行するには、メソッドのパラメータに別プロセスのメイン・ウィンドウのハンドルを指定する必要がある。よって次に、実行中の別プロセスを取得する方法を説明しよう。

実行中の別プロセスの取得

 別プロセスを取得するには、Processクラス(System.Diagnostics名前空間)のGetProcessesByNameメソッドを使って、すでに 実行しているWindowsアプリケーション(自分自身)と同じプロセス名を持ったプロセスを取得する。なお、GetProcessesByNameメソッドは現在実行中のプロセス(自分自身)も取得してしまうので、プロセスIDをチェックして自分自身のプロセスは排除し、別プロセスのみを取得する必要がある。

 ただし、これだけでは同じプロセス名を持ったまったく別のプロセスである場合もあり得るので、さらにProcessクラスのMainModule.FileNameプロパティから、そのプロセスのフルパス名を取得して、自分自身のそれと比較しなければならない。

 これらの処理をまとめたのが、次のGetPreviousProcessメソッドである。このメソッドは、別プロセスを戻り値として返す仕様になっている。

// 実行中の同じアプリケーションのプロセスを取得する
public static Process GetPreviousProcess()
{
  Process curProcess = Process.GetCurrentProcess();
  Process[] allProcesses = Process.GetProcessesByName (curProcess.ProcessName);

  foreach (Process checkProcess in allProcesses)
  {
    // 自分自身のプロセスIDは無視する
    if (checkProcess.Id != curProcess.Id)
    {
      // プロセスのフルパス名を比較して同じアプリケーションか検証
      if (String.Compare(
          checkProcess.MainModule.FileName,
          curProcess.MainModule.FileName, true) == 0)
      {
        // 同じフルパス名のプロセスを取得
        return checkProcess;
      }
    }
  }

  // 同じアプリケーションのプロセスが見つからない!
  return null;
}
別プロセスを取得するメソッド(C#)
 
' 実行中の同じアプリケーションのプロセスを取得する
Public Shared Function GetPreviousProcess() As Process
  Dim curProcess As Process = Process.GetCurrentProcess()
  Dim allProcesses() As Process = Process.GetProcessesByName(curProcess.ProcessName)

  Dim checkProcess As Process
  For Each checkProcess In allProcesses
    ' 自分自身のプロセスIDは無視する
    If checkProcess.Id <> curProcess.Id Then
      ' プロセスのフルパス名を比較して同じアプリケーションか検証
      If String.Compare( _
          checkProcess.MainModule.FileName, _
          curProcess.MainModule.FileName, True) = 0 Then
        ' 同じフルパス名のプロセスを取得
        Return checkProcess
      End If
    End If
  Next

  ' 同じアプリケーションのプロセスが見つからない!
  Return Nothing
End Function
別プロセスを取得するメソッド(VB.NET)

 以上の2つのメソッドを使えば、実行中の別プロセスのメイン・ウィンドウを最前面に表示することができる。

多重起動禁止時に実行中のWindowsアプリケーションを最前面に表示するサンプル

 具体的には、次のようにGetPreviousProcessメソッドを使って別プロセスを取得し、そのプロセスのメイン・ウィンドウ・ハンドル(MainWindowHandleプロパティ)をパラメータに指定してWakeupWindowメソッドを呼び出せばよい。

Process prevProcess = GetPreviousProcess();
if (prevProcess != null) {
  WakeupWindow(prevProcess.MainWindowHandle);
}

 この処理を「TIPS:Windowsアプリケーションの多重起動を禁止するには?」のサンプル・プログラムに組み込んだ改訂版のサンプル・プログラムを以下に用意した。

  • サンプル・プログラム(C#:winshow.cs)のダウンロード
  • サンプル・プログラム(VB.NET:winshow.vb)のダウンロード

 なお、Windows XPのFast User Switching機能による複数のユーザー環境が同時に実行されているときの多重起動禁止の場合(つまり、グローバル・ミューテックスによるエラーの場合)、当然ながら別ユーザー環境のWindowsアプリケーションを最前面に表示することはできない。よって、グローバル・ミューテックスのエラーによる警告メッセージはWakeupWindowメソッドに置き換えずそのままにしておき、グローバル・ミューテックス以外の多重起動の場合だけWakeupWindowメソッドを呼び出す方が望ましい。先ほど挙げたサンプル・プログラムはそのように実装している。End of Article

カテゴリ:Windowsフォーム 処理対象:スレッド
使用ライブラリ:Formクラス(System.Windows.Forms名前空間)
使用ライブラリ:Processクラス(System.Diagnostics名前空間)
関連TIPS:Windowsアプリケーションの多重起動を禁止するには?
関連TIPS:Win32 APIやDLL関数を呼び出すには?
 
この記事と関連性の高い別の.NET TIPS
プロセス情報を名前を基に取得するには?
デスクトップ上のすべてのメイン・ウィンドウを列挙するには?
外部プログラム実行時に処理が固まる場合には?
ウィンドウを常に最前面に表示するには?
DataGridコントロールを使用したフォームを常に最前面に表示するには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間