# Visual Basic > Visual Basic 6 and Earlier >  [RESOLVED] Browse for Folder API

## k_zeon

I have seen the new style browse for folder and code below opens it etc, but if i click cancel it crashes. this i can probably do some error handling to escape it.
but what i wanted to know is how to make the window show the selected folder on the left hand tree view.
I can see the path in the textbox at the top but will not expand the treeview on the left to said folder.
I see there is pfd.SetDefaultFolder   but i cannot set this to a string path



```

Public Declare Function SysReAllocString Lib "oleaut32.dll" (ByVal pBSTR As Long, Optional ByVal pszStrPtr As Long) As Long

Public Function PickFolder(hWndOwner As Long) As String
Dim pfd As New FileOpenDialog
Dim siResult As oleexp.IShellItem
pfd.SetTitle "Select Folder"
pfd.SetOptions FOS_PICKFOLDERS
pfd.Show hWndOwner

pfd.GetResult siResult
If (siResult Is Nothing) = False Then
    Dim lpPath As Long
    siResult.GetDisplayName SIGDN_DESKTOPABSOLUTEPARSING, lpPath
    PickFolder = LPWSTRtoStr(lpPath)
End If
End Function

Public Function LPWSTRtoStr(lPtr As Long, Optional ByVal fFree As Boolean = True) As String
SysReAllocString VarPtr(LPWSTRtoStr), lPtr
If fFree Then
    Call CoTaskMemFree(lPtr)
End If
End Function
```

----------


## fafalone

Because of the way it raises the cancel event you need to use an error handler to get around it yes. Since the code above already validates the result, you can just use
On Error Resume Next
pdf.Show hwndOwner
On Error GoTo 0

It shows the last opened folder by default, like the other open dialog versions. If you're running into an issue where you have multiple dialogs in your app and wanted the last path per-dialog, it would be much easier to use the .SetClientGuid function. Set it to a unique GUID for each dialog, then the system will automatically keep track of last folders per-dialog instead of per-app.

If you still want to use SetDefaultFolder, it accepts an IShellItem, which you can get via
Dim siItem As IShellItem
SHCreateItemFromParsingName StrPtr("path"), Nothing, IID_IShellItem, siItem

(that API is already declared in oleexp)

I'm not sure why it wouldn't expand to the folder... it does on my system... what version of Windows are you on?

----------


## k_zeon

> Because of the way it raises the cancel event you need to use an error handler to get around it yes. Since the code above already validates the result, you can just use
> On Error Resume Next
> pdf.Show hwndOwner
> On Error GoTo 0
> 
> It shows the last opened folder by default, like the other open dialog versions. If you're running into an issue where you have multiple dialogs in your app and wanted the last path per-dialog, it would be much easier to use the .SetClientGuid function. Set it to a unique GUID for each dialog, then the system will automatically keep track of last folders per-dialog instead of per-app.
> 
> If you still want to use SetDefaultFolder, it accepts an IShellItem, which you can get via
> Dim siItem As IShellItem
> ...


thks , will give the .setClientGuid a go. Yes i have put in a on error so thats sorted.

i am on Win10Pro  19045.2364  would be great to get it to expand.

wont expand In IDE or compiled.  Even when doubleclicking the folders in the right hand pane nothing happens in the left.

juat tried to get the code to work but i get error. says the word Nothing is incorrect. i tried vbnothing & vbnullstring & null

can you advise a working example.

Dim siItem As IShellItem
SHCreateItemFromParsingName StrPtr("path"), Nothing, IID_IShellItem, siItem

just tried the below but errors at SetGuidID as ByRef type mismatch. I must be doing something wrong



```
Public Function PickFolder(hWndOwner As Long) As String
On Error GoTo cancelled
Dim pfd As New FileOpenDialog
Dim siResult As oleexp.IShellItem
pfd.SetTitle "Select Folder"

Dim IID_IShellItem As oleexp.IShellItem
Dim gID As Long

Dim siItem As IShellItem
gID = SHCreateItemFromParsingName(StrPtr("D:\Temp"), 0, IID_IShellItem, siItem)

pfd.SetClientGuid gID

pfd.SetOptions FOS_PICKFOLDERS Or FOS_FORCESHOWHIDDEN
pfd.Show hWndOwner

pfd.GetResult siResult
If (siResult Is Nothing) = False Then
    Dim lpPath As Long
    siResult.GetDisplayName SIGDN_DESKTOPABSOLUTEPARSING, lpPath
    PickFolder = LPWSTRtoStr(lpPath)
End If

cancelled:
End Function
```

tks

----------


## fafalone

That's definitely weird... have you observed this problem happening in any other apps that use it (more recent Microsoft stuff is most likely to use it; I believe Notepad does)? Is the 'Expand to open folder' option enabled in Explorer?

Is there an alternate version of SHCreateItemFromParsingName declared in the project? If so it should tell you what kind of argument it expects, probably a Long for which you'd pass 0. Or you can explicitly specific oleexp.SHCreateItemFromParsingName

----------


## k_zeon

> That's definitely weird... have you observed this problem happening in any other apps that use it (more recent Microsoft stuff is most likely to use it; I believe Notepad does)? Is the 'Expand to open folder' option enabled in Explorer?
> 
> Is there an alternate version of SHCreateItemFromParsingName declared in the project? If so it should tell you what kind of argument it expects, probably a Long for which you'd pass 0. Or you can explicitly specific oleexp.SHCreateItemFromParsingName


just tried not pad and this does not expand folders either. where is the setting for explorer

edit: you nailed it, the option was not on for explorer. this is not working.

Re above post. could you advise why the setguid is not working. this will be the last bit before i start replacing the old Browse for Folder functions.

tks

----------


## fafalone

.SetClientGuid is for having it automatically track the last folder on a per-dialog basis, instead of a per-app basis. You specify a UUID, not a folder. See example here.

Also, siItem is the IShellItem for SetDefaultFolder, the return value is just a status code indicating if the call succeeded. 

eta:
If you're automatically setting the last folder by using SetClientGuid, you don't need to manually do it with SetDefaultFolder; you don't need both methods.

----------


## k_zeon

> .SetClientGuid is for having it automatically track the last folder on a per-dialog basis, instead of a per-app basis. You specify a UUID, not a folder. See example here.
> 
> Also, siItem is the IShellItem for SetDefaultFolder, the return value is just a status code indicating if the call succeeded. 
> 
> eta:
> If you're automatically setting the last folder by using SetClientGuid, you don't need to manually do it with SetDefaultFolder; you don't need both methods.


Ok, tks
so is there an example of SetDefaultFolder

sometimes i have different folders set and would like to be able to set each default per routine and not by app.

tks

so would i just do something like



```
SHCreateItemFromParsingName(StrPtr("D:\Temp"), 0, IID_IShellItem, siItem)
pfd.setdefaultfolder  siItem
```

edit1: so i just reread the link ref the guid per dialog, may try this when i get home.

but just for backward compat,  can i specify a path as in the line above

----------


## fafalone

SetClientGuid sets it per-dialog so if you have an open dialog on Form1, and another on Form2, these could have separate last folders. 

So if you have 2 different open dialogs... you would make 2 GUIDs. Then there would be a separate 'last folder' associated with GUID 1, and another for GUID 2. You'd have SetClientGuid GUID1 on the file open routine for Form1, GUID2 for the one on Form2.

It doesn't have to be different forms... or even routines, you could make it an argument if you wanted:



```
Private Declare Function CLSIDFromString Lib "ole32" (ByVal lpszGuid As Long, pGuid As Any) As Long
Private Const sGUID_Dialog1 = "{A4BF774D-0029-4c8f-9174-B397211B92F5}"
Private Const sGUID_Dialog2 = "{83E461A9-5341-46f5-8825-EAC176603E94}"

Private Function GUID_Dialog1() As UUID
Static iid As UUID
 If (iid.Data1 = 0) Then Call CLSIDFromString(StrPtr(sGUID_Dialog1), iid)
 GUID_Dialog1 = iid
End Function
Private Function GUID_Dialog2() As UUID
Static iid As UUID
 If (iid.Data1 = 0) Then Call CLSIDFromString(StrPtr(sGUID_Dialog2), iid)
 GUID_Dialog2 = iid
End Function
```

Then 



```
Public Function PickFolder(hWndOwner As Long, gID As UUID) As String
On Error GoTo cancelled

Dim pfd As New FileOpenDialog

pfd.SetClientGuid gID

Dim siResult As oleexp.IShellItem

pfd.SetTitle "Select Folder"

pfd.SetOptions FOS_PICKFOLDERS Or FOS_FORCESHOWHIDDEN
pfd.Show hWndOwner

pfd.GetResult siResult
If (siResult Is Nothing) = False Then
    Dim lpPath As Long
    siResult.GetDisplayName SIGDN_DESKTOPABSOLUTEPARSING, lpPath
    PickFolder = LPWSTRtoStr(lpPath)
End If

cancelled:
End Function
```

Then PickFolder(hwnd, GUID_Dialog1) would have it's own last folder, and PickFolder(hwnd, GUID_Dialog2) would have it's own, different, last folder. You can have as many GUIDs as you want, you just make up a new random one for each with GUIDGEN (comes with VS6) or an online generator.




If that's not what you're looking for SetDefaultFolder is used like this:



```
Public Function PickFolder(hWndOwner As Long, sDefaultPath As String) As String
On Error GoTo cancelled

Dim pfd As New FileOpenDialog

Dim siResult As oleexp.IShellItem

Dim siDefault As oleexp.IShellItem

oleexp.SHCreateItemFromParsingName StrPtr(sDefaultPath), Nothing, IID_IShellItem, siDefault
pfd.SetDefaultFolder siDefault

pfd.SetTitle "Select Folder"

pfd.SetOptions FOS_PICKFOLDERS Or FOS_FORCESHOWHIDDEN
pfd.Show hWndOwner

pfd.GetResult siResult
If (siResult Is Nothing) = False Then
    Dim lpPath As Long
    siResult.GetDisplayName SIGDN_DESKTOPABSOLUTEPARSING, lpPath
    PickFolder = LPWSTRtoStr(lpPath)
End If
```

IID_IShellItem is in mIID.bas... I don't know if you're using that or not but if not you can define it:



```
Private Function IID_IShellItem() As UUID
Static iid As UUID
 If (iid.Data1 = 0) Then Call CLSIDFromString(StrPtr("{43826d1e-e718-42ee-bc55-a1e261c37bfe}"), iid)
 IID_IShellItem = iid
End Function
```

----------


## k_zeon

> SetClientGuid sets it per-dialog so if you have an open dialog on Form1, and another on Form2, these could have separate last folders. 
> 
> So if you have 2 different open dialogs... you would make 2 GUIDs. Then there would be a separate 'last folder' associated with GUID 1, and another for GUID 2. You'd have SetClientGuid GUID1 on the file open routine for Form1, GUID2 for the one on Form2.
> 
> It doesn't have to be different forms... or even routines, you could make it an argument if you wanted:
> 
> 
> 
> ```
> ...


tks, looks like what i need.

i am basically trying to use it like i would the the BrowseforFolder API that i usually use

ie

Dim sPath as string
lastfolder = getsetting (app.exename,"Settings", "xyz path that i want to remember","C:")
spath = BrowseforFolder(me.hwnd,lastfolder, "Search Folder") ' << "Search Folder" is the Title

if sPath <> "" then
savesetting app.exename,"Settings", "xyz path that i want to remember",spath
lastfolder=spath 
end if

when i get home , will give it all another go. but thanks very much for your help

----------


## k_zeon

> SetClientGuid sets it per-dialog so if you have an open dialog on Form1, and another on Form2, these could have separate last folders. 
> 
> So if you have 2 different open dialogs... you would make 2 GUIDs. Then there would be a separate 'last folder' associated with GUID 1, and another for GUID 2. You'd have SetClientGuid GUID1 on the file open routine for Form1, GUID2 for the one on Form2.
> 
> It doesn't have to be different forms... or even routines, you could make it an argument if you wanted:
> 
> 
> 
> ```
> ...


Hi fafalone
So i tried SetDefaultFolder  option and does not open at a default folder that i pass to the function

I tried the GUID option and that works after i have selected an initial folder ie last folder.

If poss, could you make a small example app for the SetDefaultFolder  option , because i dont seem to be able to make it start at a default folder that i set.

ie PickFolder(me.hwnd, "C:\Temp")

I used the code you supplied above.

Just an FYI why i like to set folders, is if i have an app on a thunb drive i can save the thumb folder path relativly in a settings file and then use the routine even if on another pc.
hope that makes sense

tks

----------


## fafalone

Looks like you should use .SetFolder instead of .SetDefaultFolder

----------


## k_zeon

> Looks like you should use .SetFolder instead of .SetDefaultFolder


nope, tried that and not working. Just remembers the last folder even the i set  sDefaultPath  = "C:\Temp"



```
Public Function PickFolder(hWndOwner As Long, sDefaultPath As String) As String
    On Error GoTo cancelled
    Dim pfd As New FileOpenDialog
    Dim siResult As oleexp.IShellItem
    Dim siDefault As oleexp.IShellItem
    oleexp.SHCreateItemFromParsingName StrPtr(sDefaultPath), Nothing, IID_IShellItem, siDefault

    pfd.SetFolder siDefault

    pfd.SetTitle "Select Folder"

    pfd.SetOptions FOS_PICKFOLDERS Or FOS_FORCESHOWHIDDEN

    pfd.Show hWndOwner

    pfd.GetResult siResult

    If (siResult Is Nothing) = False Then
        Dim lpPath As Long
        siResult.GetDisplayName SIGDN_DESKTOPABSOLUTEPARSING, lpPath
        PickFolder = LPWSTRtoStr(lpPath)
    End If

cancelled:

End Function
```

----------


## fafalone

It works for me... did you add one of the ways of defining IID_IShellItem?

Check and see if siDefault Is Nothing.

The path must exist too; does C:\Temp exist?

----------


## k_zeon

> It works for me... did you add one of the ways of defining IID_IShellItem?
> 
> Check and see if siDefault Is Nothing.
> 
> The path must exist too; does C:\Temp exist?


just checked siDefault and it is nothing.?  why would that be

and yes the path does exist  tried  C:\Temp   and  C:\Temp\

if you could zip something up and post. i could test. maybe something wrong with my system.

here is what i have used.



```
Public Function PickFolder(hWndOwner As Long, sDefaultPath As String) As String
    On Error GoTo cancelled
    Dim pfd As New FileOpenDialog
    Dim siResult As oleexp.IShellItem
    Dim siDefault As oleexp.IShellItem
    oleexp.SHCreateItemFromParsingName StrPtr(sDefaultPath), Nothing, IID_IShellItem, siDefault
    
    If siDefault Is Nothing Then Stop
    
    
    pfd.SetFolder siDefault


    pfd.SetTitle "Select Folder"

    pfd.SetOptions FOS_PICKFOLDERS Or FOS_FORCESHOWHIDDEN
    pfd.Show hWndOwner
    pfd.GetResult siResult
    If (siResult Is Nothing) = False Then
        Dim lpPath As Long
        siResult.GetDisplayName SIGDN_DESKTOPABSOLUTEPARSING, lpPath
        PickFolder = LPWSTRtoStr(lpPath)
    End If

cancelled:

End Function

Private Function IID_IShellItem() As UUID
Static iid As UUID
 If (iid.Data1 = 0) Then Call CLSIDFromString(StrPtr("{A4BF774D-0029-4C8f-9174-B397211B92F5}"), iid)
 IID_IShellItem = iid
End Function
```

Edit1:
so i replaced



```
Private Function IID_IShellItem() As UUID
Static iid As UUID
'If (iid.Data1 = 0) Then Call CLSIDFromString(StrPtr("{A4BF774D-0029-4C8f-9174-B397211B92F5}"), iid)
IID_IShellItem = iid
End Function
```

with



```
Private Function IID_IShellItem() As oleexp.UUID
Static iid As oleexp.UUID
If (iid.Data1 = 0) Then Call DEFINE_UUID(iid, &H43826D1E, CInt(&HE718), CInt(&H42EE), &HBC, &H55, &HA1, &HE2, &H61, &HC3, &H7B, &HFE)
IID_IShellItem = iid
End Function
```

and it now works. tks for being so paitent

----------

