C#(.net)で他のプロセスのメインウィンドウハンドルを取得する

打鍵のtomo2.0で二重起動チェックを行っているんだけど,そのとき,メッセージを表示するだけでなく,すでに起動済みのプロセスのウィンドウを前面に持ってくるための処理を入れるときにちょっと手こずった.

C#で同じ名前のプロセスを取得するために以下のような処理を入れることで,起動中のプロセスのSystem.Diagnostics.Processインスタンスを取得することができる.

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) 
    {
      string prev = checkProcess.MainModule.FileName;
      string cur = curProcess.MainModule.FileName;
      if (String.Compare(prev, cur, true) == 0)
      {
        // 起動済みで同じフルパス名のプロセスを取得
        return checkProcess;
      }
    }  
  }
  return null;  
}

そしたら,この関数が返してくれるProcessインスタンスのメンバであるProcess.MainWindowHandleを見ればウィンドウハンドルがすぐ取得できるではないかと実際にやってみるとゼロが返ってくる.MSDNではメインウィンドウを持たない場合はゼロが返ると書いてあるが,打鍵のtomoは明らかにメインウィンドウを持っている.
あれ?と思ってググってみたらすぐに答えが見つかった.タスクバーに表示していない場合はゼロが返ってくるらしい.
Process.MainWindowHandle は ShowInTaskbar が無効だとゼロを返す
なんでこんな仕様になっているのだろう.結局生のEnumWindowsを使ってウィンドウを列挙してそのプロセスIDが一致するかどうかをチェックしてやらないといけない.

public class MultipleBootCheck
{
// 外部プロセスのメイン・ウィンドウを起動するための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);

[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, ref uint procId);

// コールバックメソッドのデリゲート
private delegate int EnumerateWindowsCallback(IntPtr hWnd, int lParam);

[DllImport("user32", EntryPoint = "EnumWindows")]
private static extern int EnumWindows(EnumerateWindowsCallback lpEnumFunc, int lParam);

private static Process target_proc = null;
private static IntPtr target_hwnd = IntPtr.Zero;

// ウィンドウを列挙するためのコールバックメソッド
public static int EnumerateWindows(IntPtr hWnd, int lParam)
{
  uint procId = 0;
  uint result = GetWindowThreadProcessId(hWnd, ref procId);
  if (procId == target_proc.Id)
  {
    // 同じIDで複数のウィンドウが見つかる場合がある
    // とりあえず最初のウィンドウが見つかった時点で終了する
    target_hwnd = hWnd;
    return 0;
  }

  // 列挙を継続するには0以外を返す必要がある
  return 1;
}

// 外部プロセスのウィンドウを最前面に表示する
public static void WakeupWindow(Process target)
{
  target_proc = target;
  EnumWindows(new EnumerateWindowsCallback(EnumerateWindows), 0);
  if (target_hwnd == IntPtr.Zero)
  {
      return;
  }
  // メイン・ウィンドウが最小化されていれば元に戻す
  if (IsIconic(target_hwnd))
  {
    ShowWindowAsync(target_hwnd, SW_RESTORE);
  }
  // メイン・ウィンドウを最前面に表示する
  SetForegroundWindow(target_hwnd);
}
}