DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

ウィンドウのタイトルからプロセスを探す

ここでは、ウィンドウのタイトルバーに表示されているテキスト(タイトル、キャプション)、あるいは、ウィンドウが属しているクラス名から、そのウィンドウを作成したプロセスを探す方法を紹介します。

Process.MainWindowTitleを使用する方法

まず、「画面上のすべてのウィンドウとそのタイトルを列挙する」で紹介した方法でウィンドウをすべて列挙して、その中から条件にあったウィンドウを探すという方法が考えられます。

以下の例では、プロセスのメインウィンドウを列挙して、その中から指定された文字列をウィンドウタイトルに含んでいるプロセスを取得しています。

VB.NET
コードを隠すコードを選択
''' <summary>
''' 指定された文字列を含むウィンドウタイトルを持つプロセスを取得します。
''' </summary>
''' <param name="windowTitle">ウィンドウタイトルに含む文字列。</param>
''' <returns>該当するプロセスの配列。</returns>
Public Function GetProcessesByWindowTitle(windowTitle As String) As  _
        System.Diagnostics.Process()
    Dim list As New System.Collections.ArrayList()

    'すべてのプロセスを列挙する
    Dim p As System.Diagnostics.Process
    For Each p In System.Diagnostics.Process.GetProcesses()
        '指定された文字列がメインウィンドウのタイトルに含まれているか調べる
        If 0 <= p.MainWindowTitle.IndexOf(windowTitle) Then
            '含まれていたら、コレクションに追加
            list.Add(p)
        End If
    Next

    'コレクションを配列にして返す
    Return DirectCast(list.ToArray(GetType(System.Diagnostics.Process)),  _
        System.Diagnostics.Process())
End Function
C#
コードを隠すコードを選択
/// <summary>
/// 指定された文字列を含むウィンドウタイトルを持つプロセスを取得します。
/// </summary>
/// <param name="windowTitle">ウィンドウタイトルに含む文字列。</param>
/// <returns>該当するプロセスの配列。</returns>
public System.Diagnostics.Process[] GetProcessesByWindowTitle(
    string windowTitle)
{
    System.Collections.ArrayList list = new System.Collections.ArrayList();

    //すべてのプロセスを列挙する
    foreach (System.Diagnostics.Process p
        in System.Diagnostics.Process.GetProcesses())
    {
        //指定された文字列がメインウィンドウのタイトルに含まれているか調べる
        if (0 <= p.MainWindowTitle.IndexOf(windowTitle))
        {
            //含まれていたら、コレクションに追加
            list.Add(p);
        }
    }

    //コレクションを配列にして返す
    return (System.Diagnostics.Process[])
        list.ToArray(typeof(System.Diagnostics.Process));
}

EnumWindowsを使用する方法

次は、EnumWindowsを使ってすべてのウィンドウを列挙して、その中から探す方法でやってみましょう。ウィンドウを見つけたら、GetWindowThreadProcessIdを使ってそのウィンドウを作成したプロセスのIDを取得します。

以下に、ウィンドウに「メモ帳」という文字列を含んでいるプロセスをすべて列挙するコンソールアプリケーションの例を示します。この例では「GetProcessesByWindow」というメソッドを作成して、使用しています。このGetProcessesByWindowメソッドは、指定された文字列をウィンドウのタイトルとクラス名(あるいは、どちらか一方)に含んでいるプロセスをすべて取得できます。

VB.NET
コードを隠すコードを選択
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Diagnostics
Imports System.Collections

Public Class Program
    ''' <summary>
    ''' エントリポイント
    ''' </summary>
    Public Shared Sub Main()
        'ウィンドウのタイトルに「メモ帳」を含むプロセスをすべて取得する
        Dim ps As Process() = GetProcessesByWindow("メモ帳", Nothing)

        '結果を表示する
        Dim p As Process
        For Each p In ps
            Console.WriteLine("プロセス名:" & p.ProcessName)
            Console.WriteLine("プロセスID:" & p.Id)
            Console.WriteLine("ウィンドウタイトル:" & p.MainWindowTitle)
        Next

        Console.WriteLine("終了しました。")
        Console.ReadLine()
    End Sub

    ''' <summary>
    ''' 指定された文字列をウィンドウのタイトルとクラス名に含んでいるプロセスを
    ''' すべて取得する。
    ''' </summary>
    ''' <param name="windowText">ウィンドウのタイトルに含むべき文字列。
    ''' nullを指定すると、classNameだけで検索する。</param>
    ''' <param name="className">ウィンドウが属するクラス名に含むべき文字列。
    ''' nullを指定すると、windowTextだけで検索する。</param>
    ''' <returns>見つかったプロセスの配列。</returns>
    Public Shared Function GetProcessesByWindow(windowText As String, _
            className As String) As Process()
        '検索の準備をする
        foundProcesses = New ArrayList()
        foundProcessIds = New ArrayList()
        searchWindowText = windowText
        searchClassName = className

        'ウィンドウを列挙して、対象のプロセスを探す
        EnumWindows(New EnumWindowsDelegate(AddressOf EnumWindowCallBack), _
                    IntPtr.Zero)

        '結果を返す
        Return DirectCast(foundProcesses.ToArray(GetType(Process)), Process())
    End Function

    Private Shared searchWindowText As String = Nothing
    Private Shared searchClassName As String = Nothing
    Private Shared foundProcessIds As ArrayList = Nothing
    Private Shared foundProcesses As ArrayList = Nothing

    Private Delegate Function EnumWindowsDelegate(hWnd As IntPtr, _
        lparam As IntPtr) As Boolean

    <DllImport("user32.dll")> _
    Private Shared Function EnumWindows(lpEnumFunc As EnumWindowsDelegate, _
        lparam As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function GetWindowText(hWnd As IntPtr, _
        lpString As StringBuilder, nMaxCount As Integer) As Integer
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function GetWindowTextLength(hWnd As IntPtr) As Integer
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function GetClassName(hWnd As IntPtr, _
        lpClassName As StringBuilder, nMaxCount As Integer) As Integer
    End Function

    <DllImport("user32.dll", SetLastError:=True)> _
    Private Shared Function GetWindowThreadProcessId(hWnd As IntPtr, _
        ByRef lpdwProcessId As Integer) As Integer
    End Function

    Private Shared Function EnumWindowCallBack(hWnd As IntPtr, _
            lparam As IntPtr) As Boolean
        If Not (searchWindowText Is Nothing) Then
            'ウィンドウのタイトルの長さを取得する
            Dim textLen As Integer = GetWindowTextLength(hWnd)
            If textLen = 0 Then
                '次のウィンドウを検索
                Return True
            End If
            'ウィンドウのタイトルを取得する
            Dim tsb As New StringBuilder(textLen + 1)
            GetWindowText(hWnd, tsb, tsb.Capacity)
            'タイトルに指定された文字列を含むか
            If tsb.ToString().IndexOf(searchWindowText) < 0 Then
                '含んでいない時は、次のウィンドウを検索
                Return True
            End If
        End If

        If Not (searchClassName Is Nothing) Then
            'ウィンドウのクラス名を取得する
            Dim csb As New StringBuilder(256)
            GetClassName(hWnd, csb, csb.Capacity)
            'クラス名に指定された文字列を含むか
            If csb.ToString().IndexOf(searchClassName) < 0 Then
                '含んでいない時は、次のウィンドウを検索
                Return True
            End If
        End If

        'プロセスのIDを取得する
        Dim processId As Integer
        GetWindowThreadProcessId(hWnd, processId)
        '今まで見つかったプロセスでは無いことを確認する
        If Not foundProcessIds.Contains(processId) Then
            foundProcessIds.Add(processId)
            'プロセスIDをからProcessオブジェクトを作成する
            foundProcesses.Add(Process.GetProcessById(processId))
        End If

        '次のウィンドウを検索
        Return True
    End Function
End Class
C#
コードを隠すコードを選択
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Diagnostics;
using System.Collections;

public class Program
{
    /// <summary>
    /// エントリポイント
    /// </summary>
    public static void Main()
    {
        //ウィンドウのタイトルに「メモ帳」を含むプロセスをすべて取得する
        Process[] ps = GetProcessesByWindow("メモ帳", null);

        //結果を表示する
        foreach (Process p in ps)
        {
            Console.WriteLine("プロセス名:" + p.ProcessName);
            Console.WriteLine("プロセスID:" + p.Id);
            Console.WriteLine("ウィンドウタイトル:" + p.MainWindowTitle);
        }

        Console.WriteLine("終了しました。");
        Console.ReadLine();
    }

    /// <summary>
    /// 指定された文字列をウィンドウのタイトルとクラス名に含んでいるプロセスを
    /// すべて取得する。
    /// </summary>
    /// <param name="windowText">ウィンドウのタイトルに含むべき文字列。
    /// nullを指定すると、classNameだけで検索する。</param>
    /// <param name="className">ウィンドウが属するクラス名に含むべき文字列。
    /// nullを指定すると、windowTextだけで検索する。</param>
    /// <returns>見つかったプロセスの配列。</returns>
    public static Process[] GetProcessesByWindow(
        string windowText, string className)
    {
        //検索の準備をする
        foundProcesses = new ArrayList();
        foundProcessIds = new ArrayList();
        searchWindowText = windowText;
        searchClassName = className;

        //ウィンドウを列挙して、対象のプロセスを探す
        EnumWindows(new EnumWindowsDelegate(EnumWindowCallBack), IntPtr.Zero);

        //結果を返す
        return (Process[])foundProcesses.ToArray(typeof(Process));
    }

    private static string searchWindowText = null;
    private static string searchClassName = null;
    private static ArrayList foundProcessIds = null;
    private static ArrayList foundProcesses = null;

    private delegate bool EnumWindowsDelegate(IntPtr hWnd, IntPtr lparam);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private extern static bool EnumWindows(EnumWindowsDelegate lpEnumFunc,
        IntPtr lparam);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int GetWindowText(IntPtr hWnd,
        StringBuilder lpString, int nMaxCount);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int GetWindowTextLength(IntPtr hWnd);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int GetClassName(IntPtr hWnd,
        StringBuilder lpClassName, int nMaxCount);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern int GetWindowThreadProcessId(
        IntPtr hWnd, out int lpdwProcessId);

    private static bool EnumWindowCallBack(IntPtr hWnd, IntPtr lparam)
    {
        if (searchWindowText != null)
        {
            //ウィンドウのタイトルの長さを取得する
            int textLen = GetWindowTextLength(hWnd);
            if (textLen == 0)
            {
                //次のウィンドウを検索
                return true;
            }
            //ウィンドウのタイトルを取得する
            StringBuilder tsb = new StringBuilder(textLen + 1);
            GetWindowText(hWnd, tsb, tsb.Capacity);
            //タイトルに指定された文字列を含むか
            if (tsb.ToString().IndexOf(searchWindowText) < 0)
            {
                //含んでいない時は、次のウィンドウを検索
                return true;
            }
        }

        if (searchClassName != null)
        {
            //ウィンドウのクラス名を取得する
            StringBuilder csb = new StringBuilder(256);
            GetClassName(hWnd, csb, csb.Capacity);
            //クラス名に指定された文字列を含むか
            if (csb.ToString().IndexOf(searchClassName) < 0)
            {
                //含んでいない時は、次のウィンドウを検索
                return true;
            }
        }

        //プロセスのIDを取得する
        int processId;
        GetWindowThreadProcessId(hWnd, out processId);
        //今まで見つかったプロセスでは無いことを確認する
        if (!foundProcessIds.Contains(processId))
        {
            foundProcessIds.Add(processId);
            //プロセスIDをからProcessオブジェクトを作成する
            foundProcesses.Add(Process.GetProcessById(processId));
        }

        //次のウィンドウを検索
        return true;
    }
}

Win32 APIのFindWindowを使用した方法

Win32 APIのFindWindowを使うと、指定された文字列と一致するクラス名とウィンドウのタイトル(どちらか一方だけでも可)を持つトップレベルウィンドウ(親を持たないウィンドウ)を探すことができます。

補足:子ウィンドウを検索する場合は、FindWindowExを使用します。

クラス名とウィンドウ名は部分一致ではなく、完全に一致する必要があります。そのため、使い勝手はあまり良くありません。

VB.NET
コードを隠すコードを選択
Imports System.Runtime.InteropServices
Imports System.Diagnostics

Public Class Program
    ''' <summary>
    ''' エントリポイント
    ''' </summary>
    Public Shared Sub Main()
        'タイトルが"無題 - メモ帳"のウィンドウを探す
        Dim hWnd As IntPtr = FindWindow(Nothing, "無題 - メモ帳")
        If hWnd <> IntPtr.Zero Then
            'ウィンドウを作成したプロセスのIDを取得する
            Dim processId As Integer
            GetWindowThreadProcessId(hWnd, processId)
            'Processオブジェクトを作成する
            Dim p As Process = Process.GetProcessById(processId)

            Console.WriteLine("プロセス名:" & p.ProcessName)
        Else
            Console.WriteLine("見つかりませんでした。")
        End If

        Console.ReadLine()
    End Sub

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Public Shared Function FindWindow(lpClassName As String, _
        lpWindowName As String) As IntPtr
    End Function

    <DllImport("user32.dll", SetLastError:=True)> _
    Public Shared Function GetWindowThreadProcessId(hWnd As IntPtr, _
        ByRef lpdwProcessId As Integer) As Integer
    End Function
End Class
C#
コードを隠すコードを選択
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;

public class Program
{
    /// <summary>
    /// エントリポイント
    /// </summary>
    public static void Main()
    {
        //タイトルが"無題 - メモ帳"のウィンドウを探す
        IntPtr hWnd = FindWindow(null, "無題 - メモ帳");
        if (hWnd != IntPtr.Zero)
        {
            //ウィンドウを作成したプロセスのIDを取得する
            int processId;
            GetWindowThreadProcessId(hWnd, out processId);
            //Processオブジェクトを作成する
            Process p = Process.GetProcessById(processId);

            Console.WriteLine("プロセス名:" + p.ProcessName);
        }
        else
        {
            Console.WriteLine("見つかりませんでした。");
        }

        Console.ReadLine();
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr FindWindow(
        string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern int GetWindowThreadProcessId(
        IntPtr hWnd, out int lpdwProcessId);
}
  • 履歴:
  • 2013/9/23 Process.MainWindowTitleを使用する方法で、指定された文字列がタイトルの先頭にあるとうまくいかない不具合を修正。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • .NET Tipsをご利用いただく際は、注意事項をお守りください。