【VBA×WindowsAPI】マウスカーソル位置にあるウィンドウのハンドルを取得する

WindowsAPIを使ってウィンドウの操作を行うとき、多くの場合はウィンドウハンドルを取得する必要があります。ウィンドウハンドルを取得するための関数は様々存在しますが、「指定のウィンドウ内にある特定の子ウィンドウのハンドルをパッと調べたい」という場合にそれを解決する関数は存在しません。ただいくつかの関数を組み合わせれば、このようなニーズに応えることができます。そこで、ここではマウスカーソルの下にあるウィンドウのハンドルを取得する方法を解説していきます。

カーソル位置にあるウィンドウのハンドルを取得

ウィンドウにはそれぞれを識別するためにウィンドウハンドルというものが割り当てられています。WindowsAPIではこのウィンドウハンドルを使うことで、指定のウィンドウに対して描画設定を変更したりウィンドウタイトルを取得したりと様々な操作を行うことが可能になっています。

VBAでウィンドウの操作を行う場合も基本的にはWindowsAPIを使う必要があるため、このウィンドウハンドルを取得する必要があります。WindowsAPIにはウィンドウのキャプションやクラス名から指定のウィンドウハンドルを取得するFindWindow関数や、現在アクティブなウィンドウのハンドルを取得するGetActiveWindow関数などウィンドウハンドルを取得するための関数がいくつも用意されているため状況や目的に合わせて使い分ける必要があります。

現在のカーソル位置にあるウィンドウのハンドルを取得するには下記のWindowsAPIを利用します。
それぞれ関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。

icon-check-square WindowFromPoint関数  :指定の座標にあるウィンドウのハンドルを取得する
icon-check-square GetCursorPos関数          :カーソル位置の座標を取得する(スクリーン座標基準)
icon-check-square CopyMemory関数         :指定のメモリ領域を別の領域にコピーする

「そもそもWindowsAPIって何?」という方はコチラ(メインページ)も併せて参照下さい。

サンプルコード

現在のカーソル位置にあるウィンドウのハンドルを取得するためのサンプルコードは下記の通りです。下記コードではGetWindowText関数GetClassName関数を使い、ウィンドウハンドルだけでなくウィンドウのタイトルとクラス名もあわせて取得し、イミディエイトウィンドウに出力しています。
注意点として下記コードはカーソルの動きを常に監視するためにDo~Loop関数で無限ループを発生させています。Doeventsを入れているので停止ボタンを押せばいつでも止められますが、ループを抜け出す処理は入れていないので実際にマクロに組み込む場合は何らかの終了条件の追加が必要です。

Option Explicit
Private Declare PtrSafe Function WindowFromPoint Lib "user32" (ByVal point As LongLong) As LongPtr
Private Declare PtrSafe Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr)
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 GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hWnd As LongPtr, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long

Private Type POINTAPI
    x As Long
    y As Long
End Type

'------------------------------------------------------------
'   メイン処理
'------------------------------------------------------------
Sub main()

    Dim hWnd    As LongPtr
    Dim tPt     As POINTAPI
    Dim llPtr   As LongLong
    Dim sBuf    As String * 256
    Dim sCap    As String
    Dim sCls    As String
    
    Do
        '現在のカーソル座標を取得
        Call GetCursorPos(tPt)
        
        'POINTAPI構造体をLongLong型に変換(※Win64)
        llPtr = PointToLongLong(tPt)
        
        'カーソル座標地点にあるウィンドウのハンドルを取得
        hWnd = WindowFromPoint(llPtr)
        
        If hWnd <> 0 Then
            
            'ウィンドウクラス名取得
            sBuf = vbNullString
            Call GetClassName(hWnd, sBuf, Len(sBuf))
            sCls = Left(sBuf, InStr(sBuf, vbNullChar) - 1)
            
            'ウィンドウテキスト(キャプション)取得
            sBuf = vbNullString
            Call GetWindowText(hWnd, sBuf, Len(sBuf))
            sCap = Left(sBuf, InStr(sBuf, vbNullChar) - 1)
            
            Debug.Print Hex(hWnd) & " | " & sCls & " | " & sCap
             
        End If
        DoEvents
    Loop

End Sub
'------------------------------------------------------------
'   POINTAPI構造体をLongLong型に変換する
'------------------------------------------------------------
Function PointToLongLong(point As POINTAPI) As LongLong

    Dim ll As LongLong
    Dim cbLongLong As LongPtr
    
    cbLongLong = LenB(ll)

    If LenB(point) = cbLongLong Then
        Call CopyMemory(ll, point, cbLongLong)
    End If
    
    PointToLongLong = ll
    
End Function

コード解説

icon-edit 現在のカーソル座標を取得

カーソル位置にあるウィンドウのハンドルを取得するにはまず、カーソルがスクリーンの"どの位置"に存在しているのか、つまりは座標を取得する必要があります。ここでいう座標とはスクリーンの左上を原点として横軸をX座標(右方向が+)、縦軸をY座標(下方向が+)としたスクリーン座標です。

Windows APIで2次元の座標を扱う場合は、X座標値とY座標値の2つの要素を持つPOINTAPI構造体を使うことが一般的です。この構造体はVBAには用意されていないのでTypeステートメントを使って下記のように記載し、ユーザー定義型として明示的に定義する必要があります。

icon-code POINTAPI構造体の定義 

Private Type POINTAPI

        x As Long

        y As Long

End Type

現在のカーソル位置の座標を取得するにはGetCursorPos関数を使い下記のように記載します。引数にユーザー定義型(As POINTAPI)で宣言された変数を入力することで、その変数(構造体)内に現在のカーソル位置のスクリーン座標を取得することができます。

icon-code GetCursorPos関数 

Call GetCursorPos(tPt)

 

icon-edit カーソル座標地点にあるウィンドウのハンドルを取得

現在のカーソル位置のスクリーン座標をPOINTAPI構造体に格納することが出来たら、WindowFromPoint関数を使ってその座標地点にあるウィンドウのハンドルを取得します。

icon-code WindowFromPoint関数 

hWnd = WindowFromPoint(llPtr)

Win64環境ではWindowFromPoint関数の引数にはPOINTAPI構造体をLongLong型に変換したものを入力する必要があります。このPOINTAPI構造体→LongLong型の変換はCopyMemory関数を使って行います。少し難しい内容ですが事前にLongLong型のメモリ領域を確保しておき、その領域にPOINTAPI構造体のメモリをコピーすることで型の変換を行うことができます。この方法はMicrosoft提供のWin32API_PtrSafe.txtにもそのまま記載されている手法です。

関連情報

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

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

icon-share-square 参考

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

Excel,VBA,Windows API