SHBrowseForFolder クラス
名づけて、VB 汎用モジュール アーカイブプロジェクト、第一弾です。いつまでもVB6使ってますが、有用なモジュールが結構たまっているのでちょっとづつ整理して行こうと思っています。
このモジュールは以下のようにしてフォルダ選択ダイアログを表示します。ちゃんと初期フォルダも選択されます。
Private Sub Command1_Click() Dim objBrowse As New CBrowseFolder objBrowse.Flags = BIF_NEWDIALOGSTYLE objBrowse.InitDir = CurDir() Set objBrowse.Owner = Me objBrowse.RootFolder = CSIDL_DESKTOP objBrowse.Title = "あなたの一番大事なフォルダを選択してください" Call MsgBox(objBrowse.Show()) End Sub
メインのクラスモジュール CBrowseFolder.cls です。ポイントは BROWSEINFO 構造体の lParam
に自身のポインタを格納しておくことです。これによってコールバックプロシージャより、呼びもとのインスタンスを参照することができます。これさえできれば自由自在ですよね。
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "CBrowseFolder" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = False Option Explicit Private Const mcstrMODULE_NAME As String = "CBrowseFolder" Private Const MAX_PATH As Long = 260& Private Type tagBROWSEINFO hOwner As Long pidlRoot As Long pszDisplayName As Long 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, _ ByRef pszPath As Any _ ) As Long Private Declare Sub CoTaskMemFree Lib "ole32.dll" ( _ ByVal pv As Long _ ) Public Enum gEnumBrowseInfoFlag '// Browsing for directory. BIF_RETURNONLYFSDIRS = &H1& '// For finding a folder to start document searching BIF_DONTGOBELOWDOMAIN = &H2& '// For starting the Find Computer BIF_STATUSTEXT = &H4& '// Top of the dialog has 2 lines of text for BROWSEINFO.lpszTitle and one line if '// this flag is set. Passing the message BFFM_SETSTATUSTEXTA to the hwnd can set the '// rest of the text. This is not used with BIF_USENEWUI and BROWSEINFO.lpszTitle gets '// all three lines of text. BIF_RETURNFSANCESTORS = &H8& BIF_EDITBOX = &H10& '// Add an editbox to the dialog BIF_VALIDATE = &H20& '// insist on valid result (or CANCEL) BIF_NEWDIALOGSTYLE = &H40& '// Use the new dialog layout with the ability to resize '// Caller needs to call OleInitialize() before using this API BIF_USENEWUI = (BIF_NEWDIALOGSTYLE Or BIF_EDITBOX) BIF_BROWSEINCLUDEURLS = &H80& '// Allow URLs to be displayed or entered. (Requires BIF_USENEWUI) BIF_UAHINT = &H100& '// Add a UA hint to the dialog, in place of the edit box. May not be combined with BIF_EDITBOX BIF_NONEWFOLDERBUTTON = &H200& '// Do not add the "New Folder" button to the dialog. Only applicable with BIF_NEWDIALOGSTYLE. BIF_NOTRANSLATETARGETS = &H400& '// don't traverse target as shortcut BIF_BROWSEFORCOMPUTER = &H1000& '// Browsing for Computers. BIF_BROWSEFORPRINTER = &H2000& '// Browsing for Printers BIF_BROWSEINCLUDEFILES = &H4000& '// Browsing for Everything BIF_SHAREABLE = &H8000& '// sharable resources displayed (remote shares, requires BIF_USENEWUI) End Enum Public Enum gEnumCSIDL CSIDL_DESKTOP = &H0& '// <desktop> CSIDL_INTERNET = &H1& '// Internet Explorer (icon on desktop) CSIDL_PROGRAMS = &H2& '// Start Menu\Programs CSIDL_CONTROLS = &H3& '// My Computer\Control Panel CSIDL_PRINTERS = &H4& '// My Computer\Printers CSIDL_PERSONAL = &H5& '// My Documents CSIDL_FAVORITES = &H6& '// <user name>\Favorites CSIDL_STARTUP = &H7& '// Start Menu\Programs\Startup CSIDL_RECENT = &H8& '// <user name>\Recent CSIDL_SENDTO = &H9& '// <user name>\SendTo CSIDL_BITBUCKET = &HA& '// <desktop>\Recycle Bin CSIDL_STARTMENU = &HB& '// <user name>\Start Menu CSIDL_MYDOCUMENTS = &HC& '// logical "My Documents" desktop icon CSIDL_MYMUSIC = &HD& '// "My Music" folder CSIDL_MYVIDEO = &HE& '// "My Videos" folder CSIDL_DESKTOPDIRECTORY = &H10& '// <user name>\Desktop CSIDL_DRIVES = &H11& '// My Computer CSIDL_NETWORK = &H12& '// Network Neighborhood (My Network Places) CSIDL_NETHOOD = &H13& '// <user name>\nethood CSIDL_FONTS = &H14& '// windows\fonts CSIDL_TEMPLATES = &H15& CSIDL_COMMON_STARTMENU = &H16& '// All Users\Start Menu CSIDL_COMMON_PROGRAMS = &H17& '// All Users\Start Menu\Programs CSIDL_COMMON_STARTUP = &H18& '// All Users\Startup CSIDL_COMMON_DESKTOPDIRECTORY = &H19& '// All Users\Desktop CSIDL_APPDATA = &H1A& '// <user name>\Application Data CSIDL_PRINTHOOD = &H1B& '// <user name>\PrintHood CSIDL_LOCAL_APPDATA = &H1C& '// <user name>\Local Settings\Applicaiton Data (non roaming) CSIDL_ALTSTARTUP = &H1D& '// non localized startup CSIDL_COMMON_ALTSTARTUP = &H1E& '// non localized common startup CSIDL_COMMON_FAVORITES = &H1F& CSIDL_INTERNET_CACHE = &H20& CSIDL_COOKIES = &H21& CSIDL_HISTORY = &H22& CSIDL_COMMON_APPDATA = &H23& '// All Users\Application Data CSIDL_WINDOWS = &H24& '// GetWindowsDirectory() CSIDL_SYSTEM = &H25& '// GetSystemDirectory() CSIDL_PROGRAM_FILES = &H26& '// C:\Program Files CSIDL_MYPICTURES = &H27& '// C:\Program Files\My Pictures CSIDL_PROFILE = &H28& '// USERPROFILE CSIDL_SYSTEMX86 = &H29& '// x86 system directory on RISC CSIDL_PROGRAM_FILESX86 = &H2A& '// x86 C:\Program Files on RISC CSIDL_PROGRAM_FILES_COMMON = &H2B& '// C:\Program Files\Common CSIDL_PROGRAM_FILES_COMMONX86 = &H2C& '// x86 Program Files\Common on RISC CSIDL_COMMON_TEMPLATES = &H2D& '// All Users\Templates CSIDL_COMMON_DOCUMENTS = &H2E& '// All Users\Documents CSIDL_COMMON_ADMINTOOLS = &H2F& '// All Users\Start Menu\Programs\Administrative Tools CSIDL_ADMINTOOLS = &H30& '// <user name>\Start Menu\Programs\Administrative Tools CSIDL_CONNECTIONS = &H31& '// Network and Dial-up Connections CSIDL_COMMON_MUSIC = &H35& '// All Users\My Music CSIDL_COMMON_PICTURES = &H36& '// All Users\My Pictures CSIDL_COMMON_VIDEO = &H37& '// All Users\My Video CSIDL_RESOURCES = &H38& '// Resource Direcotry CSIDL_RESOURCES_LOCALIZED = &H39& '// Localized Resource Direcotry CSIDL_COMMON_OEM_LINKS = &H3A& '// Links to All Users OEM specific apps CSIDL_CDBURN_AREA = &H3B& '// USERPROFILE\Local Settings\Application Data\Microsoft\CD Burning End Enum 'プロパティ値を保持するためのローカル変数。 Private mobjOwner As Object 'ローカル コピー Private menmRootFolder As gEnumCSIDL 'ローカル コピー Private mstrTitle As String 'ローカル コピー Private menmFlags As gEnumBrowseInfoFlag 'ローカル コピー Private mstrInitDir As String 'ローカル コピー Private mstrDisplayName As String * MAX_PATH '****************************************************************** 'プロパティ名: :Let InitDir '説明: :初期ディレクトリを設定する '引数: :pstrValue I 初期ディレクトリ '****************************************************************** Public Property Let InitDir(pstrValue As String) mstrInitDir = pstrValue End Property '****************************************************************** 'プロパティ名: :Get InitDir '説明: :初期ディレクトリを取得する '戻り値 :String型 '****************************************************************** Public Property Get InitDir() As String InitDir = mstrInitDir End Property '****************************************************************** 'プロパティ名: :Let Flags '説明: :フラグを設定する '引数: :penmValue I gEnumBrowseInfoFlagをOrで組み合わせて指定 '****************************************************************** Public Property Let Flags(ByVal penmValue As gEnumBrowseInfoFlag) menmFlags = penmValue End Property '****************************************************************** 'プロパティ名: :Get Flags '説明: :フラグを取得する '戻り値 :gEnumBrowseInfoFlag型 '****************************************************************** Public Property Get Flags() As gEnumBrowseInfoFlag Flags = menmFlags End Property '****************************************************************** 'プロパティ名: :Let Title '説明: :ダイアログのタイトルを設定する '引数: :pstrValue I ダイアログのタイトル '****************************************************************** Public Property Let Title(pstrValue As String) mstrTitle = pstrValue End Property '****************************************************************** 'プロパティ名: :Get Title '説明: :ダイアログのタイトルを取得する '戻り値 :String型 '****************************************************************** Public Property Get Title() As String Title = mstrTitle End Property '****************************************************************** 'プロパティ名: :Let RootFolder '説明: :フォルダツリーのルートをどこにするかを指定する '引数: :penmValue I gEnumCSIDLの中から指定する '****************************************************************** Public Property Let RootFolder(ByVal penmValue As gEnumCSIDL) menmRootFolder = penmValue End Property '****************************************************************** 'プロパティ名: :Get RootFolder '説明: :ルートフォルダを取得する '戻り値 :gEnumCSIDL型 '****************************************************************** Public Property Get RootFolder() As gEnumCSIDL RootFolder = menmRootFolder End Property '****************************************************************** 'プロパティ名: :Set Owner '説明: :オーナーとなるフォームを指定する '引数: :pobjValue I オーナーフォーム '****************************************************************** Public Property Set Owner(pobjValue As Object) Set mobjOwner = pobjValue End Property '****************************************************************** 'プロパティ名: :Get Owner '説明: :オーナーフォームを取得する '戻り値 :Object型 '****************************************************************** Public Property Get Owner() As Object Set Owner = mobjOwner End Property '****************************************************************** 'プロシージャ名: :Show '説明: :フォルダ選択ダイアログを表示する '戻り値 :String型 '****************************************************************** Public Function Show() As String '============= Declare ============== Dim udtBI As tagBROWSEINFO Dim lngPIDL As Long Dim strPath As String * MAX_PATH '============= Initiarize =========== On Error GoTo Show_E00 Show = "" '============= Main ================= With udtBI If mobjOwner Is Nothing Then .hOwner = 0& Else .hOwner = mobjOwner.hWnd End If .pidlRoot = menmRootFolder .pszDisplayName = StrPtr(mstrDisplayName) .lpszTitle = mstrTitle .ulFlags = menmFlags .lpfn = ProcPtr(AddressOf BrowseCallbackProc) .lParam = ObjPtr(Me) End With lngPIDL = SHBrowseForFolder(udtBI) ' ダイアログ表示 If lngPIDL Then On Error GoTo Show_E10 If SHGetPathFromIDList(lngPIDL, ByVal strPath) Then Show = Left(strPath, InStr(strPath, vbNullChar) - 1&) End If Call CoTaskMemFree(lngPIDL) ' パス格納のために確保されたメモリを開放 On Error GoTo Show_E00 End If '============= Release ============== Show_L00: Exit Function '============= Error ================ Show_E10: Call CoTaskMemFree(lngPIDL) ' パス格納のために確保されたメモリを開放 Show_E00: Call Err.Raise(vbObjectError Or Err.Number, mcstrMODULE_NAME & ".Show" & "(" & Err.Source & ")", Err.Description, Err.HelpFile, Err.HelpContext) End Function '------------------------------------------------------------------ 'プロシージャ名: :ProcPtr '説明: :AddressOfのラッパ関数 '引数: :plngAddressOfFunc I コールバックプロシージャのアドレス '戻り値 :Long型 '------------------------------------------------------------------ Private Function ProcPtr(ByVal plngAddressOfFunc As Long) As Long ProcPtr = plngAddressOfFunc End Function '------------------------------------------------------------------ 'プロシージャ名: :Class_Initialize '説明: : '------------------------------------------------------------------ Private Sub Class_Initialize() Set mobjOwner = Nothing menmRootFolder = CSIDL_DESKTOP mstrTitle = "" menmFlags = 0& mstrInitDir = CurDir() mstrDisplayName = String$(MAX_PATH, vbNullChar) End Sub '------------------------------------------------------------------ 'プロシージャ名: :Class_Terminate '説明: : '------------------------------------------------------------------ Private Sub Class_Terminate() End Sub
コールバック関数のために、どうしたって標準モジュールが必要になります。
Attribute VB_Name = "MBrowseFolder" Option Explicit Private Const mcstrMODULE_NAME As String = "MBrowseFolder" Private Const WM_USER As Long = &H400& Private Const BFFM_INITIALIZED As Long = 1& 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 SendMessage Lib "user32" _ Alias "SendMessageA" ( _ ByVal hWnd As Long, _ ByVal wMsg As Long, _ ByVal wParam As Long, _ lParam As Any _ ) As Long Private Const FORMAT_MESSAGE_ALLOCATE_BUFFER As Long = &H100& Private Const FORMAT_MESSAGE_IGNORE_INSERTS As Long = &H200& Private Const FORMAT_MESSAGE_FROM_STRING As Long = &H400& Private Const FORMAT_MESSAGE_FROM_HMODULE As Long = &H800& Private Const FORMAT_MESSAGE_FROM_SYSTEM As Long = &H1000& Private Const FORMAT_MESSAGE_ARGUMENT_ARRAY As Long = &H2000& Private Const FORMAT_MESSAGE_MAX_WIDTH_MASK As Long = &HFF& Private Declare Function FormatMessage Lib "KERNEL32" Alias "FormatMessageA" (ByVal dwFlags As Long, lpSource As Any, ByVal dwMessageId As Long, ByVal dwLanguageId As Long, lpBuffer As Any, ByVal nSize As Long, Arguments As Long) As Long '****************************************************************** 'プロシージャ名: :BrowseCallbackProc '説明: :フォルダ選択ダイアログのコールバック関数 '引数: :hwnd I フォルダ選択ダイアログのウィンドウハンドル ' :uMsg I メッセージの種類 ' :lParam I ' :lpData I BROWSEINFO構造体に与えたlParam '戻り値 :Long型 '****************************************************************** Public Function BrowseCallbackProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal lParam As Long, ByVal lpData As CBrowseFolder) As Long '============= Declare ============== Dim strData As String '============= Initiarize =========== On Error GoTo BrowseCallbackProc_E00 '============= Main ================= Select Case uMsg Case BFFM_INITIALIZED strData = lpData.InitDir Call SendMessage(hWnd, BFFM_SETSELECTIONA, 1, ByVal strData) Case Else End Select '============= Release ============== BrowseCallbackProc_L00: Exit Function '============= Error ================ BrowseCallbackProc_E00: Call Err.Raise(vbObjectError Or Err.Number, mcstrMODULE_NAME & ".BrowseCallbackProc" & "(" & Err.Source & ")", Err.Description, Err.HelpFile, Err.HelpContext) End Function '****************************************************************** 'プロシージャ名: :pDllErrRaise '説明: :Err.LastDllErrorよりエラーを発生する '引数: :plngLastDllError I Err.LastDllError ' :pstrErrSource O エラーソース文字列 '****************************************************************** Public Sub DllErrRaise(ByVal plngLastDllError As Long, pstrErrSource As String) '============= Declare ============== Const clngMAX_BUFFER As Long = 1024& Dim bytBuf(clngMAX_BUFFER - 1&) As Byte 'バッファ Dim lngRet As Long 'APIの戻り値 Dim strErrDescription As String '============= Initiarize =========== If plngLastDllError = 0& Then Exit Sub End If Erase bytBuf strErrDescription = "" '============= Main ================= '@@ エラー値に対応するメッセージを取得する lngRet = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS, _ ByVal 0&, _ plngLastDllError, _ 0&, _ bytBuf(0), _ clngMAX_BUFFER, _ ByVal 0&) If lngRet Then strErrDescription = StrConv(LeftB(bytBuf, lngRet), vbUnicode) End If '@@ エラーを発生する Call Err.Raise(plngLastDllError Or vbObjectError, pstrErrSource, strErrDescription) End Sub
------------------------
友人より、OleInitialize
呼んどけって書いてあるよ、という指摘を受けましたが、OleInitialize
って、VB アプリであれば、最初に呼ばれてるからOKという認識でいたのですが、どうなんでしょう。
トラックバック
- このエントリーにトラックバック:
- http://frog.raindrop.jp/cgi-bin/mt/mt-tb.cgi/291
コメント
VBでフォルダ選択の実装方法を探していてここに来ました。
よそにもたくさん例があったのですが、クラス化されていて使いやすいので参考にさせていただきます。
プロの方なのでしょうか???
kiraさん、いらっしゃいませ。
> プロの方なのでしょうか???
プログラミングやシステム設計で飯を食ってるので、一応プロの方です 笑。