ソフトウェア

VBAのユーザーフォームでハンドル情報を取得する方法[No19]

スポンサーリンク

動機

自宅にてアプリケーションの自動操作を行うプログラムを作成する機会がありました。

ほとんどのWindowsのアプリケーションは、管理番号がふられた部品で構成されています。

この管理番号はウィンドウハンドル(Handleまたはハンドル)と呼ばれており、"複数の情報から成る「ハンドル情報」"から取得することが可能です。

※「ハンドル情報」は「Window情報」と呼ばれることもあります。

アプリケーションの「ハンドル情報」を取得できれば、「ウィンドウハンドル」を取得でき、部品の操作が可能となります。コーディングする前にあらかじめ「ハンドル情報」を調べておく必要があります。

Visual StudioのSpy++ツールで「ハンドル情報」を取得することも可能ですが、そうするとVisual Studioをインストールしなくてはならなくなるし、無償で長く利用するにはMicroSoftアカウントでのログインが必要となって手間がかかってしまいます。そのようなときに次のサイトに出会いました。

出典

ウィンドウ情報列挙−VBパーツ
ウィンドウ情報列挙−VBパーツ

VisualBasicプログラムパーツ

続きを見る

この記事を書くにあたって、ダウンロードセンター様に掲載してあるツールが大変参考にさせて頂きました。ありがとうございます。記載できる範囲で引用にて紹介させて頂ければと思います。ダウンロードセンター様では、上記ツールの他にも色々なツールが紹介されているので、気になった方は是非チェックしてみて下さい。

ダウンロードセンター
ダウンロードセンター

続きを見る

スポンサーリンク

出典のツールを利用するために行ったこと

執筆時点では、私のPCではそのまま利用できませんでした。

VB6で開発されたツールのようで現在のVB7では上手くいかなかったです。

次の手順を行い動作することができました。

手順

私のWindows10 PCでは次のように手順を踏んで行いました。

手順1

次のサイトにあるソースを参考にするために、まずサンプルコードのダウンロードを行う。(VB6ソースのフォーム、標準モジュール)

ウィンドウ情報列挙−VBパーツ
ウィンドウ情報列挙−VBパーツ

VisualBasicプログラムパーツ

続きを見る

手順2

Excel(拡張子xlsm)を新規作成する。

※VBAの開発画面で「ヘルプ→バージョン情報」を確認してみると「Microsoft Visual Basic for Applications 7.1」となっていました。

手順3

ユーザーフォーム「UserForm1」を新規作成する。

フォームのデザインは手順1のサイトを参考に作成する。

※UserForm1の大枠サイズはHeight=400,Width=1050が良さそうです。

※作成はツールボックスだけで完結できます。ツールボックスにListViewがなければ、「その他のコントロール」から追加して使用して下さい。(Microsoft ListView Control, version 6.0)

手順4

UserForm1に、コマンドボタンをもう1つ作成する。

※この手順が完了すると、コマンドボタンは全部で2つになります。

手順5

UserForm1を、手順1で取得したコードを使用してVB6ソースのフォームとして上書きする。

手順6

標準モジュール「Module1」を新規作成する。

手順7

Module1を、手順1で取得したコードを使用してVB6ソースの標準モジュールとして上書きする。

手順8

もう1つの標準モジュール「Module2」を新規作成する。

手順9

Module2を次のコード(code_09-01)で上書きし、実行する。

※フォームが起動することを確認して下さい。


Option Explicit

Public Sub Open_form()
    With UserForm1
        .StartUpPosition = 0
        .Top = 100
        .Left = 100
        
        .Show vbModeless
    End With
End Sub

スポンサーリンク

手順10

フォーム起動後、手順1のサイトにある実行イメージのようになっておらず、ボタンを押しても反応がない場合は次のことを行う。

<UserForm1内のコードの変更>

  • Command1をCommandButton1に全て置換する。
  • Command2をCommandButton2に全て置換する。
  • Option1をOptionButton1に全て置換する。
  • Option2をOptionButton2に全て置換する。
  • Form_ResizeをUserForm_Resizeに置換する。
  • Form_LoadをUserForm_Initializeに置換する。
  • UserForm_Initializeで設定している書式設定で、上から順番に幅を変更する。
    ※変更する幅は画面サイズによります。
     例:110、110、110、150、150、300。
  • 次のコード(code_10-01)のように、Form_Unloadの関数一式は、次のUserForm_QueryCloseに変更する。
  • 次のコード(code_10-02)のように、UserForm_Resizeの関数内の「With」で挟まれた箇所を変更する。
  • 次のコード(code_10-03)の定義を一番上のOption Explicitの次の行に追加する。
  • 次のコード(code_10-04)の関数を追加する。

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    If CloseMode = 0 Then
        End
    End If
End Sub

            .Width = Me.InsideWidth - .Left * 2
            .Height = Me.InsideHeight - .Top * 2

Private Declare Function WindowFromAccessibleObject Lib "oleacc.dll" (ByVal IAcessible As Object, ByRef hWnd As Long) As Long

Public Property Get hWnd() As Long
    WindowFromAccessibleObject Me, hWnd
End Property

手順11

Module2のOpen_formを実行し、手順1のサイト先の実行イメージのようになることを確認する。

スポンサーリンク

手順12

ThisWorkbookを次のコード(code_12-01)で上書きすると、起動したときにフォームが立ち上がることを確認する。


Option Explicit

Private Sub Workbook_Open()
    UserForm1.Show vbModeless
End Sub

手順13(+αの手順:機能を追加)

追加で次のことを行うと利便性が向上します。

※ツールで表示されるHandleなどはHexの16進数となります。使用用途にあわせて10進も確認できるようにすると、より使いやすくなるのでおすすめです。

<マクロのExcelの設定>

1シート目に「Sheet1」、2シート目に「WriteSheet」、というシート名のシートを作成する。(後の処理で結構重要)

<UserForm1のデザイン>

  • UserForm1にコマンドボタンを3つ追加。追加場所はコマンドボタン2つ目の右隣。(この手順でコマンドボタンは合計5つになる。)
  • UserForm1にラベルを1つ追加。追加場所はラベル2つ目の右隣。(この手順でラベルは合計2つになる。)

<UserForm1内のコード>

  • 次のコード(code_13-01)を一番上のOption Explicitの次の行に追加。
  • CommandButton1_Clickの関数内(関数始め)に次のコード(code_13-02)を追加。
  • SetListの関数内でフォームの一行目に表示されるhandleをHex16進数にしているFormatの箇所を「hWnd」に置換。(後の処理で結構重要)
  • SetListの関数内(For文開始直後)に次のコード(code_13-03)を追加。
  • 次のコード(code_13-04)の関数を追加。
  • UserForm_Initializeの書式設定に次のコード(code_13-05)を追加。
  • UserForm_Initializeの関数内(関数終わり)に次のコード(code_13-06)を追加。

Private Declare Function ShowWindow Lib "user32" (ByVal hwindow As Long, ByVal cmdshow As Long) As Long

    If OptionButton2 Then
        CommandButton3.Enabled = True
    End If
    CommandButton4.Enabled = True
    CommandButton5.Enabled = True

Label2.Caption = "取得件数:" & UBound(hWndList) & "件"

Private Sub OptionButton1_Click()
    LoopSw = False
    ListView1.ListItems.Clear
    CommandButton2.Enabled = False
    CommandButton3.Enabled = False
    CommandButton4.Enabled = False
End Sub
 
Private Sub OptionButton2_Click()
    LoopSw = False
    ListView1.ListItems.Clear
    CommandButton2.Enabled = False
    CommandButton3.Enabled = False
    CommandButton4.Enabled = False
End Sub
 
'Flashボタン
Private Sub CommandButton3_Click()
    If OptionButton2 Then
        LoopSw = False
         
        Dim handle As Long
     
        With ListView1.SelectedItem
            If ListView1.SelectedItem Is Nothing Then
                Exit Sub
            End If
             
            handle = CLng(Trim((.Text)))
        End With
         
        ShowWindow handle, 0
        Application.Wait Now() + TimeValue("00:00:01")
        ShowWindow handle, 1
        Application.Wait Now() + TimeValue("00:00:01")
        ShowWindow handle, 0
        Application.Wait Now() + TimeValue("00:00:01")
        ShowWindow handle, 1
         
    End If
End Sub
 
'Line Copyボタン
Private Sub CommandButton4_Click()
        Dim handle      As Long
        Dim proccId     As String
        Dim threadId    As String
        Dim rect        As String
        Dim className   As String
        Dim winText     As String
 
        With ListView1.SelectedItem
            If ListView1.SelectedItem Is Nothing Then
                Exit Sub
            End If
             
            handle = CLng(Trim(.Text))
            proccId = .SubItems(1)
            threadId = .SubItems(2)
            rect = .SubItems(3)
            className = .SubItems(4)
            winText = .SubItems(5)
        End With
         
        Open ThisWorkbook.Path & "\LineCopy.txt" For Output As #1
        Print #1, "handle:"; "「" & handle & "」" & vbCrLf & _
                  "proccId:"; "「" & proccId & "」" & vbCrLf & _
                  "threadId:"; "「" & threadId & "」" & vbCrLf & _
                  "rect:"; "「" & rect & "」" & vbCrLf & _
                  "className:"; "「" & className & "」" & vbCrLf & _
                  "winText:"; "「" & winText & "」" & vbCrLf
        Close #1
End Sub
 
'All Copyボタン
Private Sub CommandButton5_Click()
        Dim handle      As String
        Dim proccId     As String
        Dim threadId    As String
        Dim rect        As String
        Dim className   As String
        Dim winText     As String
         
        If ListView1.ListItems.Count = 0 Then
            Exit Sub
        End If
         
        Dim workSheetName As String
        workSheetName = "WriteSheet"
         
        Sheets(workSheetName).Select
        Cells.Clear
         
        With Worksheets(workSheetName)
            .Range("A1") = "Handle"
            .Range("B1") = "ProccId"
            .Range("C1") = "ThreadId"
            .Range("D1") = "Rect"
            .Range("E1") = "ClassName"
            .Range("F1") = "WinText"
        End With
         
        With ListView1
            Dim i As Long
            For i = 1 To .ListItems.Count Step 1
                With .ListItems(i)
                    handle = .Text
                    proccId = .SubItems(1)
                    threadId = .SubItems(2)
                    rect = .SubItems(3)
                    className = .SubItems(4)
                    winText = .SubItems(5)
                End With
                With Worksheets(workSheetName)
                    .Range("A" & (1 + i)) = handle
                    .Range("B" & (1 + i)) = proccId
                    .Range("C" & (1 + i)) = threadId
                    .Range("D" & (1 + i)) = rect
                    .Range("E" & (1 + i)) = className
                    .Range("F" & (1 + i)) = winText
                End With
            Next i
        End With
         
        With Worksheets(workSheetName)
            .Columns("A:F").EntireColumn.AutoFit
        End With
         
        Dim selectedOptionButtonName
        If OptionButton1 Then
            selectedOptionButtonName = "親"
        ElseIf OptionButton2 Then
            selectedOptionButtonName = "子"
        End If
         
        Sheets(workSheetName).Copy
        Dim fileName As String
        fileName = "ウィンドウ情報(" & Format(Now(), "YYYY年MM月DD日") & "(" & Format(Now(), "aaa") & ")" & "_" & Format(Now(), "hh時nn分ss秒出力") & ")(" & selectedOptionButtonName & ")"
        ActiveWorkbook.SaveAs _
        fileName:=ThisWorkbook.Path & "\" & fileName, _
        FileFormat:=xlOpenXMLWorkbook, CreateBackup:=False
        ActiveWorkbook.Close
         
        Sheets(workSheetName).Select
        Cells.Clear
         
        Dim mainSheet As String
        mainSheet = "Sheet1"
        Sheets(mainSheet).Select
        ThisWorkbook.Save
End Sub

    CommandButton3.Caption = "Flash"
    CommandButton4.Caption = "Line Copy"
    CommandButton5.Caption = "All Copy"
    Label2.Caption = ""
    UserForm1.Caption = "Window情報取得ツール"

    With ListView1
        .Gridlines = True           'True:グリッド線追加
        .FullRowSelect = True       'FullRowSelect:1行選択
        .HideSelection = False      'False:選択行が常に強調表示
        .LabelEdit = lvwManual      'lvwManual:編集許可しない
        .AllowColumnReorder = True  'True:列幅変更許可する
    End With
    
    OptionButton1 = True

スポンサーリンク

完成イメージ

すべての手順を行うと次のフォームになるはずです。

スポンサーリンク

追加機能について

Flashボタン

Flashボタンは子ウィンドウ選択時のみ利用可能です。選択した子ウィンドウを点滅させ、場所を確認することができます。しかし、親ウィンドウ選択時にはFlashボタンは利用しないことを推奨します。間違って利用してしまった際は×ボタンで閉じないほうが良さそうです。

Line Copyボタン

実行Excelのファイルがある場所に、選択されている行の情報をテキストファイルで出力することができます。

All Copyボタン

実行Excelのファイルがある場所に、すべての行の情報をExcelファイルで出力する。

取得件数ラベル

一覧の取得件数を表示可能です。

ポイント

  • 子ウィンドウでは、アプリケーション(ソフト)のタイトル部分にマウスを当てておくと、子ウィンドウの情報が取得しやすい。
  • Line Copyで出力されたテキストファイルを開いた状態でLine Copyを押すとエラーになるので、開いたら閉じることを忘れずに!

追記

下記の記事で紹介している「要素確認ソフト(Inspect.exe)」を用いることでも確認することが可能なので、お時間のある方は是非こちらの記事も読んでみて頂けると嬉しいです。

no image
【簡単!UIAutomation利用】VBAツールで画面操作して自動ログインする[No88]
毎日のように、複数環境にログインして作業しているシステムがあります。 本番環境、開発環境のように環境ごとにホスト名、ユーザ名、パスワードが異なるため、これまでは手作業で切り替えて利用 ...

関連記事

no image
ウィンドウハンドル関連の記事一覧[No74]
記事一覧 ハンドル番号、キャプション名、クラス名について書かれた記事のまとめです。 最後までお付き合いいただきありがとうございます! この情報が誰かの役にたてれば幸 ...

雑記

最近よく思うことですが、何処でもいいからお出掛けしたいですよね。早くコロナが収まって、自由に街を出歩ける日常が戻ってくることを祈るばかりです。#stay home

最後までお付き合いいただきありがとうございます!

この情報が誰かの役にたてれば幸いです。

スポンサーリンク

-ソフトウェア