フォルダの選択ダイアログを表示するコード で、デフォルトのフォルダ選択をするためにコールバックを使うのですが、 そこにコードをちょこっと足すといろんな事ができそうです。
古いタイプのファイルの選択ダイアログで、選択中のディレクトリのフルパスが表示されたりしていたが、そういったことができないだろうかと思って実験してみた。
コールバックでBFFM_SELCHANGEDメッセージは、ツリービューのアイテムが選択されるたびにコールバックされるメッセージであるので、ここでパスを解析し、表示すればいい。
表示するため、SHBrowseForFolderのBROWSEINFO構造体のulFlagsにBIF_STATUSTEXTフラグをセットする。
流れとしては、
Case BFFM_SELCHANGED
strBuffer = String(MAX_PATH + 1, vbNullChar)
' 現在選択されているフォルダのパスを得る。
lngResult = SHGetPathFromIDList(lParam, strBuffer)
' 成功した場合。
If lngResult <> 0 Then
' バッファから有効な文字列を取り出す。
strDir = Left$(strBuffer, InStr(strBuffer, vbNullChar) - 1)
' テキストをセットする。
Call SendMessage(hwnd, _
BFFM_SETSTATUSTEXTA, _
0, _
ByVal strDir)
Else
' テキストを削除する。
Call SendMessage(hwnd, _
BFFM_SETSTATUSTEXTA, _
0, _
ByVal 0&)
End If
しかし、上記のコードだけでは、フルパスが長い場合うまく表示しきれない。やはり「...」などのようにパスの一部を省略させて表示させたい。
そういった希望をかなえるAPIとしては、PathCompactPathがある。残念ながら、SHLWAPI.DLLに実装されているので、IEが古いバージョンの場合APIが無いかもしれない。しかし、今更IE4未満の環境のことなど考慮に入れる必要なんて無いだろう。 (^_^;)
Call PathCompactPath(lngDCHandle, strDir, mlngStaticWidth)
さて、PathCompactPathには、引数としてhDC(デバイスコンテキストハンドル)が必要だ。これは、GetDCで得ることにする。お約束として使用後にはReleaseDCでハンドルを開放する必要がある。
もうひとつの引数に、ラベルの幅を得る必要がある。BFFM_INITIALIZEDメッセージのときに、
lngStaticHandle = FindWindowEx(hwnd, 0, "static", vbNullString)
If lngStaticHandle <> 0 Then
Call GetWindowRect(lngStaticHandle, rc)
mlngStaticWidth = rc.Right - rc.Left
End If
とすれば、モジュール変数mlngStaticWidthに幅が入る。ダイアログには、クラス名「static」は2つ存在するのだが、どちらも同じ幅のようなので気にしない気にしない。 (^_^;)
そういった訳で、全体のコードは以下のようになった。
Attribute VB_Name = "BrowseForFolders" ' ===================================== ' BrowseForFolders(BFolder.Bas) ' Niiyama(HEROPA) ' heropa@dream.com ' ===================================== Option Explicit ' BROWSEINFO構造体 Private Type tagBROWSEINFO hOwner As Long pidlRoot As Long pszDisplayName As String 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 tagBROWSEINFO) 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 Const MAX_PATH = 260 Private Const WM_USER = &H400 Private Const BFFM_INITIALIZED = 1 Private Const BFFM_SELCHANGED = 2 Private Const BFFM_SETSTATUSTEXTA As Long = (WM_USER + 100) Private Const BFFM_ENABLEOK As Long = (WM_USER + 101) Private Const BFFM_SETSELECTIONA As Long = (WM_USER + 102) Private Declare Function LocalAlloc Lib "kernel32" _ (ByVal uFlags As Long, _ ByVal uBytes As Long) As Long Private Declare Function LocalFree Lib "kernel32" _ (ByVal hMem As Long) As Long Private Const LMEM_FIXED = &H0 Private Const LMEM_ZEROINIT = &H40 Private Const LPTR = (LMEM_FIXED Or LMEM_ZEROINIT) Public Enum BrowseInfoFlags ' ディレクトリ以外は選択できないようにする。 ' ディレクトリではないフォルダを選択しても、 ' OKボタンが有効にならない。 BIF_RETURNONLYFSDIRS = &H1 ' ネットワークドライブの選択はできないようにする。 BIF_DONTGOBELOWDOMAIN = &H2 BIF_STATUSTEXT = &H4 BIF_RETURNFSANCESTORS = &H8 ' エディットボックスを表示する。 BIF_EDITBOX = &H10 BIF_VALIDATE = &H20 ' コンピュータの選択しかできないようにする。 BIF_BROWSEFORCOMPUTER = &H1000 ' プリンタの選択しかできないようにする。 BIF_BROWSEFORPRINTER = &H2000 ' ファイルも選択できるようにする。 BIF_BROWSEINCLUDEFILES = &H4000 End Enum ' --------追加-------- Private Type tagRECT Left As Long Top As Long Right As Long Bottom As Long End Type Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _ (ByVal hwndParent As Long, _ ByVal hwndChildAfter As Long, _ ByVal lpszClass As String, _ ByVal lpszWindow As String) As Long Private Declare Function GetWindowRect Lib "user32" _ (ByVal hwnd As Long, _ lpRect As tagRECT) As Long Private Declare Function GetDC Lib "user32" _ (ByVal hwnd As Long) As Long Private Declare Function ReleaseDC Lib "user32" _ (ByVal hwnd As Long, _ ByVal hdc As Long) As Long Private Declare Function PathCompactPath Lib "SHLWAPI.DLL" Alias "PathCompactPathA" _ (ByVal hdc As Long, _ ByVal lpszPath As String, _ ByVal dx As Long) As Long Private mlngStaticWidth As Long ' -------------------- Private mstrDefaultPath As String ' ' コールバックされたメッセージによってメッセージを送る。 ' Private Function BrowseCallbackProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal lParam As Long, ByVal lpData As Long) As Long ' --------追加-------- Dim strBuffer As String Dim lngResult As Long Dim strDir As String Dim lngStaticHandle As Long Dim rc As tagRECT Dim lngDCHandle As Long ' -------------------- Select Case uMsg Case BFFM_INITIALIZED ' 選択されるフォルダを設定する。 Call SendMessage(hwnd, _ BFFM_SETSELECTIONA, _ 1, _ ByVal mstrDefaultPath) ' --------追加-------- lngStaticHandle = FindWindowEx(hwnd, 0, "static", vbNullString) If lngStaticHandle <> 0 Then Call GetWindowRect(lngStaticHandle, rc) mlngStaticWidth = rc.Right - rc.Left End If Case BFFM_SELCHANGED strBuffer = String(MAX_PATH + 1, vbNullChar) ' 現在選択されているフォルダのパスを得る。 lngResult = SHGetPathFromIDList(lParam, strBuffer) ' 成功した場合。 If lngResult <> 0 Then ' バッファから有効な文字列を取り出す。 strDir = Left$(strBuffer, InStr(strBuffer, vbNullChar) - 1) If mlngStaticWidth > 0 Then strDir = Left$(strDir & String(MAX_PATH, vbNullChar), MAX_PATH) lngDCHandle = GetDC(hwnd) ' パスを表示しきれない場合は...で省略する。 Call PathCompactPath(lngDCHandle, strDir, mlngStaticWidth) Call ReleaseDC(hwnd, lngDCHandle) If InStr(strDir, vbNullChar) > 0 Then strDir = Left$(strDir, InStr(strDir, vbNullChar) - 1) End If End If ' テキストをセットする。 Call SendMessage(hwnd, _ BFFM_SETSTATUSTEXTA, _ 0, _ ByVal strDir) Else ' テキストを削除する。 Call SendMessage(hwnd, _ BFFM_SETSTATUSTEXTA, _ 0, _ ByVal 0&) End If ' -------------------- Case Else End Select End Function ' ' ' Private Function FARPROC(pfn As Long) As Long ' AddressOfは標準モジュールのプロシージャを指定しなければならないので、 ' ダミーのプロシージャを実装する。 FARPROC = pfn End Function ' ' フォルダの選択ダイアログを表示する。 ' Public Function ShowFolderDlg(ByVal hOwner As Long, ByVal strPrompt As String, ByVal strDefaultPath As String, ByVal lngFlag As BrowseInfoFlags) As String ' 【引数】 ' hOwner : オーナーとなるウィンドウハンドル ' strPrompt : ダイアログ内に表示される文字列 ' strDefaultPath : デフォルトとなるフルパス ' lngFlag : BROWSEINFO構造体のulFlag Dim bi As tagBROWSEINFO Dim pidl As Long Dim lpSelPath As Long Dim sPath As String * MAX_PATH mstrDefaultPath = strDefaultPath With bi .hOwner = hOwner ' ここの値を0じゃなく、ちゃんとしたpidlを入れると ' ルートとなるフォルダを変更することができる。 ' デフォルトの0というのはデスクトップ。 .pidlRoot = 0 ' ダイアログに表示するプロンプトをセットする。 .lpszTitle = strPrompt ' フラグ値をセットする。 .ulFlags = lngFlag ' コールバックに使用するメソッドをセットする。 .lpfn = FARPROC(AddressOf BrowseCallbackProc) End With ' ダイアログを表示する。 pidl = SHBrowseForFolder(bi) If pidl Then ' pidlからパス名に変換する。 If SHGetPathFromIDList(pidl, sPath) Then ' パス名を取り出す。 ShowFolderDlg = Left$(sPath, InStr(sPath, vbNullChar) - 1) End If ' pidlをメモリから開放する。 Call CoTaskMemFree(pidl) End If ' 文字列のポインタを開放する。 Call LocalFree(lpSelPath) End Function
として、
Private Sub Command1_Click()
Dim strPath As String
strPath = ShowFolderDlg(Me.hWnd, "フォルダを選択してちょんまげ", App.Path, BIF_RETURNONLYFSDIRS Or BIF_STATUSTEXT)
If Len(strPath) > 0 Then
Label1.Caption = "Path : " & strPath
Else
Label1.Caption = "キャンセルされた。"
End If
End Sub