● フォルダ選択ダイアログ ●

なんとな〜くできちゃった初期ディレクトリを指定できるバージョン。

さて、関数へ行く前にAPI宣言、定数などたくさんあるけどこれは適当に見てね。さて、この関数では初期ディレクトリを指定して、フォルダ選択ダイアログを表示するようにしているんだけど、このような場合、コールバック関数によりメッセージをフックしなければならない。初期ディレクトリを選択状態にして表示するには、フォルダ選択ダイアログが表示されるときに送られてくるメッセージ BFFM_INITIALIZED を拾い、 SendMessage 関数で初期選択ディレクトリを指定すればいい。

'SHBrowseForFolderで使用する構造体
Private Type BROWSEINFO
        hwndOwner As Long             '親Windowのハンドル
        pidlRoot As Long              'ルートフォルダ種別
        pszDisplayName As String      '対象フォルダがExploreで表示される場合の文字
        lpszTitle As String           'ダイアログに表示するメッセージ
        ulFlags As Long               'オプション
        lpfn As Long                  'コールバック関数アドレス
        lParam As Long                'コールバック関数用パラメータ
        iImage As Long
End Type

'ブラウズフォルダダイアログを表示する
Private Declare Function SHBrowseForFolder Lib "shell32.dll" Alias "SHBrowseForFolderA" (lpBROWSEINFO As BROWSEINFO) As Long

'ブラウズフォルダダイアログで指定されたフォルダのパスを取得する
Private Declare Function SHGetPathFromIDList Lib "shell32.dll" Alias "SHGetPathFromIDListA" (ByVal pidl As Long, ByVal pszPath As String) As Long

Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long)

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

'メモリーをコピーする
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal ByteLen As Long)

Private Declare Function lstrlenW Lib "kernel32" (ByVal lpString As Long) As Long

'ルートフォルダ定数
Private Const CSIDL_DESKTOP = &H0                      'デスクトップ
Private Const CSIDL_INTERNET = &H1                     'インターネット
Private Const CSIDL_PROGRAMS = &H2                     'プログラム
Private Const CSIDL_CONTROLS = &H3                     'コントロールパネル
Private Const CSIDL_PRINTERS = &H4                     'プリンター
Private Const CSIDL_PERSONAL = &H5                     'パーソナル
Private Const CSIDL_FAVORITES = &H6                    'ブックマーク
Private Const CSIDL_STARTUP = &H7                      'スタートアップ
Private Const CSIDL_RECENT = &H8                       '最近使ったファイル
Private Const CSIDL_SENDTO = &H9                       '送る
Private Const CSIDL_BITBUCKET = &HA                    'ごみ箱
Private Const CSIDL_STARTMENU = &HB                    'スタートメニュー
Private Const CSIDL_DESKTOPDIRECTORY = &H10            'デスクトップショートカット
Private Const CSIDL_DRIVES = &H11                      'マイコンピュータ
Private Const CSIDL_NETWORK = &H12                     'ネットワークコンピュータ
Private Const CSIDL_NETHOOD = &H13                     '(Windows\NetHood)
Private Const CSIDL_FONTS = &H14                       'フォント
Private Const CSIDL_TEMPLATES = &H15                   'Shell New
Private Const CSIDL_COMMON_STARTMENU = &H16            'スタートメニュー
Private Const CSIDL_COMMON_PROGRAMS = &H17             'プログラム
Private Const CSIDL_COMMON_STARTUP = &H18              'スタートアップ
Private Const CSIDL_COMMON_DESKTOPDIRECTORY = &H19
Private Const CSIDL_APPDATA = &H1A                     '(Windows\Appdata)
Private Const CSIDL_PRINTHOOD = &H1B                   '(Windows\printhood)

'ulFlags用定数
Private Const BIF_DEFAULT = 0                          '標準
Private Const BIF_RETURNONLYFSDIRS = 1                 'ファイルシステムフォルダのみ選択可能
Private Const BIF_DONTGOBELOWDOMAIN = 2                'ネットワークフォルダーを表示しない
Private Const BIF_STATUSTEXT = 4                       'ステータスエリアを表示
Private Const BIF_RETURNFSANCESTORS = 8                'ファイルシステムアンセスタのみ選択可能
Private Const BIF_BROWSEFORCOMPUTER = &H1000           'コンピュータのみ選択可能
Private Const BIF_BROWSEFORPRINTER = &H2000            'プリンタのみ選択可能
Private Const BIF_BROWSEINCLUDEFILES = &H4000          'フォルダ以外も表示

'SHBrowseForFolderのコールバック処理用の定数
Private Const BFFM_INITIALIZED = 1        '初期化時
Private Const BFFM_SELCHANGED = 2         '選択フォルダ変更時

'POSYMESSAG用
Private Const WM_USER = &H400
Private Const BFFM_SETSTATUSTEXTA = (WM_USER + 100)   'ステータスエリアに表示
Private Const BFFM_ENABLEOK = (WM_USER + 101)         '選択可能
Private Const BFFM_SETSELECTIONA = (WM_USER + 102)    'フォルダ選択
Private Const BFFM_SETSELECTIONW = (WM_USER + 103)    'フォルダ選択 (UNICODE)

Private Const BFFM_SETSTATUSTEXTW = (WM_USER + 104)   '??? UNICODE

以下は関数。

'---------------------------------------------------------------------------
'  関数名 : GetFunctionAddress
'  機能   : フック関数のアドレスを得る
'  引数   : (in) lngAddressOfFunc … フック関数のアドレス
'  返り値 : フック関数のアドレス
'---------------------------------------------------------------------------
Private Function GetFunctionAddress(ByVal lngAddressOfFunc As Long) As Long
    GetFunctionAddress = lngAddressOfFunc
End Function

'---------------------------------------------------------------------------
'  関数名 : BrowseCallbackProc
'  機能   : フォルダ参照用コールバック
'  引数   : (in) hwnd … ハンドル
'             (in) uMsg … ウインドウメッセージ
'             (in) lParam … 追加情報1
'            (in) lpData … 追加情報2
'  返り値 : なし
'---------------------------------------------------------------------------
Private Function BrowseCallbackProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal lParam As Long, _
                              ByVal lpData As Long) As Long

    Select Case uMsg
        Case BFFM_INITIALIZED
            Dim StrLenA As Long   'lpData より受け取った文字列の長さ(ANSI形式として)
            Dim strWBuff() As Byte 'lpData より受け取った文字列データ格納用(Unicode形式として)
            Dim strABuff As String 'lpData より受け取った文字列格納用(ANSI形式として)


            'lpData にはUnicode文字列のポインタが送られてくる
            '例えば、C:\Temp\Abc.txt であれば、内部ではでは
            '        C, ,:, ,\, ,T, ,e, ,m, ,p, ,\, ,A, ,b, ,c, ,., ,t, ,x, ,t, , …@
            '        注)","は(カンマ)単に分かりやすいように区切りとして使用したものであり
            '            メモリー上に存在しているわけではない。
            'となっている。したがって Unicode 文字としての文字列の長さを取得しなければ
            'ならない。上の例で言うと lstrlenW(lpData)より15が得られる。ここまでは Unicode
            '文字として扱えばいいが、これ以降はANSI文字として扱わねばならない。(CopyMemory
            '関数はANSI文字として扱われているようなので…)実際はメモリーには@の様になって
            'なっているので、ANSI文字として扱うと30バイトになる。これはlstrlenW(lpData) に
            '2をかけた値に等しい。よって下のコードのようになる。

            StrLenA = lstrlenW(lpData) * 2

            'メモリー確保
            ReDim Preserve strWBuff(StrLenA) As Byte

            'メモリコピー
            Call CopyMemory(strWBuff(0), ByVal lpData, StrLenA)

            'バイト → 文字列変換
            strABuff = strWBuff()

            Call SendMessage(hWnd, BFFM_SETSELECTIONA, True, ByVal strABuff)

            'Windows NT はこれだけで動くけどWindows95は駄目みたい
            'Call SendMessage(hWnd, BFFM_SETSELECTIONW, True, ByVal lpData)
    End Select

End Function

'---------------------------------------------------------------------------
'  関数名 : ShowFolderDlg
'  機能   : フォルダ参照ダイアログを表示する
'  引数   : (in) frmhWnd … フォームのウインドウハンドル
'            (in) IFolder … 初期フォルダ
'            (in) dlgMessage … ダイアログで表示するメッセージ
'  返り値 : 正常:選択されたフォルダのパス、エラー:空文字列
'---------------------------------------------------------------------------
Public Function ShowFolderDlg(ByVal frmhWnd As Long, _
                              Optional ByVal IFolder As String = "", _
                              Optional ByVal dlgMessage As String = "フォルダを選択してください") As String

    Dim udtBROWSEINFO As BROWSEINFO  'BROWSEINFO構造体
    Dim pidl As Long                      'SHGetPathFromIDListの第1引数で指定する
    Dim PathName As String * 256    '取得するフォルダ名
    Dim tmpStrBuf As String

    With udtBROWSEINFO
        .hwndOwner = frmhWnd
        .pidlRoot = CSIDL_DESKTOP  'ルートフォルダを設定
        .lpszTitle = dlgMessage      'ダイアログで表示するメッセージを設定
        .ulFlags = BIF_DEFAULT  'フォルダ表示/選択のフラグ

        If IFolder <> "" Then
            IFolder = IFolder & Chr$(0) & Chr$(0)

            .lpfn = GetFunctionAddress(AddressOf BrowseCallbackProc) 'コールバック

            tmpStrBuf = String$(LenB(IFolder), Chr$(0))

            '確保した領域にポインタをコピーする
            Call CopyMemory(ByVal tmpStrBuf, ByVal IFolder, LenB(IFolder))

            .lParam = StrPtr(tmpStrBuf)    '指定された初期フォルダへのポインタを指定
        End If
    End With

    'ダイアログを呼び出す
    pidl = SHBrowseForFolder(udtBROWSEINFO)

    If pidl Then
        If SHGetPathFromIDList(pidl, PathName) Then
            'フォルダのフルパスを取得する
            ShowFolderDlg = Left$(PathName, InStr(PathName, Chr$(0)) - 1)
        End If

        '割り当てられたメモリを開放
        Call CoTaskMemFree(pidl)
    End If

  End Function

戻る