C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

フォルダ選択ダイアログで初期パス指定について

[トピック内 3 記事 (1 - 3 表示)]  << 0 >>

■103002 / inTopicNo.1)  フォルダ選択ダイアログで初期パス指定について
  
□投稿者/ くま (23回)-(2024/03/11(Mon) 12:33:26)

分類:[Microsoft Office 全般] 

したい事
・フォルダ選択ダイアログで初期表示時、外部ドライブを選択した場合[PC]から下の外部ドライブを選択したい。

ソフト Access 2013 または Excel 2013 64bit版
言語 VBA
OS Windows11 64bit版 または Windows11 64bit版

症状
PCドライブ構成
・ローカル ディスク(C:) 内蔵ドライブ
・ドライブ(3)(D:) 外部USBドライブ
・ドライブ(2)(E:) 外部USBドライブ
・ドライブ(1)(F:) 外部USBドライブ

上記構成で「フォルダ選択ダイアログ」を開くと
----------
ホーム
ギャラリー
----------
デスクトップ
ダウンロード
ピクチャ
ミュージック
ビデオ
----------
PC
    ローカル ディスク(C:)
    ドライブ(3)(D:)
    ドライブ(2)(E:)
    ドライブ(1)(F:)
ドライブ(1)(F:)
ドライブ(2)(E:)
ドライブ(3)(D:)
----------

と表示されます。
そこで
SHBrowseForFolder APIを使用して「フォルダ選択ダイアログ」を開いた時のイベントで
初期フォルダ(ドライブ)を指定したのですが、
内蔵ドライブの場合は[例 C:\]
    PC > ローカル ディスク(C:)
と表示され外部ドライブの場合[例 D:\]は
外部ドライブ
   ドライブ(3)(D:)
とPCの下ではない所から表示されてしまいます。
これを
   PC > ドライブ(3)(D:)
からにしたいのですがうまくいきません。

また「フォルダ選択ダイアログ」の表示に関して条件があり
1. フォルダの並び順がエクスプローラーと一緒の「数字を数値として並び変える」順である事。
※ BIF_NEWDIALOGSTYLEで対応済み
2. 新しいダイアログスタイルだとフォルダ内に一定量以上フォルダが存在する場合すべて展開されない症状があるがこれを回避する。
3. 初期表示させるフォルダが画面外の位置になる場合があるのを回避する。
※ 一つ上の階層パスを選択してで展開、その後目的のパスを選択し直すことで回避

上記3つの条件が満たされる事が必須となります。
なにか良い方法をお教え頂ければ幸いです。

下に現在の状態のコードを記載しておきます

引用返信 編集キー/
■103003 / inTopicNo.2)  Re[1]: フォルダ選択ダイアログで初期パス指定について
□投稿者/ くま (24回)-(2024/03/11(Mon) 12:34:35)
2024/03/11(Mon) 12:42:12 編集(投稿者)
'こちらが現在の状態のコードです
'実行は
'FolderDialogOpen_TEST()
'にて試せます。
'また初期パスはサンプルでは"C:\folder1\folder2"になっています。
'
'    FolderDialog_Path = "C:\folder1\folder2"	'フォルダ指定ダイアログ用初期パス
'    FolderDialog_UpPath = "C:\folder1"		'フォルダ指定ダイアログ用初期パス(一つ上)
'

Option Explicit

Private Const MAX_PATH                  As Long = 260
    
Private Declare PtrSafe Function FindWindowEx Lib "user32.dll" Alias "FindWindowExW" ( _
    ByVal hWndParent As LongPtr, _
    ByVal hwndChildAfter As LongPtr, _
    ByVal lpszClass As LongPtr, _
    ByVal lpszWindow As LongPtr) As Long

Private Declare PtrSafe Function SHBrowseForFolder Lib "shell32.dll" Alias "SHBrowseForFolderW" ( _
    lpBrowseInfo As BROWSEINFO) As LongPtr

Private Declare PtrSafe Function SHGetPathFromIDList Lib "shell32.dll" Alias "SHGetPathFromIDListW" ( _
    ByVal pidl As LongPtr, _
    ByVal pszPath As LongPtr) As Long

Private Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageW" ( _
    ByVal hwnd As LongPtr, _
    ByVal wMsg As Long, _
    ByVal wParam As LongPtr, _
    lParam As Any) As Long

Private Declare PtrSafe Sub CoTaskMemFree Lib "ole32.dll" ( _
    ByVal pv As LongPtr)

Public Type BROWSEINFO
    hWndOwner       As LongPtr
    pidlRoot        As Long
    pszDisplayName  As LongPtr
    lpszTitle       As LongPtr
    ulFlags         As Long
    lpfn            As LongPtr
    lParam          As LongPtr
    iImage          As Long
End Type

Private Const BIF_RETURNONLYFSDIRS      As Long = &H1&
Private Const BIF_NEWDIALOGSTYLE        As Long = &H40&
Private Const BIF_NONEWFOLDERBUTTON     As Long = &H200&

Private Const BFFM_INITIALIZED      As Long = 1
Private Const BFFM_SELCHANGED       As Long = 2
Private Const BFFM_VALIDATEFAILEDA  As Long = 3
Private Const BFFM_VALIDATEFAILEDW  As Long = 4
Private Const BFFM_IUNKNOWN         As Long = 5

Private Const WM_USER               As Long = &H400
Private Const BFFM_SETSELECTIONA    As Long = (WM_USER + 102)
Private Const BFFM_SETSELECTIONW    As Long = (WM_USER + 103)
Private Const BFFM_SETEXPANDED      As Long = (WM_USER + 106)

Private Const KEYEVENTF_EXTENDEDKEY     As Long = &H1
Private Const KEYEVENTF_KEYUP           As Long = &H2
Private Const WM_KEYDOWN                As Long = &H100
Private Const WM_KEYUP                  As Long = &H101
Private Const VK_ADD                    As Long = &H6B      '仮想キー(テンキー+)

Private FolderDialog_Path   As String       'フォルダ指定ダイアログ用初期パス
Private FolderDialog_UpPath As String       'フォルダ指定ダイアログ用初期パス(一つ上)
Private FolderDialog_Step   As Integer      'フォルダ指定ダイアログ用実行状況

Public Function FolderDialogOpen_TEST() As Boolean
    
    Dim strPath             As String
    
    Dim tBrowsInfo          As BROWSEINFO
    Dim strDisplayName      As String
    Dim strTitle            As String
    
    Dim objFS               As Object
    Static strPathBuffer    As String
    Dim strPathTemp         As String

    Dim pidl                As LongPtr

On Error GoTo FolderDialogOpen_TEST_Err:
    
    
    FolderDialog_Path = "C:\folder1\folder2"
    FolderDialog_UpPath = "C:\folder1"
    FolderDialog_Step = 0
    
    strDisplayName = String(MAX_PATH * 2, vbNullChar)
    
    With tBrowsInfo
        .hWndOwner = 0
        .pidlRoot = 0
        .pszDisplayName = StrPtr(strDisplayName)
        .lpszTitle = 0
        .ulFlags = BIF_RETURNONLYFSDIRS Or BIF_NEWDIALOGSTYLE Or BIF_NONEWFOLDERBUTTON
        .lpfn = AddressPoint(AddressOf BrowseCallbackProc)
        .lParam = 0
        .iImage = 0
    End With
    
    pidl = SHBrowseForFolder(tBrowsInfo)
    If pidl = 0 Then
        FolderDialogOpen_TEST = False
    Else
        strPath = String(MAX_PATH * 2, vbNullChar)
        Call SHGetPathFromIDList(pidl, ByVal StrPtr(strPath))
        Call CoTaskMemFree(pidl)
        strPath = Left(strPath, InStr(strPath, vbNullChar & vbNullChar) - 1)
        Debug.Print strPath
        FolderDialogOpen_TEST = True
    End If

FolderDialogOpen_TEST_End:
Exit Function

FolderDialogOpen_TEST_Err:
    '----- エラー処理
    FolderDialogOpen_TEST = False
Resume FolderDialogOpen_TEST_End:
End Function

Private Function AddressPoint(lngAddressOf As LongPtr) As LongPtr
    AddressPoint = lngAddressOf
End Function

Private Function BrowseCallbackProc(ByVal hwnd As LongPtr, ByVal uMsg As Long, ByVal lParam As LongPtr, ByVal lpData As LongPtr) As Long
    Dim strPath         As String
    Dim strClassName    As String
    Dim strWindowName   As String
    
    Dim hWndChild       As LongPtr
    Dim hWndTreeView    As LongPtr

    If FolderDialog_Step < 9 Then
        Select Case uMsg
            Case BFFM_INITIALIZED
                '----- 一つ上のフォルダを選択
                If FolderDialog_Step = 0 And Len(FolderDialog_UpPath) > 0 Then
                    Call SendMessage(hwnd, BFFM_SETSELECTIONW, True, ByVal StrPtr(FolderDialog_UpPath))
                    FolderDialog_Step = 1    '処理待機1
                Else
                    FolderDialog_Step = 9    '処理終了
                End If
            
            Case BFFM_SELCHANGED
                If FolderDialog_Step = 2 Then   '処理待機2
                    '----- 選択されたパス取得
                    strPath = String(MAX_PATH * 2, vbNullChar)
                    Call SHGetPathFromIDList(lParam, StrPtr(strPath))
                    strPath = Left(strPath, InStr(strPath, vbNullChar & vbNullChar) - 1)
                    
                    '----- 指定されたパスが選択されている場合
                    If strPath = FolderDialog_UpPath Then
                        '----- フォルダを選択ダイアログのコントロール取得
                        strClassName = "SHBrowseForFolder ShellNameSpace Control"
                        strWindowName = vbNullString
                        hWndChild = FindWindowEx(hwnd, 0, StrPtr(strClassName), StrPtr(strWindowName))
                        
                        If hWndChild <> 0 Then
                            '----- フォルダを選択ダイアログのツリービュー取得
                            strClassName = "SysTreeView32"
                            strWindowName = vbNullString
                            hWndTreeView = FindWindowEx(hWndChild, 0, StrPtr(strClassName), StrPtr(strWindowName))
                            
                            If hWndChild <> 0 Then
                                '----- テンキー[+]を送信(ツリー展開)
                                Call SendMessage(hWndTreeView, WM_KEYDOWN, VK_ADD, 0)
                                Call SendMessage(hWndTreeView, WM_KEYUP, VK_ADD, 0)
                                
                                '----- フォルダを選択
                                If FolderDialog_UpPath <> FolderDialog_Path _
                                And Len(FolderDialog_Path) > 0 Then
                                    Call SendMessage(hwnd, BFFM_SETSELECTIONW, True, ByVal StrPtr(FolderDialog_Path))
                                End If
                            End If
                        End If
                        FolderDialog_Step = 9    '処理終了
                    End If
                End If
            
            Case BFFM_VALIDATEFAILEDA
            Case BFFM_VALIDATEFAILEDW
            Case BFFM_IUNKNOWN
                If FolderDialog_Step < 2 Then FolderDialog_Step = 2   '処理待機2
        End Select
    End If
End Function


引用返信 編集キー/
■103007 / inTopicNo.3)  Re[2]: フォルダ選択ダイアログで初期パス指定について
□投稿者/ くま (25回)-(2024/03/13(Wed) 07:27:42)
2024/03/13(Wed) 07:37:35 編集(投稿者)
No103003 (くま さん) に返信
自己解決できましたので解決とします。
修正箇所は以下の通り

Private Function BrowseCallbackProc(ByVal hwnd As LongPtr, ByVal uMsg As Long, ByVal lParam As LongPtr, ByVal lpData As LongPtr) As Long
    Dim strPath         As String
    Dim strClassName    As String
    Dim strWindowName   As String
    
    Dim hWndChild       As LongPtr
    Dim hWndTreeView    As LongPtr

    Const MY_COMPUTER_FOLDER    As String = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"

    If FolderDialog_Step < 9 Then
        Select Case uMsg
            Case BFFM_INITIALIZED
                '----- 一つ上のフォルダを選択
                If FolderDialog_Step = 0 And Len(FolderDialog_UpPath) > 0 Then
                    strPath = MY_COMPUTER_FOLDER
                    If Len(FolderDialog_UpPath) > 0 Then
                        strPath = strPath & "\" & FolderDialog_UpPath
                    End If
                    Call SendMessage(hwnd, BFFM_SETSELECTIONW, True, ByVal StrPtr(strPath))
                    FolderDialog_Step = 1    '処理待機1
                Else
                    FolderDialog_Step = 9    '処理終了
                End If
            
            Case BFFM_SELCHANGED
                If FolderDialog_Step = 2 Then   '処理待機2
                    '----- 選択されたパス取得
                    strPath = String(MAX_PATH * 2, vbNullChar)
                    Call SHGetPathFromIDList(lParam, StrPtr(strPath))
                    strPath = Left(strPath, InStr(strPath, vbNullChar & vbNullChar) - 1)

                    '----- 指定されたパスが選択されている場合
                    If strPath = FolderDialog_UpPath Then
                        '----- フォルダを選択ダイアログのコントロール取得
                        strClassName = "SHBrowseForFolder ShellNameSpace Control"
                        strWindowName = vbNullString
                        hWndChild = FindWindowEx(hwnd, 0, StrPtr(strClassName), StrPtr(strWindowName))

                        If hWndChild <> 0 Then
                            '----- フォルダを選択ダイアログのツリービュー取得
                            strClassName = "SysTreeView32"
                            strWindowName = vbNullString
                            hWndTreeView = FindWindowEx(hWndChild, 0, StrPtr(strClassName), StrPtr(strWindowName))

                            If hWndChild <> 0 Then
                                '----- テンキー[+]を送信(ツリー展開)
                                Call SendMessage(hWndTreeView, WM_KEYDOWN, VK_ADD, 0)
                                Call SendMessage(hWndTreeView, WM_KEYUP, VK_ADD, 0)

                                '----- フォルダを選択
                                If FolderDialog_UpPath <> FolderDialog_Path _
                                And Len(FolderDialog_Path) > 0 Then
                                    strPath = MY_COMPUTER_FOLDER
                                    If Len(FolderDialog_Path) > 0 Then
                                        strPath = strPath & "\" & FolderDialog_Path
                                    End If
                                    Call SendMessage(hwnd, BFFM_SETSELECTIONW, True, ByVal StrPtr(strPath))
'                                    Call SendMessage(hwnd, BFFM_SETSELECTIONW, True, ByVal StrPtr(FolderDialog_Path))
                                End If
                            End If
                        End If
                        FolderDialog_Step = 9    '処理終了
                    End If
                End If
            
            Case BFFM_VALIDATEFAILEDA
            Case BFFM_VALIDATEFAILEDW
            Case BFFM_IUNKNOWN
                If FolderDialog_Step < 2 Then FolderDialog_Step = 2   '処理待機2
        End Select
    End If
End Function


解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ