【VBA×WindowsAPI】FindWindow関数を部分一致に対応させる

Windows APIのFindWindow関数を使うと、指定したウィンドウのハンドルを取得できます。このハンドルを利用することで、ウィンドウを最前面に表示したり、透過させたりすることが可能です。この関数は、ウィンドウのクラス名とウィンドウ名を指定することで対象のウィンドウのハンドルを取得することができます。ただし、これらの値は完全に一致している必要があります。

すでにFindWindow関数を利用したことがある方はご存じだと思いますが、部分一致で検索したいと思う場面は少なくありません。たとえば、あるアプリケーションのハンドルを取得したい場合、そのタイトルに開いているファイル名が含まれるケースがあります。このような場合、動的に一致する文字列を生成しなければならずかなり煩わしいですが、FindWindow関数だけでなくそれ以外のWindows APIの関数も併用することでこの問題を解決することができます

部分一致FindWindow関数

FindWindow関数を部分一致に対応させる方法は非常にシンプルです。FindWindow関数で取得できるのは最上位の親ウィンドウなので、すべての最上位ウィンドウのクラス名とウィンドウ名を順に探索し、入力値と部分一致するかを判定して条件に合致するウィンドウを取得するだけです。

下記のWindows API関数を使うことで部分一致に対応したFindWindow関数を作成することができます。それぞれの関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。

icon-check-square FindWindow関数          :ウィンドウのハンドルを取得する
icon-check-square IsWindowVisible関数  :ウィンドウが可視状態であるかを判定する
icon-check-square GetClassName関数     :ウィンドウのクラス名を取得する
icon-check-square GetWindowText関数      :ウィンドウのウィンドウ名(タイトル)を取得する
icon-check-square GetNextWindow関数     :指定のウィンドウの次にあるウィンドウのハンドルを取得する

最上位の親ウィンドウを探索してウィンドウ名をすべて取得する方法は下記で解説しているので合わせて参照ください。後述のサンプルコードは本内容の処理に少し処理を追加しただけのものです。

サンプルコード

FindWindow関数を部分一致対応させた関数は下記の通りです。FindWindow関数の代わりにFindWindowPartialMatch関数を利用することで、クラス名とウィンドウ名を部分一致で探索することができます。Optional引数の値を切り替えることで部分一致と完全一致を切り替えることも可能です。

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関数でも同等の操作を行うことができます。

関連情報

icon-share-square VBA×WindowsAPIまとめページ

その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。

icon-share-square 参考

Microsoft公式:FindWindowA 関数 (winuser.h) – Win32 apps

Excel,VBA,Windows API