ここでは、ウィンドウのタイトルバーに表示されているテキスト(タイトル、キャプション)、あるいは、ウィンドウが属しているクラス名から、そのウィンドウを作成したプロセスを探す方法を紹介します。
まず、「画面上のすべてのウィンドウとそのタイトルを列挙する」で紹介した方法でウィンドウをすべて列挙して、その中から条件にあったウィンドウを探すという方法が考えられます。
以下の例では、プロセスのメインウィンドウを列挙して、その中から指定された文字列をウィンドウタイトルに含んでいるプロセスを取得しています。
''' <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
/// <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を使ってすべてのウィンドウを列挙して、その中から探す方法でやってみましょう。ウィンドウを見つけたら、GetWindowThreadProcessIdを使ってそのウィンドウを作成したプロセスのIDを取得します。
以下に、ウィンドウに「メモ帳」という文字列を含んでいるプロセスをすべて列挙するコンソールアプリケーションの例を示します。この例では「GetProcessesByWindow」というメソッドを作成して、使用しています。このGetProcessesByWindowメソッドは、指定された文字列をウィンドウのタイトルとクラス名(あるいは、どちらか一方)に含んでいるプロセスをすべて取得できます。
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
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を使うと、指定された文字列と一致するクラス名とウィンドウのタイトル(どちらか一方だけでも可)を持つトップレベルウィンドウ(親を持たないウィンドウ)を探すことができます。
補足:子ウィンドウを検索する場合は、FindWindowExを使用します。
クラス名とウィンドウ名は部分一致ではなく、完全に一致する必要があります。そのため、使い勝手はあまり良くありません。
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
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); }
注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。