[VBA]フォルダ選択ダイアログの使い方・使い分け

vbaのフォルダ選択ダイアログの使い方VBA

VBAでプログラムを作成しているとEXCEL・Accessを問わず「特定のフォルダを指定して作業を実行する」というプロセスを組むロジックはよくある話です。

実務でVBAを使用する方々はかなりの割合で、フォルダ選択ダイアログを制御してフォルダを特定させる方法を採用しているのではないでしょうか。

プログラムを実行する際に特定のフォルダのパスが固定値であれば何ら悩む必要はないのですが、状況・環境に合わせてフォルダのパスが固定できなかったり、フォルダのパスをあえてユーザ選択させることで柔軟なプログラムを作成するケースがあります。

そのような場面で活躍するのがフォルダ選択ダイアログです。

フォルダ選択ダイアログについてはパスを取得することが出来ればそれで「OK」という方もいれば、フォルダ選択ダイアログを表示させるにしてもいろいろ条件ありきで表示させたい、などと様々な都合があると思います。

今回はフォルダ選択ダイアログについて以下の点を徹底的にあぶり出してみたいと思います。

  • どのような種類があるのか
  • どのような使い方ができるのか

初めての方、すでに知っている方、いらっしゃるかもしれませんが、使用できる状況に合わせて使用できる手段を把握しておくことはスキルアップにつながりますので、ぜひご一読ください。

スポンサーリンク
スポンサーリンク

FileDialogオブジェクト

これは主観ですがVBAでフォルダ選択ダイアログを必要としたとき、一番無難な実現方法はFileDialogオブジェクトを使用する方法なのではないかと考えます。

まずFileDialogオブジェクトはプロパティとメソッドが豊富にサポートされており、フォルダ選択できるだけのオブジェクトではありません。

サポートしているダイアログは以下のとおりです。

  • ファイル選択ダイアログ
  • フォルダ選択ダイアログ
  • [ファイルを開く]ダイアログ
  • [名前を付けて保存]ダイアログ

これだけサポートされていると「ダイアログについてはFileDialogオブジェクトにすべて任せた!」と言い切っても正直不足はないのではないでしょうか。

ExcelやAccessで使用可能で、FileDialogオブジェクトを呼び出す際にMsoFileDialogType(列挙型)を指定することで定数に応じたダイアログのオブジェクトを得ることができます。

名前説明
msoFileDialogOpen1[ファイルを開く]ダイアログ
msoFileDialogSaveAs2[名前を付けて保存]ダイアログ
msoFileDialogFilePicker3ファイル選択ダイアログ
msoFileDialogFolderPicker4フォルダ選択ダイアログ

今回の場合で言えばフォルダ選択を目的とするので、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実行時での表示結果は以下の通りとなります。
vba-filedialog-folderpicker

サンプルの補足

今回はフォルダ選択ダイアログを表示する際に任意で変更可能なプロパティの値を変更していますが、これらのプロパティは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引数lngHwndLong不可
  • EXCEL
    Application.Hwnd
  • ACCESS
    Application.hWndAccessApp
  • VBS(VBScript)
    「0」指定
第2引数strTitleString不可ダイアログ上に表示するメッセージ
第3引数lngOptionsLong不可ダイアログのオプション設定
第4引数vRootFolderVariantルートパス:文字列か数値(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実行時での表示結果は以下の通りとなります。
vba-shell-BrowseForFolder

サンプルの補足

このダイアログの良いところはルートパスが指定できるところにつきます。

上位階層への移動を制限したい場合などルートを指定することで移動制限することができるので、用途は限定的かもしれませんが要件に移動制限が含まれている場合にはShellオブジェクトのBrowseForFolderはオススメです。

ダイアログのオプション設定については加算していくことでオプションが追加されていきます。

ここではよく使用されるオプションのみご紹介します。

オプションフラグ説明
BIF_RETURNONLYFSDIRS&H1ファイルシステムディレクトリ
BIF_DONTGOBELOWDOMAIN&H2ネットワークフォルダを展開しない
BIF_EDITBOX&H10フォルダ名をボックスに表示する
BIF_NONEWFOLDERBUTTON&H200「新しいフォルダの作成」を表示しない

その他のオプションフラグについては以下を参考にしてください。

BROWSEINFOA (shlobj_core.h) - Win32 apps
Contains parameters for the SHBrowseForFolder function and receives information about the folder selected by the user. (ANSI)

必要に応じて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実行時での表示結果は以下の通りとなります。
vba-winapi32-SHBrowseForFolder

サンプルの補足

Win32APIを深掘りしていくと難解な解釈が多くなってくるため、ここでは割愛させて頂きます。

このサンプルではOffice2010以前のバージョンへの考慮は切り捨てて作られているため、必要に応じた編集が生じる場合もありますので、あらかじめご承知おきください。

フォルダ選択ダイアログの設定についてはシンプルによく使用されるメンバーを使用しておりますので、その点は参考にはなるのではないかと思います。

サンプルですのでシンプルな作りこみですが、引数で表示仕様をカスタム出来るように改善させる余地はあるかと思います。

ほぼほぼShell.ApplicationオブジェクトのBrowseForFolderをWin32APIを介して制御しているようなものなのでオーナーハンドル、メッセージ、オプションフラグはShell.ApplicationオブジェクトのBrowseForFolderと同じ設定値で動作します。

ルートフォルダの設定については数値(CSIDL値)のみとなります。

CSIDL (Shlobj.h) - Win32 apps
CSIDL (定数特殊項目 ID リスト) の値は、アプリケーションで頻繁に使用される特殊なフォルダーを識別する一意のシステムに依存しない方法を提供しますが、特定のシステム上で同じ名前や場所を持たない可能性があります。

オブジェクトの力を借りずに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引数hwndOwnerLong不可呼び出し元のハンドル
※Application.hWndAccessApp
第2引数AppNameString不可実行中のアプリケーションの名前
※「Microsoft Access」など
第3引数DlgTitleString不可ダイアログのタイトル
第4引数OpenTitleString不可選択ボタンのキャプション
第5引数FileString不可選択フォルダのフルパス
※選択したパスが戻り値になります
第6引数InitialDirString不可初期フォルダのフルパス
※ダイアログ起動時の初期フォルダのパス
第7引数FilterString不可ダイアログの[ファイルの種類]の設定
※フォルダ選択時には表示されません。
第8引数FilterIndexLong不可適用するフィルタのインデックス
第9引数ViewLong不可ダイアログの初期表示スタイル
※フォルダ選択ダイアログでは無効
第10引数flagsLong不可ダイアログの動作機能
※フォルダ選択モード:「32」
第11引数fOpenBoolean不可ダイアログの種類
※フォルダ選択ダイアログ: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実行時での表示結果は以下の通りとなります。
vba-wizhook-GetFileName

サンプルの補足

さてAccessの非公開クラスであるWizhookオブジェクトを使ったフォルダ選択ダイアログですがいかがでしょうか。

Wizhookを使用するにあたり、Wizhookを有効化する必要があるので覚えておきましょう。

どの引数も省略不可なので、設定する値がない場合はString型なら空文字、Long型なら「0」を設定しておけば問題ありません。

第6引数InitialDirで初期フォルダを指定したい場合は、第7引数Filterを設定しておかないと正しく動作しないのでご注意ください。

GetFileNameメソッドは戻り値でLong型で返します。

戻り値説明
0選択ボタン押下
-302キャンセルボタン押下

選択したフォルダのパスは第5引数に出力される仕様となっているため、出力用の変数を引数に設定しておく必要があります。

GetFileNameについては一行書きで記述できるのでFileDialogオブジェクトのようなダイアログを簡単に呼び出すことができます。

参照設定不要で使用できるのでモジュール移植時のメンテナンスも負担軽減が見込めます。

ただし非公開クラスについての捉え方次第によっては業務上の開発ではなかなか理解が得られないかもしれません。

まとめ

フォルダ選択ダイログについていくつかご紹介させて頂きましたが、いかがでしたでしょうか。

上の説明にも挙げましたが、基本的にはFileDialogオブジェクトを使用することをマイクロソフトでも推奨しておりますのでその点は鉄板ですが、同じ結果を切り口を変えて模索してみるのは開発者の性とも言えるでしょう。

ベーシックな知識からマニアックな知識まで知識欲が枯渇するまで追求していきましょう!!

今回ご紹介した方法以外にフォルダ選択ダイアログの表示する方法をご存じな方いらっしゃいましたら、ぜひぜひ教えてください。

タイトルとURLをコピーしました