【VBA×WindowsAPI】FindWindow関数を部分一致に対応させる
Windows APIのFindWindow関数を使うと、指定したウィンドウのハンドルを取得できます。このハンドルを利用することで、ウィンドウを最前面に表示したり、透過させたりすることが可能です。この関数は、ウィンドウのクラス名とウィンドウ名を指定することで対象のウィンドウのハンドルを取得することができます。ただし、これらの値は完全に一致している必要があります。
すでにFindWindow関数を利用したことがある方はご存じだと思いますが、部分一致で検索したいと思う場面は少なくありません。たとえば、あるアプリケーションのハンドルを取得したい場合、そのタイトルに開いているファイル名が含まれるケースがあります。このような場合、動的に一致する文字列を生成しなければならずかなり煩わしいですが、FindWindow関数だけでなくそれ以外のWindows APIの関数も併用することでこの問題を解決することができます。
部分一致FindWindow関数
FindWindow関数を部分一致に対応させる方法は非常にシンプルです。FindWindow関数で取得できるのは最上位の親ウィンドウなので、すべての最上位ウィンドウのクラス名とウィンドウ名を順に探索し、入力値と部分一致するかを判定して条件に合致するウィンドウを取得するだけです。
下記のWindows API関数を使うことで部分一致に対応したFindWindow関数を作成することができます。それぞれの関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。
FindWindow関数 :ウィンドウのハンドルを取得する
IsWindowVisible関数 :ウィンドウが可視状態であるかを判定する
GetClassName関数 :ウィンドウのクラス名を取得する
GetWindowText関数 :ウィンドウのウィンドウ名(タイトル)を取得する
GetNextWindow関数 :指定のウィンドウの次にあるウィンドウのハンドルを取得する
最上位の親ウィンドウを探索してウィンドウ名をすべて取得する方法は下記で解説しているので合わせて参照ください。後述のサンプルコードは本内容の処理に少し処理を追加しただけのものです。
サンプルコード
FindWindow関数を部分一致対応させた関数は下記の通りです。FindWindow関数の代わりにFindWindowPartialMatch関数を利用することで、クラス名とウィンドウ名を部分一致で探索することができます。Optional引数の値を切り替えることで部分一致と完全一致を切り替えることも可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
Option Explicit Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr Private Declare PtrSafe Function IsWindowVisible Lib "user32" (ByVal hWnd As LongPtr) As Long Private Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hWnd As LongPtr, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long Private Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As LongPtr, ByVal lpString As String, ByVal cch As Long) As Long Private Declare PtrSafe Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hWnd As LongPtr, ByVal wFlag As Long) As LongPtr Private Const GW_HWNDLAST = 1 Private Const GW_HWNDNEXT = 2 Sub main() Dim hWnd As LongPtr 'クラス名、ウィンドウ名それぞれが部分一致 hWnd = FindWindowPartialMatch("ClassName", "WindowName") 'ウィンドウ名が部分一致 hWnd = FindWindowPartialMatch(vbNullString, "WindowName") 'クラス名が完全一致 + ウィンドウ名が部分一致 hWnd = FindWindowPartialMatch("ClassName", "WindowName", True, False) 'クラス名、ウィンドウ名それぞれが完全一致 (FindWindow関数と同じ) hWnd = FindWindowPartialMatch("ClassName", "WindowName", False, False) End Sub Private Function FindWindowPartialMatch(ByVal sClassName As String, _ ByVal sWindowName As String, _ Optional flgPartialMatchClsName = True, _ Optional flgPartialMatchWinName = True) As LongPtr Dim hWnd As LongPtr Dim sBuf As String * 256 Dim sWin As String Dim sCls As String Dim IsClsNameOK As Boolean Dim IsWinNameOK As Boolean hWnd = FindWindow(vbNullString, vbNullString) Do If IsWindowVisible(hWnd) Then IsClsNameOK = False IsWinNameOK = False 'クラス名取得 sBuf = vbNullString Call GetClassName(hWnd, sBuf, Len(sBuf)) sCls = Left(sBuf, InStr(sBuf, vbNullChar) - 1) 'ウィンドウ名取得 sBuf = vbNullString Call GetWindowText(hWnd, sBuf, Len(sBuf)) sWin = Left(sBuf, InStr(sBuf, vbNullChar) - 1) 'クラス名判定 If sClassName = "" Then IsClsNameOK = True Else If flgPartialMatchClsName Then If InStr(sCls, sClassName) > 0 Then IsClsNameOK = True Else If sCls = sClassName Then IsClsNameOK = True End If End If 'ウィンドウ名判定 If sWindowName = "" Then IsWinNameOK = True Else If flgPartialMatchWinName Then If InStr(sWin, sWindowName) > 0 Then IsWinNameOK = True Else If sWin = sWindowName Then IsWinNameOK = True End If End If If IsClsNameOK And IsWinNameOK Then FindWindowPartialMatch = hWnd Exit Function End If End If hWnd = GetNextWindow(hWnd, GW_HWNDNEXT) Loop Until hWnd = GetNextWindow(hWnd, GW_HWNDLAST) End Function |
上記コードではGetNextWindow関数で次のウィンドウを取得していますが、FindWindowEx関数でも同等の操作を行うことができます。
関連情報
VBA×WindowsAPIまとめページ
その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。
参考
Microsoft公式:FindWindowA 関数 (winuser.h) – Win32 apps