VBAでプログラムを作成しているとEXCEL・Accessを問わず「特定のフォルダを指定して作業を実行する」というプロセスを組むロジックはよくある話です。
実務でVBAを使用する方々はかなりの割合で、フォルダ選択ダイアログを制御してフォルダを特定させる方法を採用しているのではないでしょうか。
プログラムを実行する際に特定のフォルダのパスが固定値であれば何ら悩む必要はないのですが、状況・環境に合わせてフォルダのパスが固定できなかったり、フォルダのパスをあえてユーザ選択させることで柔軟なプログラムを作成するケースがあります。
そのような場面で活躍するのがフォルダ選択ダイアログです。
フォルダ選択ダイアログについてはパスを取得することが出来ればそれで「OK」という方もいれば、フォルダ選択ダイアログを表示させるにしてもいろいろ条件ありきで表示させたい、などと様々な都合があると思います。
今回はフォルダ選択ダイアログについて以下の点を徹底的にあぶり出してみたいと思います。
- どのような種類があるのか
- どのような使い方ができるのか
初めての方、すでに知っている方、いらっしゃるかもしれませんが、使用できる状況に合わせて使用できる手段を把握しておくことはスキルアップにつながりますので、ぜひご一読ください。
FileDialogオブジェクト
これは主観ですがVBAでフォルダ選択ダイアログを必要としたとき、一番無難な実現方法はFileDialogオブジェクトを使用する方法なのではないかと考えます。
まずFileDialogオブジェクトはプロパティとメソッドが豊富にサポートされており、フォルダ選択できるだけのオブジェクトではありません。
サポートしているダイアログは以下のとおりです。
- ファイル選択ダイアログ
- フォルダ選択ダイアログ
- [ファイルを開く]ダイアログ
- [名前を付けて保存]ダイアログ
これだけサポートされていると「ダイアログについてはFileDialogオブジェクトにすべて任せた!」と言い切っても正直不足はないのではないでしょうか。
ExcelやAccessで使用可能で、FileDialogオブジェクトを呼び出す際にMsoFileDialogType(列挙型)を指定することで定数に応じたダイアログのオブジェクトを得ることができます。
名前 | 値 | 説明 |
---|---|---|
msoFileDialogOpen | 1 | [ファイルを開く]ダイアログ |
msoFileDialogSaveAs | 2 | [名前を付けて保存]ダイアログ |
msoFileDialogFilePicker | 3 | ファイル選択ダイアログ |
msoFileDialogFolderPicker | 4 | フォルダ選択ダイアログ |
今回の場合で言えばフォルダ選択を目的とするので、msoFileDialogFolderPickerを指定することになります。
FileDialogオブジェクトを使用するには参照設定で以下のオブジェクトライブラリの参照を有効する必要があります。
必要なオブジェクトライブラリ
Microsoft Office XX.X Object Library
※バージョンについてはOfficeのバージョンにより異なりますので「XX.X」と記載していますので、各環境に合わせて読み替えてください。
このオブジェクトライブラリを有効にしていない場合、上で説明した「MsoFileDialogType」を使用することができません。
定数の値(数値)を記述することでオブジェクトライブラリの有効化を回避する方法もあります。
他ツールへの移植が想定されるモジュールの場合などではメンテナンス性を考慮してあえて数値を直接記述するケースもあります。
どちらの方法を採用しても動作上は問題ありません。
フォルダ選択ダイログを表示するサンプル
Sub FileDialog_Sample001() 'FileDialogオブジェクトのインスタンス取得:フォルダ選択ダイログ With Application.FileDialog(msoFileDialogFolderPicker) 'ダイアログのタイトルを設定 .Title = "フォルダを選択してください" '「OK」ボタンのキャプションを設定 .ButtonName = "選択する" 'ダイアログの初期パスを設定 .InitialFileName = "C:\" 'ダイアログを表示(キャンセル:処理抜け) If Not .Show Then Exit Sub '選択したフォルダのフルパスをデバッグ出力 Debug.Print .SelectedItems(1) End With End Sub
EXCEL実行時での表示結果は以下の通りとなります。
サンプルの補足
今回はフォルダ選択ダイアログを表示する際に任意で変更可能なプロパティの値を変更していますが、これらのプロパティはShowメソッドを実行する前に設定しておく必要がありますのでご注意ください。
使用頻度の高いプロパティはおそらくInitialFileNameあたりでしょうか。
InitialFileNameで初期パスを適切に設定することでフォルダ移動の負担軽減が見込めます。
なお、プロパティ設定は任意なので即Showメソッドでも何ら問題ありません。
Sub FileDialog_Sample002() 'FileDialogオブジェクトのインスタンス取得:フォルダ選択ダイログ With Application.FileDialog(4) 'ダイアログを表示(キャンセル:処理抜け) If Not .Show Then Exit Sub '選択したフォルダのフルパスをデバッグ出力 Debug.Print .SelectedItems(1) End With End Sub
上のサンプルではフォルダ選択ダイアログの呼び出し時の引数を数値で記述して呼び出していますが、これでも問題なく動作します。
Showメソッドは戻り値でBoolean型で返します。
戻り値 | 説明 |
---|---|
True | 選択ボタン押下 |
False | キャンセルボタン押下 |
フォルダ選択ダイアログで指定したフォルダのパスは「SelectedItems」で取得可能です。
プロパティである「SelectedItems」はFileDialogSelectedItemsコレクションを取得するため、フォルダパスの取得ではインデックスの指定が必要になります。
なお、複数選択はファイル選択のみでフォルダ選択では無効になるため、フォルダ選択の場合はインデックス「1」を固定と考えてもいいでしょう。
FileDialogオブジェクトの各種ダイアログおよびプロパティやメソッドの詳細については、改めて解説の機会を作りたいと思います。
ShellオブジェクトのBrowseForFolder
Shellオブジェクトでもフォルダ選択ダイアログを表示させることができます。
FileDialogオブジェクトで表示されるダイアログとは雰囲気が異なりますが、ShellオブジェクトはEXCEL、ACCESSだけでなくVBS(VBScript)上でも呼び出すことが出来る強みがあります。
Shellオブジェクト自体はフォルダ選択ダイアログ機能だけをサポートしているわけではないので、その中の一つのメソッドを実行することでフォルダ選択ダイアログを表示させます。
'フォルダ選択ダイアログを出力するメソッド BrowseForFolder( lngHwnd , strTitle , lngOptions [, vRootFolder ])
引数名 | データ型 | 省略 | 説明 | |
---|---|---|---|---|
第1引数 | lngHwnd | Long | 不可 |
|
第2引数 | strTitle | String | 不可 | ダイアログ上に表示するメッセージ |
第3引数 | lngOptions | Long | 不可 | ダイアログのオプション設定 |
第4引数 | vRootFolder | Variant | 可 | ルートパス:文字列か数値(CSIDL値) |
一行書きで実行できるため、簡素な記述でダイアログを表示させることが出来ます。
必要なオブジェクトライブラリ
Microsoft Shell Controls And Automation
これは必須と書きつつ必須ではありません。
アーリーバインディング(参照設定)でいくか、レイトバインディング(CreatObject)でいくかの違いで、ロジックを組む上での好みの問題なのでそれぞれでご判断ください。
フォルダ選択ダイアログを表示するサンプル
Sub BrowseForFolder_Sample() Dim oFolder As Object 'Shellオブジェクトのインスタンスを取得 With CreateObject("Shell.Application") 'フォルダ選択ダイアログを表示 Set oFolder = .BrowseForFolder(Application.Hwnd _ , "フォルダを選んでください" _ , &H1 + &H10 + &H200 _ , "C:\") '戻り値が空の場合:処理抜け If oFolder Is Nothing Then Exit Sub '選択したフォルダのフルパスをデバッグ出力 Debug.Print oFolder.Items.Item.Path End With Set oFolder = Nothing End Sub
EXCEL実行時での表示結果は以下の通りとなります。
サンプルの補足
このダイアログの良いところはルートパスが指定できるところにつきます。
上位階層への移動を制限したい場合などルートを指定することで移動制限することができるので、用途は限定的かもしれませんが要件に移動制限が含まれている場合にはShellオブジェクトのBrowseForFolderはオススメです。
ダイアログのオプション設定については加算していくことでオプションが追加されていきます。
ここではよく使用されるオプションのみご紹介します。
オプションフラグ | 値 | 説明 |
---|---|---|
BIF_RETURNONLYFSDIRS | &H1 | ファイルシステムディレクトリ |
BIF_DONTGOBELOWDOMAIN | &H2 | ネットワークフォルダを展開しない |
BIF_EDITBOX | &H10 | フォルダ名をボックスに表示する |
BIF_NONEWFOLDERBUTTON | &H200 | 「新しいフォルダの作成」を表示しない |
その他のオプションフラグについては以下を参考にしてください。
必要に応じてShellオブジェクトのBrowseForFolderをカスタムしてください。
Win32 API の SHBrowseForFolder
Win32APIの「SHBrowseForFolder」を使ったフォルダ選択ダイアログの表示方法のご紹介になります。
Shell.Applicationで呼び出せるダイアログと似たビジュアルになりますが、SHBrowseForFolderでも同様な使い方が可能であるため手段のひとつとして挙げさせて頂きました。
Win32APIだと32bit・64bit対応が必要だったり、ロジックのハードルも上がってくるため知的探求心とガッツで理解を深めていくとスキルの幅も広がっていくかと思います。
ちなみにWin32APIはVBAでは使用できますが、VBSでは使用できませんのでご注意ください。
フォルダ選択ダイアログを表示するサンプル
'フォルダ指定ダイアログを表示する Private Declare PtrSafe Function SHBrowseForFolder Lib "shell32.dll" Alias "SHBrowseForFolderA" (lpBrowseInfo As BROWSEINFO) As LongPtr 'アイテム識別子のリストをファイルシステムのパス名に変換する Private Declare PtrSafe Function SHGetPathFromIDList Lib "shell32.dll" Alias "SHGetPathFromIDListA" (ByVal pidl As LongPtr, ByVal pszPath As String) As Boolean 'BROWSEINFO構造体設定 Private Type BROWSEINFO hOwner As LongPtr pidlRoot As LongPtr pszDisplayName As String lpszTitle As String ulFlags As Long lpfn As LongPtr lParam As LongPtr iImage As Long End Type 'オプションフラグ Private Const BIF_RETURNONLYFSDIRS As Long = &H1 Private Const BIF_NONEWFOLDERBUTTON As Long = &H200 Sub SHBrowseForFolder_Sample() Dim myBROWSEINFO As BROWSEINFO Dim idlist As LongPtr Dim path As String Dim res As Boolean 'フォルダ選択ダイアログの設定 With myBROWSEINFO 'ウィンドウのハンドル .hOwner = Application.Hwnd 'ルートで表示するフォルダ .pidlRoot = &H11 'ダイアログ上のメッセージ .lpszTitle = "フォルダを選択してください。" 'オプションフラグ .ulFlags = BIF_RETURNONLYFSDIRS + BIF_NONEWFOLDERBUTTON End With 'フォルダ選択ダイアログを表示 idlist = SHBrowseForFolder(myBROWSEINFO) 'パス情報格納変数を初期化 path = String(260, vbNullChar) 'パス情報を取得 res = SHGetPathFromIDList(idlist, path) '取得していた場合、結果をデバッグ出力 If res Then Debug.Print Left(path, InStr(path, vbNullChar) - 1) End Sub
EXCEL実行時での表示結果は以下の通りとなります。
サンプルの補足
Win32APIを深掘りしていくと難解な解釈が多くなってくるため、ここでは割愛させて頂きます。
このサンプルではOffice2010以前のバージョンへの考慮は切り捨てて作られているため、必要に応じた編集が生じる場合もありますので、あらかじめご承知おきください。
フォルダ選択ダイアログの設定についてはシンプルによく使用されるメンバーを使用しておりますので、その点は参考にはなるのではないかと思います。
サンプルですのでシンプルな作りこみですが、引数で表示仕様をカスタム出来るように改善させる余地はあるかと思います。
ほぼほぼShell.ApplicationオブジェクトのBrowseForFolderをWin32APIを介して制御しているようなものなのでオーナーハンドル、メッセージ、オプションフラグはShell.ApplicationオブジェクトのBrowseForFolderと同じ設定値で動作します。
ルートフォルダの設定については数値(CSIDL値)のみとなります。
オブジェクトの力を借りずにAPIで我が道を進む方はそれはそれで邁進していってもらいたいものの、近道できるオブジェクトの存在意義を考えると代替手段としてスキルをストックしておくことは全然ありですが、VBAではサポートされているオブジェクトを使用する方が無難かと思います。
あくまで主観ですが。
WizHookオブジェクトのGetFileName
最後にもう一つマニアックなオブジェクトのご紹介ですが、皆さんACCESS限定の非公開オブジェクトである「Wizhook」オブジェクトってご存じでしょうか?
非公開オブジェクト化しているあたり、マイクロソフトでは非推奨扱いなのでしょうか。
この「Wizhook」オブジェクトで使用できるメソッドは意外とパワフルで便利なものもあり、その中の一つにフォルダ選択ダイアログも含まれているわけです。
「Wizhook」オブジェクトの「GetFileName」メソッド
'フォルダ選択ダイアログを出力するメソッド GetFileName(hwndOwner As Long, _ AppName As String, _ DlgTitle As String, _ OpenTitle As String, _ File As String, _ InitialDir As String, _ Filter As String, _ FilterIndex As Long, _ View As Long, _ flags As Long, _ fOpen As Boolean) As Long
引数は多いのですがファイル・フォルダ選択ダイアログとして使用することができます。
引数名 | データ型 | 省略 | 説明 | |
---|---|---|---|---|
第1引数 | hwndOwner | Long | 不可 | 呼び出し元のハンドル ※Application.hWndAccessApp |
第2引数 | AppName | String | 不可 | 実行中のアプリケーションの名前 ※「Microsoft Access」など |
第3引数 | DlgTitle | String | 不可 | ダイアログのタイトル |
第4引数 | OpenTitle | String | 不可 | 選択ボタンのキャプション |
第5引数 | File | String | 不可 | 選択フォルダのフルパス ※選択したパスが戻り値になります |
第6引数 | InitialDir | String | 不可 | 初期フォルダのフルパス ※ダイアログ起動時の初期フォルダのパス |
第7引数 | Filter | String | 不可 | ダイアログの[ファイルの種類]の設定 ※フォルダ選択時には表示されません。 |
第8引数 | FilterIndex | Long | 不可 | 適用するフィルタのインデックス |
第9引数 | View | Long | 不可 | ダイアログの初期表示スタイル ※フォルダ選択ダイアログでは無効 |
第10引数 | flags | Long | 不可 | ダイアログの動作機能 ※フォルダ選択モード:「32」 |
第11引数 | fOpen | Boolean | 不可 | ダイアログの種類 ※フォルダ選択ダイアログ:False |
この「Wizhook」オブジェクト自体はACCESS限定で使用できるオブジェクトであるため、残念ながらEXCELやVBS(VBScript)ではそのまま使用することができません。
ExcelにもApplication.GetOpenFilenameメソッドという似たようなフレーズのメソッドがありますが、そちらは残念ながらフォルダ選択ダイログはサポートされておりません。
必要なオブジェクトライブラリ
Microsoft Access XX.X Object Library
※バージョンについてはOfficeのバージョンにより異なりますので「XX.X」と記載していますので、各環境に合わせて読み替えてください。
ACCESSはデフォルトで参照されているため問題ないですが、EXCELで使用する場合はオブジェクトライブラリへの参照設定か、CreateObjectによるバインディングが必要となります。
なお、VBS(VBScript)の場合は。Createobjectのみ対応可です。
EXCEL、VBS(VBScript)ともにAccessがインストールされていないとオブジェクトライブラリが使用できないため、使用環境や要件によってはうまく動作させることが出来なくなりますのでご注意ください。
フォルダ選択ダイログを表示するサンプル
Sub Wizhook_GetFileName_Sample() Dim strPath As String Dim lngRes As Long 'Wizhookの有効化 WizHook.Key = 51488399 'フォルダ選択ダイアログを表示 lngRes = WizHook.GetFileName(Application.hWndAccessApp, _ "", _ "フォルダを選択", _ "選択する", _ strPath, _ "C:\", _ "すべてのファイル (*.*)|*.*", _ 0, _ 0, _ 32, _ False) '取得パスをデバッグ出力 ※キャンセル押下時の戻り値:-302 If lngRes <> -302 Then Debug.Print strPath 'Wizhookの無効化 WizHook.Key = 0 End Sub
ACCESS実行時での表示結果は以下の通りとなります。
サンプルの補足
さてAccessの非公開クラスであるWizhookオブジェクトを使ったフォルダ選択ダイアログですがいかがでしょうか。
Wizhookを使用するにあたり、Wizhookを有効化する必要があるので覚えておきましょう。
どの引数も省略不可なので、設定する値がない場合はString型なら空文字、Long型なら「0」を設定しておけば問題ありません。
第6引数InitialDirで初期フォルダを指定したい場合は、第7引数Filterを設定しておかないと正しく動作しないのでご注意ください。
GetFileNameメソッドは戻り値でLong型で返します。
戻り値 | 説明 |
---|---|
0 | 選択ボタン押下 |
-302 | キャンセルボタン押下 |
選択したフォルダのパスは第5引数に出力される仕様となっているため、出力用の変数を引数に設定しておく必要があります。
GetFileNameについては一行書きで記述できるのでFileDialogオブジェクトのようなダイアログを簡単に呼び出すことができます。
参照設定不要で使用できるのでモジュール移植時のメンテナンスも負担軽減が見込めます。
ただし非公開クラスについての捉え方次第によっては業務上の開発ではなかなか理解が得られないかもしれません。
まとめ
フォルダ選択ダイログについていくつかご紹介させて頂きましたが、いかがでしたでしょうか。
上の説明にも挙げましたが、基本的にはFileDialogオブジェクトを使用することをマイクロソフトでも推奨しておりますのでその点は鉄板ですが、同じ結果を切り口を変えて模索してみるのは開発者の性とも言えるでしょう。
ベーシックな知識からマニアックな知識まで知識欲が枯渇するまで追求していきましょう!!
今回ご紹介した方法以外にフォルダ選択ダイアログの表示する方法をご存じな方いらっしゃいましたら、ぜひぜひ教えてください。