【VBA×WindowsAPI】指定のウィンドウの実行ファイルパスを取得する

Windows APIでは各ウィンドウに設定されているウィンドウハンドルを使ってウィンドウ操作を行います。このウィンドウハンドルは基本的に特定のウィンドウを指定して取得するものです。つまり、どのアプリケーションのウィンドウなのかを分かったうえで使うのが普通です。ただ、ハンドルだけ手元にあってこれがどのアプリケーションのものなのか知りたいという状況に出くわすこともあります。

ここではWindows APIを使って指定のウィンドウハンドルから実行ファイルのフルパスを取得する方法を解説していきます。これにより、たとえばGetForegroundWindow関数で取得した最前面ウィンドウがどのアプリケーションのウィンドウかを判定することができるようになります。

ウィンドウハンドルから実行ファイルを特定

Windows APIを使うことで、指定ウィンドウ(=プロセス)に紐づく実行ファイルのフルパスを取得できます。流れはシンプルで下記の通りです。ウィンドウはアプリケーションの最上位のウィンドウである必要はなく、FindWindowEx関数などで取得した子ウィンドウのハンドルでも問題ありません。

① GetWindowThreadProcessIdで hWndからプロセスID取得
② OpenProcessでプロセスIDの対象プロセスを開いてプロセスハンドルを取得
③ GetModuleFileNameExでメインモジュール(= 実行ファイル)のフルパスを取得
④ CloseHandleで後片付け

それぞれの関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。

icon-check-square GetWindowThreadProcessId関数 :指定ウィンドウを作成したプロセスの識別子を取得する
icon-check-square OpenProcess関数                               :指定プロセスを操作するためのハンドルを取得する
icon-check-square CloseHandle関数                                   :取得したハンドルを閉じてリソースを解放す
icon-check-square GetModuleFileNameExA関数          :指定プロセスの実行ファイルのパスを取得する

 

サンプルコード

下記は指定のウィンドウハンドルから実行ファイルのパスを取得するサンプルコードです。
コード内ではExcelのハンドル(Application.hWnd)を入力しているためそのまま実行すると「EXCEL.EXE」のフルパスが取得できます(結果はイミディエイトウィンドウに出力)。他アプリを対象にしたい場合はFindWindow関数などで取得したウィンドウハンドルを使用してください。

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 GetWindowThreadProcessId Lib "user32" (ByVal hWnd As LongPtr, lpdwProcessId As Long) As Long
Private Declare PtrSafe Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As LongPtr
Private Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As LongPtr) As Long
Private Declare PtrSafe Function GetModuleFileNameExA Lib "psapi" (ByVal hProcess As LongPtr, ByVal hModule As LongPtr, ByVal lpFileName As String, ByVal nSize As Long) As Long

Private Const PROCESS_QUERY_INFORMATION = &H400
Private Const PROCESS_VM_READ = &H10

Sub main()

    Dim hWnd As LongPtr
    Dim sPath As String

    hWnd = Application.hWnd
    sPath = GetAppPathFromHwnd(hWnd)
    Debug.Print sPath
    
End Sub
Private Function GetAppPathFromHwnd(ByVal hWnd As LongPtr) As String

    Dim lProcId As Long
    Dim hProc As LongPtr
    Dim sBuf As String * 260
    Dim lLen As Long

    'ウィンドウのプロセスIDを取得
    Call GetWindowThreadProcessId(hWnd, lProcId)
    If lProcId = 0 Then Exit Function

    'プロセスを開く
    hProc = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, 0, lProcId)
    If hProc <> 0 Then
        lLen = GetModuleFileNameExA(hProc, 0, sBuf, Len(sBuf))
        If lLen > 0 Then
            GetAppPathFromHwnd = Left$(sBuf, lLen)
        End If
        Call CloseHandle(hProc)
    End If
    
End Function

関連情報

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

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

icon-share-square 参考

Microsoft公式:GetWindowThreadProcessId 関数 (winuser.h) – Win32 apps
                                OpenProcess 関数 (processthreadsapi.h) – Win32 apps
                               
CloseHandle 関数 (handleapi.h) – Win32 apps
                                GetModuleFileNameExA 関数 (psapi.h) – Win32 apps

Excel,VBA,Windows API