Solved

Browse for folder API - Preselecting a UNC path

Posted on 2001-07-18
9
455 Views
Last Modified: 2012-08-13
HELP!

I have a routine that displays the browse for folder using API calls and a callback to preselect the desired folder. Problem is, it doesn't work for UNC paths only paths that start with drive letters.

This is a serious problem in this case because there is a lot of selections to be made and they are all UNC paths. The paths are remembered but if the user goes to change one I don't want them to have to start from the root directory again. I want the path they last selected to be active when the dialog opens.

The code is rather long and complex but works find. The only thing I can think of is there may be a special call for UNC paths when initializing the path?

Here is the call back code. Very simple....

Public 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
            '
            ' On initialization, set the dialog's
            ' pre-selected folder using the pidl
            ' set as the bi.lParam, and passed back
            ' to the callback as lpData param.
            '
            SendMessage hWnd, BFFM_SETSELECTIONA, True, ByVal lpData
    End Select
End Function
0
Comment
Question by:schworak
  • 5
  • 3
9 Comments
 
LVL 3

Author Comment

by:schworak
Comment Utility
Added note I have just discovered...

The routine above is actually pre-selecting the requested folder. The problem is the display is not matching the selection.

If I pre-select a UNC path there is a delay before the dialog shows up. Then nothing is selected in the window. If I hit the OK button the preselected path is returned.

I now think the problem is simply a matter of the display not expanding the tree to the selected location. Any ideas on this one?
0
 
LVL 4

Expert Comment

by:VincentLawlor
Comment Utility
Try this it gives back a UNC

'****************************************************************/

' General
'   Useage:
'       Add a textbox or label and command button to your form
'       In the command buttons click event Use the method BrowseForFolder passing the name of the control as a parameter


Option Explicit

'common to both methods
Public Type BROWSEINFO
  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

Public Declare Function SHBrowseForFolder Lib _
   "shell32.dll" Alias "SHBrowseForFolderA" _
   (lpBrowseInfo As BROWSEINFO) As Long

Public Declare Function SHGetPathFromIDList Lib _
   "shell32.dll" Alias "SHGetPathFromIDListA" _
   (ByVal pidl As Long, _
   ByVal pszPath As String) As Long

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

Public Declare Function SendMessage Lib "user32" _
   Alias "SendMessageA" _
   (ByVal hWnd As Long, _
   ByVal wMsg As Long, _
   ByVal wParam As Long, _
   lParam As Any) As Long
   
Public Declare Sub MoveMemory Lib "kernel32" _
   Alias "RtlMoveMemory" _
   (pDest As Any, _
    pSource As Any, _
    ByVal dwLength As Long)
   
Public Const MAX_PATH = 260
Public Const WM_USER = &H400
Public Const BFFM_INITIALIZED = 1

'Constants ending in 'A' are for Win95 ANSI
'calls; those ending in 'W' are the wide Unicode
'calls for NT.

'Sets the status text to the null-terminated
'string specified by the lParam parameter.
'wParam is ignored and should be set to 0.
Public Const BFFM_SETSTATUSTEXTA As Long = (WM_USER + 100)
Public Const BFFM_SETSTATUSTEXTW As Long = (WM_USER + 104)

'If the lParam  parameter is non-zero, enables the
'OK button, or disables it if lParam is zero.
'(docs erroneously said wParam!)
'wParam is ignored and should be set to 0.
Public Const BFFM_ENABLEOK As Long = (WM_USER + 101)

'Selects the specified folder. If the wParam
'parameter is FALSE, the lParam parameter is the
'PIDL of the folder to select , or it is the path
'of the folder if wParam is the C value TRUE (or 1).
'Note that after this message is sent, the browse
'dialog receives a subsequent BFFM_SELECTIONCHANGED
'message.
Public Const BFFM_SETSELECTIONA As Long = (WM_USER + 102)
Public Const BFFM_SETSELECTIONW As Long = (WM_USER + 103)
   

'specific to the PIDL method
'Undocumented call for the example. IShellFolder's
'ParseDisplayName member function should be used instead.
Public Declare Function SHSimpleIDListFromPath Lib _
   "shell32" Alias "#162" _
   (ByVal szPath As String) As Long


'specific to the STRING method
Public Declare Function LocalAlloc Lib "kernel32" _
   (ByVal uFlags As Long, _
    ByVal uBytes As Long) As Long
   
Public Declare Function LocalFree Lib "kernel32" _
   (ByVal hMem As Long) As Long

Public Declare Function lstrcpyA Lib "kernel32" _
   (lpString1 As Any, lpString2 As Any) As Long

Public Declare Function lstrlenA Lib "kernel32" _
   (lpString As Any) As Long

Public Const LMEM_FIXED = &H0
Public Const LMEM_ZEROINIT = &H40
Public Const LPTR = (LMEM_FIXED Or LMEM_ZEROINIT)

'windows-defined type OSVERSIONINFO
Public Type OSVERSIONINFO
  OSVSize         As Long
  dwVerMajor      As Long
  dwVerMinor      As Long
  dwBuildNumber   As Long
  PlatformID      As Long
  szCSDVersion    As String * 128
End Type
Public Const VER_PLATFORM_WIN32_NT = 2
Public Declare Function GetVersionEx Lib "kernel32" Alias "GetVersionExA" _
  (lpVersionInformation As OSVERSIONINFO) As Long
Public Sub BrowseForFolder(ctlX As Control)

    Dim spath As String
    'The call can not have a trailing slash, so
    'strip it from the path if present
    spath = UnqualifyPath((ctlX))  'new
    'call the function, returning the path  'selected (or blank if cancelled)
    ctlX = BrowseForFolderByPath(spath, "Browse for Folders")
   
    If (ctlX = "") Then ctlX = spath


End Sub

Public Function BrowseCallbackProcStr(ByVal hWnd As Long, _
                                      ByVal uMsg As Long, _
                                      ByVal lParam As Long, _
                                      ByVal lpData As Long) As Long
                                       
  'Callback for the Browse STRING method.
 
  'On initialization, set the dialog's
  'pre-selected folder from the pointer
  'to the path allocated as bi.lParam,
  'passed back to the callback as lpData param.
 
   Select Case uMsg
      Case BFFM_INITIALIZED
     
         Call SendMessage(hWnd, BFFM_SETSELECTIONA, _
                          True, ByVal lpData)
                         
         Case Else:
         
   End Select
         
End Function
         

Public Function BrowseCallbackProc(ByVal hWnd As Long, _
                                   ByVal uMsg As Long, _
                                   ByVal lParam As Long, _
                                   ByVal lpData As Long) As Long
 
  'Callback for the Browse PIDL method.
 
  'On initialization, set the dialog's
  'pre-selected folder using the pidl
  'set as the bi.lParam, and passed back
  'to the callback as lpData param.
 
   Select Case uMsg
      Case BFFM_INITIALIZED
     
         Call SendMessage(hWnd, BFFM_SETSELECTIONA, _
                          False, ByVal lpData)
                         
         Case Else:
         
   End Select

End Function


Public Function FARPROC(pfn As Long) As Long
 
  'A dummy procedure that receives and returns
  'the value of the AddressOf operator.
 
  'Obtain and set the address of the callback
  'This workaround is needed as you can't assign
  'AddressOf directly to a member of a user-
  'defined type, but you can assign it to another
  'long and use that (as returned here)
 
  FARPROC = pfn

End Function

Public Function BrowseForFolderByPath(sSelPath As String, sName As String) As String
    Dim BI As BROWSEINFO
    Dim pidl As Long
    Dim lpSelPath As Long
    Dim spath As String * MAX_PATH

    With BI
        .hOwner = Screen.ActiveForm.hWnd
        .pidlRoot = 0
        .lpszTitle = "Select a " + sName + " folder"
        .lpfn = FARPROC(AddressOf BrowseCallbackProcStr)
        lpSelPath = LocalAlloc(LPTR, Len(sSelPath))
        MoveMemory ByVal lpSelPath, ByVal sSelPath, Len(sSelPath)
        .lParam = lpSelPath
    End With
   
    pidl = SHBrowseForFolder(BI)
   
    If pidl Then
        If SHGetPathFromIDList(pidl, spath) Then
            BrowseForFolderByPath = Left$(spath, InStr(spath, vbNullChar) - 1)
        End If
        Call CoTaskMemFree(pidl)
    End If
   
    Call LocalFree(lpSelPath)
   
End Function
Public Function BrowseForFolderByPIDL(sSelPath As String) As String
   
    Dim BI As BROWSEINFO
    Dim pidl As Long
    Dim spath As String * MAX_PATH
    With BI
        .hOwner = Screen.ActiveForm.hWnd
        .pidlRoot = 0
        .lpszTitle = "Pre-selecting a folder using the folder's pidl."
        .lpfn = FARPROC(AddressOf BrowseCallbackProc)
        .lParam = GetPIDLFromPath(sSelPath) 'replaces '= SHSimpleIDListFromPath(sSelPath)'
    End With
   
   pidl = SHBrowseForFolder(BI)
   
    If pidl Then
        If SHGetPathFromIDList(pidl, spath) Then
            BrowseForFolderByPIDL = Left$(spath, InStr(spath, vbNullChar) - 1)
        End If          'free the pidl returned by call to SHBrowseForFolder
        Call CoTaskMemFree(pidl)
    End If
 'free the pidl set in call to GetPIDLFromPath
 Call CoTaskMemFree(BI.lParam)
 
End Function

Public Function GetPIDLFromPath(spath As String) As Long
'return the pidl to the path supplied by calling the
'undocumented API #162 (our name SHSimpleIDListFromPath).
'This function is necessary as, unlike documented APIs,
'the API is not implemented in 'A' or 'W' versions.
    If IsWinNT Then
        GetPIDLFromPath = SHSimpleIDListFromPath(StrConv(spath, vbUnicode))
    Else
        GetPIDLFromPath = SHSimpleIDListFromPath(spath)
    End If
End Function

Public Function IsWinNT() As Boolean

#If Win32 Then
    Dim OSV As OSVERSIONINFO
    OSV.OSVSize = Len(OSV)
    'API returns 1 if a successful call
    If GetVersionEx(OSV) = 1 Then
        'PlatformId contains a value representing
        'the OS, so if its VER_PLATFORM_WIN32_NT,        'return true
        IsWinNT = OSV.PlatformID = VER_PLATFORM_WIN32_NT
    End If
#End If

End Function
Public Function UnqualifyPath(spath As String) As String

'qualifying a path usually involves assuring
'that its format is valid, including a trailing slash
'ready for a filename. Since the SHBrowseForFolder API
'will pre-select the path if it contains the trailing
'slash, I call stripping it 'unqualifying the path'.

    If Len(spath) > 0 Then
        If Right$(spath, 1) = "\" Then
            UnqualifyPath = Left$(spath, Len(spath) - 1)
            Exit Function
        End If
    End If
   
    UnqualifyPath = spath

End Function


Vin.
0
 
LVL 3

Author Comment

by:schworak
Comment Utility
Yeah, returning the UNC is no problem. Getting the UNC to be pre-selected when the dialog opens, that is the problem.

Your code has the same problem mine does. If I select a UNC path the dialog opens but no path is selected.

I created a form with a text box (text1) and a button (command1) and this code...

Private Sub Command1_Click()
    Text1 = BrowseForFolderByPath(Text1, "test")
End Sub

0
 
LVL 3

Author Comment

by:schworak
Comment Utility
WHOW!!!! NEWS FLASH!

It only seems to be a problem when the UNC path also exists as a mapped drive.... I will investigate this more. If that is the case, it is an easy fix. I will turn the UNC back into a drive letter before selecting.
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 3

Author Comment

by:schworak
Comment Utility
YES! This is the solution....

Before trying to pre-select a UNC path when displaying the "Browse For Folder" dialog, convert the UNC back to a mapped drive path.

If the path can not be mapped to a drive, then allow the UNC path to be pre-selected. It will work.

If you do not convert a UNC to mapped path and a mapped path exists with the same UNC value, the pre-select will not properly display in the dialog when it opens.
0
 
LVL 4

Accepted Solution

by:
VincentLawlor earned 0 total points
Comment Utility
I have had no problems with this code Preselecting i.e. passing it in to BrowseForFolder with the path set in the control passed to it.

Vin.


0
 
LVL 2

Expert Comment

by:Lunchy
Comment Utility
I have reduced points to zero to add question to PAQ as questioner found the answer himself.

Sorry Vin.  At least I can give you an A for helping.

Lunchy
Friendly Neighbourhood Community Support Moderator
0
 
LVL 3

Author Comment

by:schworak
Comment Utility
Maybe it is a difference between using MS Networking and NetWare?

Just reporting what I am seeing happening. Very strange.

The code (both yours and mine) on our systems (Win 98 and NetWare) have this strange pre-selection action.

If the drive is a real dirve (C: D: M: Z: etc.) it pre-selects just fine.

If it is say \\testsystem\data\personal and there is no drive mapped to \\testsystem it will properly pre-select in the dialog when it opens. But if say T: is mapped to \\testsystem\data and I try to pre-select \\testsystem\data\personal then it won't pre-select. But if I try to pre-select t:\personal that works.

Odd for sure. This also happens with the CCRP version of the browse for folder control so I don't think it is the code but something else in the OS or a mix of our OS and the network.

It was easy enough to fix. I just switched the UNC to a drive letter when posible and the problem is gone now.
0
 
LVL 4

Expert Comment

by:VincentLawlor
Comment Utility
Cheers Lunchy.

Vin.
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Introduction While answering a recent question about filtering a custom class collection, I realized that this could be accomplished with very little code by using the ScriptControl (SC) library.  This article will introduce you to the SC library a…
Have you ever wanted to restrict the users input in a textbox to numbers, and while doing that make sure that they can't 'cheat' by pasting in non-numeric text? Of course you can do that with code you write yourself but it's tedious and error-prone …
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
Show developers how to use a criteria form to limit the data that appears on an Access report. It is a common requirement that users can specify the criteria for a report at runtime. The easiest way to accomplish this is using a criteria form that a…

772 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now