How to VBA ~ウィンドウ操作~
Excelの内容を別システムに入力していきたい。そんなことを考えたことはありませんか?
そんな時に便利なのが、Excelに存在するVBAのウィンドウ操作処理です。
★確認していく内容について
VBAのウィンドウ操作といっても、いろいろな方法や種類があります。数ある中から今回は、「指定のウィンドウに対して文字を入力する」という動作を紹介します。
※この記事の記述は64bit版のコードです 。
★特定のウィンドウに対して操作を実行したい!
操作するにあたり、以下の内容を考慮して作成していきます。
・入力操作を実行する時は、VBAの標準機能を利用
・入力対象のウィンドウはアクティブである必要がある
・特に画面の切り替わりがあるウィンドウ操作には待機等の遅延も必要
-
- 指定のウィンドウを前面に表示する
タイトルの通り、まず操作したいウィンドウを探してアクティブ化(前面化)させる必要があります。前面化で利用するのは以下のWindowsに搭載されているAPI関数と呼ばれるものです。アクティブにしたい!けどまずは・・・どうやって判定する?どのウィンドウかを判定するために、「FindWindow」と「GetWindow」、「GetWindowText」を利用していきます。まずは「FindWindow」で今の前面化されているウィンドウを取得します。取得したウィンドウで「GetWindowText」を利用してウィンドウタイトルを取得します。
※画像の赤マル部分
- タイトルに指定の文字が含まれるかを判断する。含まれていないときは「GetWindow」の第2引数(wCmdの部分)を「2」にして次のウィンドウを取得する。
上記の内容を繰り返して実行します。上記内容が完了したら、指定のウィンドウを判定できている状態ということになります。
※利用するAPIコードが多いので下にまとめて記述します 。'FindWindow Declare PtrSafe Function Findwindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr 'GetWindow Declare PtrSafe Function GetWindow Lib "user32" (ByVal hWnd As LongPtr, ByVal wCmd As Long) As LongPtr 'GetWindowText Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As LongPtr, ByVal lpString As String, ByVal cch As Long) As Long 'GetWindowで次のウィンドウを選択する用の固定値 Const GW_HWNDNEXT As Long = 2 '最初のウィンドウを取得 hWnd = Findwindow(vbNullString, vbNullString) '取得できるウィンドウがなくなるまで繰り返す Do While hWnd <> 0 '取得できたウィンドウのタイトルを取得する GetWindowText hWnd, title, Len(title) 'タイトルに指定した名称が含まれる可を判定する If InStr(title, "指定したいタイトル") > 0 Then 'タイトルが部分一致していれば対象のウィンドウであると判断 Exit Do End If '次に取得できるウィンドウを取得する hWnd = GetWindow(hWnd, GW_HWNDNEXT) Loop次に取得できたウィンドウに対して、アクティブ化の実行をします。指定のウィンドウをアクティブ化するためには「SetForegroundWindow」を利用していきます。前述した処理で変数(hWnd)にウィンドウの識別を保持しているため、「SetForegroundWindow」でその識別を利用してウィンドウを前面化します。
※最小化されたウィンドウを表示したいときは別途記載あり'SetForegroundWndow Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hWnd As LongPtr) As Long '対象のウィンドウを前面に変更する SetForegroundWindow hWnd※最小化状態を変更したいときは「ShowWindow」を利用します。
'ShowWindow Declare PtrSafe Function ShowWindow Lib "user32" (ByVal hWnd As LongPtr, ByVal nCmdShow As Long) As Long 'ShowWindowのコマンド定数 Private Const SW_RESTORE As Long = 9 '対象のウィンドウを最小化状態から通常表示にする ShowWindow hWnd, SW_RESTORE - ウィンドウに対して処理を実行する
今回はわかりやすいように、「TEST」という文字列を送信してみます。上記の内容でアクティブ化された画面に対して、今回は「SendKeys」を利用して文字列を送信していきます。コードを見てわかるとおり、こちらはVBAの標準機能のため記述が少なくなります。'基本的な文字入力は基本機能にあるSendKeysを利用する SendKeys "TEST", True - 操作の実行
上記の内容を通してコードを記述して実装してみましょう!
※今回は画面遷移や最小化状態等の考慮を入れて記述してあります。
処理がそれぞれで何をしているかは、コメントを参照してください。◆操作前にEdgeの検索画面を開いて、入力位置を検索枠内においてくださいOption Explicit 'FindWindow 指定のウィンドウや前面のウィンドウを取得する Declare PtrSafe Function Findwindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr 'GetWindow 対象ウィンドウの次のウィンドウや前のウィンドウを取得する Declare PtrSafe Function GetWindow Lib "user32" (ByVal hWnd As LongPtr, ByVal wCmd As Long) As LongPtr 'GetWindowText 対象ウィンドウのタイトルを取得する Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As LongPtr, ByVal lpString As String, ByVal cch As Long) As Long 'SendInput 特殊文字の送信(右Ctrlなど) Declare PtrSafe Function SendInput Lib "user32" (ByVal nInputs As Long, ByRef pInputs As typINPUT, ByVal cbSize As Long) As Long 'ShowWindow 指定のウィンドウの表示状態を変更する(最小化を通常表示に変更するなど) Declare PtrSafe Function ShowWindow Lib "user32" (ByVal hWnd As LongPtr, ByVal nCmdShow As Long) As Long 'SetForegroundWndow 指定のウィンドウを前面化する Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hWnd As LongPtr) As Long 'GetForrgroundWindow 現在前面にあるウィンドウのハンドルを取得する Declare PtrSafe Function GetForegroundWindow Lib "user32" () As LongPtr 'Sleep 指定のミリ秒分待機する(1秒=1000ミリ秒) Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'SnedInputにて利用するタイプ型 Type typKBINPUT wVk As Integer wScan As Integer dwFlags As Long time As Long dwExtraInfo As LongPtr bytUnusedPadding(7) As Byte End Type 'SnedInputにて利用するタイプ型 Type typINPUT type As Long ki As typKBINPUT End Type 'SnedInputにて利用する設定値 Public Const INPUT_KEYBOARD As Long = 1 'キーボードの入力タイプ Public Const KEYEVENTF_KEYUP As Long = &H2 'キーボードのUpコード(離すコード) Public Const KEYEVENTF_EXTENDEDKEY As Long = &H1 'キーボードの特殊キーを利用するという宣言 'ShowWindowのコマンド定数 Private Const SW_RESTORE As Long = 9 'GetWindowで次のウィンドウを選択する用の固定値 Const GW_HWNDNEXT As Long = 2 '操作前にEdgeの検索画面を開いて、入力位置を検索枠内においてください Sub HowtoVBA_WindowActivate() '対象ウィンドウを保持するための変数 Dim hWnd As LongPtr 'タイトルを取得する際に利用する Dim title As String * 255 Dim tgtWindow As String Dim searchWord As String '入力対象のウィンドウ名 tgtWindow = "Edge" '検索するためのワード searchWord = "検索用文字列" '指定のウィンドウハンドルを取得する hWnd = GetTargetWindow(tgtWindow) '対象のウィンドウが見つかったか If hWnd = 0 Then MsgBox "対象のウィンドウが見つかりませんでした" Exit Sub End If '対象のウィンドウを最小化状態から通常表示にする ShowWindow hWnd, SW_RESTORE '対象のウィンドウを前面に変更する SetForegroundWindow hWnd 'ウィンドウ表示待機 Sleep 1000 '前面にしたウィンドウを取得する hWnd = GetForegroundWindow '取得できたウィンドウのタイトルを取得する GetWindowText hWnd, title, Len(title) '対象ウィンドウ名に指定文字が存在しなかった場合、エラーとする If InStr(title, tgtWindow) = 0 Then 'ここに探したいウィンドウのタイトルを記述する 'タイトルが部分一致していれば対象のウィンドウであると判断 MsgBox "対象画面が前面化されていません。" End If 'アクティブなウィンドウに検索用の文字列を入力する SendKeys searchWord, True '入力待機 Sleep 1000 '検索実行(SendInputでEnterキーを押下する) PressKey vbKeyReturn '検索実行待機 Sleep 1000 '取得できるウィンドウがなくなるまで繰り返す Do '現在表示されているウィンドウ内で検索ワードを持ったウィンドウを取得する(ないときは0になる) hWnd = GetTargetWindow(searchWord) '対象ウィンドウが見つかったら処理を抜ける If hWnd > 0 Then Exit Do '無限ループになってしまう際に一時的に操作を受け付けるようにするコード(Escキーで停止可能) DoEvents Loop MsgBox "検索完了!" End Sub 'SebdInputにて1コード分入力する(特殊キーにも対応済み※右Ctrlなど) Sub PressKey(ByVal KeyCode As Long) Dim KeyAr(1) As typINPUT Dim lngResult As Long 'キーを押す With KeyAr(0) .type = INPUT_KEYBOARD .ki.wVk = KeyCode .ki.dwFlags = KEYEVENTF_EXTENDEDKEY Or 0 End With 'キーを離す With KeyAr(1) .type = INPUT_KEYBOARD .ki.wVk = KeyCode .ki.dwFlags = KEYEVENTF_EXTENDEDKEY Or KEYEVENTF_KEYUP End With 'キーの長さやどのキーを押すか、コードの記述等を引数にする lngResult = SendInput(2, KeyAr(0), LenB(KeyAr(0))) End Sub '呼出 :GetTargetWindow : 指定したウィンドウタイトルの画面のウィンドウハンドルを取得する '引数 :tgtWindowName : ここで指定した名称の含まれるウィンドウを操作対象とする Function GetTargetWindow(ByVal tgtWindowName As String) As LongPtr 'ウィンドウハンドル保持用 Dim hWnd As LongPtr 'タイトル保持用 Dim title As String * 255 ' 最初のウィンドウを取得 hWnd = Findwindow(vbNullString, vbNullString) '取得できるウィンドウがなくなるまで繰り返す Do While hWnd <> 0 GetWindowText hWnd, title, Len(title) ' タイトルに指定した名称を含む場合 If InStr(title, tgtWindowName) > 0 Then Exit Do End If hWnd = GetWindow(hWnd, GW_HWNDNEXT) Loop '取得したウィンドウハンドルを戻り値とする GetTargetWindow = hWnd End Function※ウィンドウ操作処理の注意点
まず、対象のウィンドウが存在しないことには動作しません。そのため、最初にウィンドウが存在することを確認したり、対象のウィンドウが前面化されなかったときはエラーとしたりする処理が多いです。上記以外にも、画面遷移を伴うものに関しては、画面が変更されることを加味して待機やウィンドウの再確認等を実施するようにしています。
- 指定のウィンドウを前面に表示する
★最後に
いろいろ考慮して記述しようとすると、どうしてもコードが長くなります。
が、それぞれが必要なコードであると認識して、不要だと省きすぎないことも大切です。それぞれの動作ですが、人が操作するより VBAの処理が速すぎる時が多いです。
そのため、待機をうまく活用して操作間のラグを調整することが、処理の効率化に対して大切になります。
様々なRPAの導入が実施されている中、セキュリティ関係でExcelVBAのみ利用可能な状況である、そんな人たちでもより高度な自動化による生産性向上が図れるようになる。そんな貢献ができればと思います。





