【EXCEL VBA | ノート】Windowハンドルを取得して、ファイル選択ダイアログボックスを操作する

スポンサーリンク

Windowハンドルを取得して、ファイル選択ダイアログボックスを操作する

この前、「SendKeysでパソコンのキー操作を自動化する」っていう記事でダイアログボックスを操作する記事を書いたんですが、、、

動作が安定しないんで、面倒ではありますが結局ハンドルを捕まえに行くことになりました。。。

で、そのへんのことを紹介させて頂きます!w

WindowとWindowハンドル

まずは、WindowとWindowハンドルについてです。

ウィンドウズ上で動いているいろんなアプリケーションは、基本的には”Window”と”コントロール”によって構成されています。

Windowは、アイコンクリックしたら、ビヨンって出てくる画面ですね。

コントロールは、その中のボタンとかみたいに理解するといいかもしれません。

それで、上の図のような感じで、各々のパーツが番号で管理されているというわけです。この番号をハンドルといいます。

図で「00000000」とか「99999999」とかの番号(ハンドル)は、Windowが作られた段階で作られるのでその都度変わります。

VBAから、このパーツを操作するためには、そのハンドルをその都度取得して捕まえる必要があるってことです。

操作の方法

WindowsAPI関数

Windowハンドルを捕まえに行くためには、2つのステップがあります。

1つ目は、APIの宣言です。

そして2つ目はそのAPIの関数を使うことです。

これはセットですので覚えておいてください。

APIとは、Application Programming Interface (アプリケーション・プログラミング・インタフェース) の略で

、他の製品やプログラムにアプリケーションにアクセスするために用意されているプログラムです。

Windowは、Windowsの機能になります(別のプログラム)ので、VBAからWindowsのシステムにアクセスするためにはAPIを使う必要があります。

Windowsの機能にアクセスするためには、WindowsAPIというものが用意されています。

VBAから、WindowsAPIを利用するためには、VBAに対して、

「このAPI使うぞ!」

って最初に言っとかないといけないというわけです。

これがAPIの宣言です。

次の図は、Windowハンドルを取得するための”FindWindow関数”を使うための、WindowsAPIの宣言を書いた状態です。

ここで注意してもらいたいのは、APIの宣言は、プロシージャを記述する前の宣言セクションに記述する必要があることです。

図で”FindWindow関数”を使うためのと言いましたが、WindowsAPIは、使用したい関数ごとに宣言セクションに宣言んを記述する必要があります。

めんどくさいですねw

我慢して下さい。

宣言セクションに、宣言が記述出来たら、通常通りプロシージャの中で宣言したWindowsAPI関数が使えるようになります。

ちなみに、Windowハンドルを取得する”FindWindow関数”は以下のような構文になっています。

ウインドウハンドル = FindWindow(“クラス名”, “キャプション名”)

クラス名は、どのウィンドウを取得するのかであらかじめ決まっています。以下に一部紹介しますが、他にもたくさんあります。

アプリケーション名ウィンドウクラス名
ExcelXLMAIN
WordOpusApp
AccessOMain
PowerPointPP10FrameClass
エクスプローラExploreWClass
メモ帳Notepad
ペイントMSPaintApp

キャプション名は、ウィンドウがポンと出たときに左上に出てくる題名です。

メモ帳だと次のような漢字ですね。

実施に、図のメモ帳のハンドルを取得してみます。

次のコードです。

’ API宣言
Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" ( _
        ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Sub sample()   
  Dim hmemo As Long ’変数宣言(ハンドルは番号なのでLong)
  hmemo = FindWindow("Notepad", "sample.txt - メモ帳")    '実際に取得しているコード
  Debug.Print hmemo ’イミディエイトウィンドウに出力
End Sub

※キャプションは、vbNullStringでもいけるっぽい。

番号はその都度変わってしまうので、これとは言えないですが、イミディエイトウィンドウに番号が表示されてばOKです。

ついでに、その捕まえたメモ帳に文字を入力してみましょう!

先ほど捕まえたのは、メモ帳の全体のハンドルなので、

まずは、文字を打ち込む場所のハンドルを捕まえる必要があります。

そして、そのあと文字を打ち込む場所に文字列を送るための機能も必要です。

めんどくさくなってきましたか?

我慢してくださいw

’ API宣言
Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" ( _
        ByVal lpClassName As String, _
        ByVal lpWindowName As String) As Long

' これ追加(編集部分を取得するために使う)
Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" ( _
        ByVal hwndParent As LongPtr, _
        ByVal hwndChildAfter As LongPtr, _
        ByVal lpClassName As String, _
        ByVal lpWindowName As String) As Long
        
' これ追加(編集部分に文字列を送るために使う)
Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" ( _
        ByVal hwnd As Long, _
        ByVal wMsg As Long, _
        ByVal wParam As Long, _
        lParam As Any) As LongPtr

Sub sample()
  
 ’メモ帳のハンドルを捕まえる
  Dim hmemo As Long
  hmemo = FindWindow("Notepad", "sample.txt - メモ帳")
  Debug.Print hmemo
  
  '捕まえたハンドル(Hmemo)から、Edit(文字を入れるとこ)のハンドルを捕まえる
  Dim hInputBox As Long
  hInputBox = FindWindowEx(hmemo, 0, "Edit", "") 
  Debug.Print hInputBox
  
  '捕まえたハンドル(Edit)に文字を送る
  Call SendMessage(hInputBox, &HC, 0, ByVal "さんぷる")
  
End Sub

まとめると、

  1. メモ帳のハンドルを捕まえる
  2. メモ帳のハンドルから編集するところを捕まえる
  3. 編集するところに文字列を送る

ってことです。

FindWindowsEx関数とSendMessage関数については、ここでは詳しく書きませんが、一個一個API宣言して関数を使うんだな~ということだけ覚えてください(^^♪

他にも、WindowsAPIには色々な関数がありますが、やると長くなりすぎるのでまた別の機会に。。。

パソコンの環境によって微妙に書き方が違ったりするので、これをコピペで動かない場合は、ご自身の環境に合わせていろいろググって探してみてください!(^^♪

ファイル選択ダイアログボックスを操作する

これが本題でしたので、ファイル選択ダイアログボックスの操作について紹介しますw

下の図のようなやつですね。

もうメモ帳でほとんどやったので、さっそくコードです。

’ ①API宣言
Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" ( _
        ByVal lpClassName As String, _
        ByVal lpWindowName As String) As Long

' これ追加(編集部分を取得するために使う)
Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" ( _
        ByVal hwndParent As LongPtr, _
        ByVal hwndChildAfter As LongPtr, _
        ByVal lpClassName As String, _
        ByVal lpWindowName As String) As Long
        
' これ追加(編集部分に文字列を送るために使う)
Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" ( _
        ByVal hwnd As Long, _
        ByVal wMsg As Long, _
        ByVal wParam As Long, _
        lParam As Any) As LongPtr



Sub sample()
  
 ’②ファイル選択ダイアログボックスのハンドルを捕まえる
  Dim handle As Long
  handle = FindWindow("#32770", "開く")
  
  '③捕まえたハンドル(handle)から入力ボックス(ファイル名が入るところ)のハンドルを捕まえる
  Dim hInputBox As Long
  hInputBox = FindWindowEx(handle, 0&, "ComboBoxEx32", "")
  hInputBox = FindWindowEx(hInputBox, 0&, "ComboBox", "")
  hInputBox = FindWindowEx(hInputBox, 0&, "Edit", "")
  Call SendMessage(hInputBox, &HC, 0, ByVal "***ファイルPATH**")
 
 ’④捕まえたハンドル(handle)から”開く”ボタンのハンドルを取得する
 Dim hButton As Long
 hButton = FindWindowEx(handle, 0&, "Button", "開く(&O)") '開くボタン
  Call SendMessage(hButton, &HF5, 0, 0&) 'ボタンをクリックする

End Sub

みたいな感じになります。

①は同じですね。

②は、ファイ選択ダイアログボックスのクラス名は「♯32770」になるので注意してください。

③ファイル名を入れるところに、ファイルPATHを入れる必要がありますが、何階層かになっているので順番に取得していきます。

最後に、SendMessageでファイルPATHを送信です。

④ファイルを確定するには、通常”開く”ボタンを押しますね。

開くボタンを取得して、SendMessage関数を使ってボタン押下の命令を出します。

SeleniumBasicと合わせてWEBアプリを操作

SeleniumBasicでブラウザ操作しながら、ファイルのアップロードなどでダイアログボックスが出てきたらWindowsAPIで操作

みたいな感じで、WEBアプリケーションを操作出来ます!

SeleniumBasicについて知りたい方はこちら!

まとめ

SendKeysでずぼらかまそうとしましたが、やっぱりちゃんとやらんといかんですねw

という勉強になりました(^^♪

安定感抜群で動いてくれますよ。