【VBA×WindowsAPI】マウスカーソル位置にあるウィンドウのハンドルを取得する
WindowsAPIを使ってウィンドウの操作を行うとき、多くの場合はウィンドウハンドルを取得する必要があります。ウィンドウハンドルを取得するための関数は様々存在しますが、「指定のウィンドウ内にある特定の子ウィンドウのハンドルをパッと調べたい」という場合にそれを解決する関数は存在しません。ただいくつかの関数を組み合わせれば、このようなニーズに応えることができます。そこで、ここではマウスカーソルの下にあるウィンドウのハンドルを取得する方法を解説していきます。
カーソル位置にあるウィンドウのハンドルを取得
ウィンドウにはそれぞれを識別するためにウィンドウハンドルというものが割り当てられています。WindowsAPIではこのウィンドウハンドルを使うことで、指定のウィンドウに対して描画設定を変更したりウィンドウタイトルを取得したりと様々な操作を行うことが可能になっています。
VBAでウィンドウの操作を行う場合も基本的にはWindowsAPIを使う必要があるため、このウィンドウハンドルを取得する必要があります。WindowsAPIにはウィンドウのキャプションやクラス名から指定のウィンドウハンドルを取得するFindWindow関数や、現在アクティブなウィンドウのハンドルを取得するGetActiveWindow関数などウィンドウハンドルを取得するための関数がいくつも用意されているため状況や目的に合わせて使い分ける必要があります。
現在のカーソル位置にあるウィンドウのハンドルを取得するには下記のWindowsAPIを利用します。
それぞれ関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。
GetCursorPos関数 :カーソル位置の座標を取得する(スクリーン座標基準)
CopyMemory関数 :指定のメモリ領域を別の領域にコピーする
「そもそもWindowsAPIって何?」という方はコチラ(メインページ)も併せて参照下さい。
サンプルコード
現在のカーソル位置にあるウィンドウのハンドルを取得するためのサンプルコードは下記の通りです。下記コードではGetWindowText関数とGetClassName関数を使い、ウィンドウハンドルだけでなくウィンドウのタイトルとクラス名もあわせて取得し、イミディエイトウィンドウに出力しています。
注意点として下記コードはカーソルの動きを常に監視するためにDo~Loop関数で無限ループを発生させています。Doeventsを入れているので停止ボタンを押せばいつでも止められますが、ループを抜け出す処理は入れていないので実際にマクロに組み込む場合は何らかの終了条件の追加が必要です。
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 |
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 |
コード解説
現在のカーソル座標を取得
カーソル位置にあるウィンドウのハンドルを取得するにはまず、カーソルがスクリーンの”どの位置”に存在しているのか、つまりは座標を取得する必要があります。ここでいう座標とはスクリーンの左上を原点として横軸をX座標(右方向が+)、縦軸をY座標(下方向が+)としたスクリーン座標です。
Windows APIで2次元の座標を扱う場合は、X座標値とY座標値の2つの要素を持つPOINTAPI構造体を使うことが一般的です。この構造体はVBAには用意されていないのでTypeステートメントを使って下記のように記載し、ユーザー定義型として明示的に定義する必要があります。
Private Type POINTAPI
x As Long
y As Long
End Type
現在のカーソル位置の座標を取得するにはGetCursorPos関数を使い下記のように記載します。引数にユーザー定義型(As POINTAPI)で宣言された変数を入力することで、その変数(構造体)内に現在のカーソル位置のスクリーン座標を取得することができます。
Call GetCursorPos(tPt)
カーソル座標地点にあるウィンドウのハンドルを取得
現在のカーソル位置のスクリーン座標をPOINTAPI構造体に格納することが出来たら、WindowFromPoint関数を使ってその座標地点にあるウィンドウのハンドルを取得します。
hWnd = WindowFromPoint(llPtr)
Win64環境ではWindowFromPoint関数の引数にはPOINTAPI構造体をLongLong型に変換したものを入力する必要があります。このPOINTAPI構造体→LongLong型の変換はCopyMemory関数を使って行います。少し難しい内容ですが事前にLongLong型のメモリ領域を確保しておき、その領域にPOINTAPI構造体のメモリをコピーすることで型の変換を行うことができます。この方法はMicrosoft提供のWin32API_PtrSafe.txtにもそのまま記載されている手法です。
関連情報
VBA×WindowsAPIまとめページ
その他のWindowsAPI関数は下記ページにまとまっているので合わせて参照下さい。
参考
Microsoft公式:WindowFromPoint 関数 (winuser.h) – Win32 apps