【VBA×WindowsAPI】UserFormへのドラッグ&ドロップでファイルパスを取得

VBAのUserFormにファイルを入力してもらう際にファイル選択ダイアログを使って入力させる手法は一般ですが、場合によってはUserFormへのドラッグ&ドロップでファイルを入力させたいということがしばしばあります。このときUserFormのListViewコントロールを利用することでドラッグ&ドロップされたファイルを取得することはできますが、このコントロールはVBA6時代のもののため環境によっては利用することが出来ない機能となっています。

ListViewコントロールは利用できないがどうしてもドラッグ&ドロップ入力したいという場合、WindowsAPIを使うことでこれを解決することができます。処理内容としてはウィンドウに設定されているウィンドウプロシージャ(WNDPROC)というイベントを検知する関数にVBAで定義した関数を割り込ませるという、VBAでやるにはいささか強引な手法です。イメージとしてはUserFormのイベントの根源部分の処理にVBAでアクセスするようなイメージです。

 icon-warning 注意  

本内容は処理内容としてはかなり複雑かつ間違えて実装するとアプリケーション(Excel等)が強制終了されるため細心の注意を払う必要のある手法です。正しく実装できれば問題なく利用することはできますがあまり推奨はできない内容のため、最終手段としてのみ使用することをお勧めします。
 
本ページは『サブクラス化とウィンドウプロシージャ』を理解している前提の内容となっています。サブクラス化やウィンドウプロシージャとは何かについてはリンク先ページを参照下さい。

UserFormへのドラッグ&ドロップでファイルパスを取得 

 
Windows APIを使うことで上動画の通り、UserFormにドラッグ&ドロップされたファイル(フォルダ)のパスを取得することができます。上動画ではUserFormの全体がドラッグ&ドロップの対象エリアとなっていますが、ListBoxやFrameなどのウィンドウハンドルが割り当てられるコントロールを指定することで、そのコントロールをドラッグ&ドロップの対象エリアとして設定することも可能です。

VBAでUserFormにドラッグ&ドロップされたファイルのパスを取得するには下記のWindows APIを利用します。それぞれ関数のより詳細な使い方の解説は各関数のリンクページを参照下さい。

icon-check-square FindWindow関数:      ウィンドウハンドルを取得する
icon-check-square SetForegroundWindow関数: 指定ウィンドウを最前面に移動する
icon-check-square SetWindowLongPtr関数:       ウィンドウプロシージャを設定する
icon-check-square CallWindowProc関数:            指定ウィンドウプロシージャにメッセージ情報を渡す
icon-check-square DragAcceptFiles関数:            指定ウィンドウにドラッグ&ドロップ可否の設定をする
icon-check-square DragQueryFile関数:              ドラッグ&ドロップされたファイルのパスを取得する
icon-check-square DragFinish関数:                ドラッグ&ドロップ時に確保されたメモリを解放する

「そもそもWindows APIって何?」という方はコチラ(メインページ)も併せて参照下さい。

サンプルコード

下記はUserFormにドラッグ&ドロップされたファイルのパスをすべて取得してメッセージボックスで表示するためのサンプルコードです。標準モジュールとUserFormそれぞれに下記コードをコピペすればすぐに実行可能です。(※vbModelessで表示したい場合はVBEを閉じて実行する必要があります)

 標準モジュールコード

 UserFormコード

コード解説

UserFormにイベントを追加するにはUserFormウィンドウをサブクラス化する必要があります。UserFormウィンドウのサブクラス化およびイベントの追加にあたるウィンドウプロシージャ(WINPROC関数)の設定については『サブクラス化とウィンドウプロシージャ』を参照下さい。
 

icon-edit UserFormへのドラッグ&ドロップを許可する

UserFromにファイルをドラッグ&ドロップしてパスを取得するには、予めUserFormウィンドウへのファイルのドラッグ&ドロップを許可する設定しておく必要があります。指定のウィンドウに対してドラッグ&ドロップを許可する設定にするにはDragAcceptFiles関数を使い下記のように記載します。

icon-code ドラッグ&ドロップを許可 

Call DragAcceptFiles(hWnd, True)

第1引数の「hWnd」にはUserFormのウィンドウハンドル、第2引数には「True」を入力します。これによりUserFormウィンドウをファイルのドラッグ&ドロップに対応したウィンドウとすることができます。このとき「hWnd」にUserFromコントロールのウィンドウハンドルを入力することで、指定のコントロールのみをドラッグ&ドロップの対象エリアとして設定することも可能です。
  

icon-edit ドラッグ&ドロップイベント(WINPROC関数)

ファイルがドラッグ&ドロップされたときにウィンドウは「WM_DROPFILES(=&H233)」メッセージを受け取ります。そのためウィンドウプロシージャ(WINPROC関数)として下記のようなFunctionプロシージャをウィンドウに紐づければドラッグ&ドロップ時の処理を設定することができます。

 

icon-edit ファイルパスの取得

ファイルがドロップされたことを検知したらまずは、ドロップされたファイルの総数を取得します。ドラッグ&ドロップされたファイルの総数を取得するにはDragAcceptFiles関数を使い下記のように記載します。第1引数の「wParam」はWNDPROC関数の第3引数でそれ以外は定数値です。

icon-code ドラッグ&ドロップされたファイル総数を取得 

lCnt = DragQueryFile(wParam, &HFFFFFFFF, vbNullString, 0)

ファイルの総数が取得できたらその数だけループを回して、ドロップされたファイル1つずつに対してパス取得の処理を行います。ドラッグ&ドロップされたファイルのパスを取得するにはファイル総数の取得時と同じくDragAcceptFiles関数を使い下記のように記載します。

icon-code ドラッグ&ドロップされたファイルのパスを取得 

Call DragQueryFile(wParam, lIndex, sBuf, Len(sBuf))

第1引数の「wParam」にはWNDPROC関数の第3引数、第2引数の「lIndex」にはドロップされたファイルのインデックス(0始まり)、第3引数の「sBuf」にはファイルパスを受け取るための固定長文字列、第4引数には第3引数の大きさである「Len(sBuf)」を入力します。

 
ウィンドウにドラッグ&ドロップされたファイル情報はメモリ上に保持されるため、DragFinish関数を使ってメモリを明示的に解放する必要があります。WNDPROC関数の第3引数である「wParam」を引数として入力することでメモリの解放を行うことができます。

icon-code ドラッグ&ドロップされたファイルのメモリ解放

Call DragFinish(wParam)

WNDPROC関数は指定の引数以外の設定ができないため、取得したパスはグローバル変数に格納しておくことが一般的です。サンプルコードでは配列に格納してドラッグ&ドロップされた直後にメッセージボックスを表示するような処理としていますが、任意で書き換えても問題ありません。

 

関連情報

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

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

icon-share-square 参考

Microsoft公式:ウィンドウ プロシージャの使用 – Win32 apps

2024年4月9日Excel, VBA, Windows API

Posted by Lic