# VBForums CodeBank > CodeBank - Visual Basic 6 and earlier >  [vb6]Common Dialog Class (Yet Another One)

## LaVolpe

Then enclosed class combines the Windows XP/Win2000 Open/Save dialog that uses APIs to generate the dialog with the IFileDialog interface used in Vista and higher. Basically, the class is a unicode-friendly dialog option as a drop-in, self-contained class. Do note that the class has been hard-coded to not run on any O/S less than XP/Win2000. Also included in the class is the old-style "Browse for Folder" dialog. The class can get all dialogs to callback to itself and forward you the events.

Though the class makes heavy use of calling to interfaces not known to VB, it does not use type libraries (TLBs). However, I have made every effort to make it compatible to TLBs you may be using in your project. In other words, objects returned by this class through its events or functions should be 100% compatible with a TLB that defines interfaces that this class is using. Anything less would be an oversight by me and considered a "bug report".

This class has absolutely no real benefit over existing code you may already be using unless you want more advanced options. Some of those options include:

- XP/Win2000. APIs used to display the dialogs
-- class-generated thunks for hooking the dialog. Those thunks result in raised events from the class to its host, i.e., form, usercontrol, other class, etc.
-- auto-sizing buffer for multi-selection to prevent the ugly 'buffer too small' error and forcing users to go back and try again

- Vista and higher. IFileDialog interface used to display the dialogs
-- Customize by adding additional controls to the dialog and receive events for those controls
-- Add a read-only checkbox back to the dialog that populates the common OFN_ReadOnly flag
-- Interact with the dialog via class-generated thunks that raise events from the class to its host
-- Use embedded custom configurations. There are currently 7 of those. 
1. Browse for Folders while showing file names too
2. Navigate into compressed folders (zips) while being able to select the zip itself or one of its contained files or any other file
3. Show both files and folders and be able to select either folders or files or both
4. Four "basket mode" settings which allows selecting files/folders across multiple directories. Similar to "Add to my Cart" button.
-- All custom mode button captions can be assigned by you or default to locale-aware captions (see screenshot below)

Nearly all of the advanced Vista+ options are incorporated into this class, but not all. If you find you need anything more that is not offered, modify as needed. 

If you just want a simple Open/Save dialog where the filter is: All Files, the code needed for the dialog is as simple as:


```
    Dim cBrowser As CmnDialogEx
    Set cBrowser = New CmnDialogEx
    If cBrowser.ShowOpen(Me.hWnd) = True Then
        MsgBox "File Selected: " & cBrowser.FileName
    End If
```

Want to add the "Read-Only" checkbox back to the dialog?


```
    Dim cBrowser As CmnDialogEx
    Set cBrowser = New CmnDialogEx
    cBrowser.Controls_AddReadOnlyOption 100    ' << user-defined Control ID
    If cBrowser.ShowOpen(Me.hWnd) = True Then
        MsgBox "File Selected and Read-Only opted for: " & CBool(cBrowser.Flags And Dlg_ReadOnly)
    End If
```

Want a "Browse for Folder" like dialog that also shows files (not doable with newer dialog using standard options)?


```
    Dim cBrowser As CmnDialogEx
    Set cBrowser = New CmnDialogEx
    cBrowser.Controls_SetCustomMode cm_BrowseFoldersShowFiles
    If cBrowser.ShowOpen(Me.hWnd) = True Then
        MsgBox "Selected Folder: " & cBrowser.FileName
    End If
```

The screenshot below highlights locale-aware captions. The only one I haven't been able to find is a locale-aware caption like: All Files. That would be a nice-touch. 
Edited: Found it in shell32.dll ID: 34193 for Vista+. XP/Win2K will still use hard-coded value as needed


The sample project offers examples of several dialog variations. The class itself is heavily commented.


```
Update History
5 Aug 18 - Massive changes see post #13 for  details
28 May 18 - Enhancements & altered format of FileName property return value 
        when multiple files selected
23 Jan 18 - Minor fixes
        Fixed a couple locale-aware captions being retrieved from common dlls
        Addressed case where Windows can convert "Open" button to unexpected split-button
21 Jan 18 - initial release
```

FYI: If you are using any previous version, do not simply replace it with the one in the zip below. So many changes were made to this version that it is not compatible with previous ones. To ensure no confusion between versions, the class name was also changed from OSDialogEx to CmnDialogEx.

Minor fix to be applied after downloading. Next time zip is uploaded, fix will be applied and this paragraph will be removed. Test code left as-was by mistake.


```
- Routine: InitDir property Let
- Action:
relocate this line: If Len(Value) = 0& Then Exit Property
relocate to immediately above this line: If InStr(Value, ":") Then- Reason: can't reset InitDir to null otherwise, without calling the Clear method
```

----------


## LaVolpe

*Notes*. For those of you not yet using a real Vista+ dialog, there are several new flags made available. And some of those flags conflict with previous version of the dialog. Here's a couple of those changes. See this MSDN link for a complete list, also commented in the class.

ForceFileSystem. Display only true files, not virtual files
AllNonStorageItems. Allows you to navigate into non-system folders and zip files. AllNonStorageItems & ForceFileSystem are mutually exclusive. One or the other.
PickFolders. Enables the new basic "Browse for Folder" dialog. 
Previous version flag OFN_ENABLEHOOK. Not supported because same value as new PickFolders constant. For pre-Vista users, the ShowOpen & ShowSave functions have an optional parameter to hook the dialog and pass events to you instead.

All public enumerations in the class have been made unique so that they don't conflict with any similar enumerations you may already have. So, the class isn't truly plug & play with your existing dialog code, but very little effort is required. Typically, you may only need to change your Flags enumeration values from FOS_[xxxx] or OFN_[xxx] values to the class' DLG_[xxx] & DLGex_[xxx] values. 
*Tip*. DLG_ prefix is for all systems, DLGex_ prefix is for Vista and above DLGXP_ is for XP/Win2K

Another major change is the heavy usage of IShellItem and IShellItemArray interfaces. Though for basic usage, interacting with those interfaces is not absolutely needed. The class does offer some 'courtesy' functions to query most of those interface methods.

For those of you still quite comfortable with VB's common dialog or other classes designed to use GetOpenFileName and GetSaveFileName APIs, this class was designed around that structure. But the newer Vista+ dialog offers much more. The class offers many additional properties/methods to address those. All methods and properties in the class, except a few, are valid for the Vista+ dialog. Those few are silently ignored. Many of those others are not valid for the older XP/Win2K dialog. There are comments for each method/property. Those not applicable for the older dialog should be clearly stated.

*Tip*. Any additional controls/containers you add to the dialog are assigned a user-defined numeric ID. That ID allows you to call the Controls_PropertyGet/Set functions to get/set properties before the dialog is displayed, while displayed, or after it closes. No ID can be duplicated and the IDs can range from 1 to 268435455 (&H1 to &HFFFFFFF)

*Tip*. The FileName property contains the selected item(s) from the dialog. This property is different than other dialogs you have used when multiple files are selected. Because Vista+ can allow selection of files from different folders (items in a Library for example), each selected file includes the file's path. Previous versions included the path then a vbNullChar-delimited list of file titles which no longer can be enforced with Vista+. As was before, testing for a vbNullChar in the FileName property can be used to determine if multiple items were selected by the user.
Note: If you allow non-system folders to be browsed/selected from within (AllNonStorageItems flag), then it is possible you will get a virtual path/file.

*Tip*. The ShowOpen/ShowSave functions have an  optional parameter to pass a stdole.IUnknown reference. This reference will contain an IShellItem or IShellItemArray (multiple selected items) on return. That reference can be passed to the modern IFileOperation interface which supersedes the SHFileOperation API (Win2K/XP). This class does not expose the IFileOperation interface. A TLB or custom solution is required.

*Tip*. This class appears to work just fine in MS Office (at least Access), even with the thunks and subclassing in play. However, you will need to change some vartypes. Access does not recognize CheckBoxConstants used in public event: DialogCheckBoxChanged. Change that to Long or Boolean. It also does not recognize the vbCFFiles constant used in pvGetDisplayName. Change that constant to: 15.

*Tip*. Though the dialog can be displayed without a passed owner hWnd, you should avoid this unless absolutely needed. Without that hWnd, the dialog is modal only to the line of code that called it, not the form that called it. That means your form is not 'disabled' while the dialog is displayed. It also means the user can close your form/project while the dialog is running. When they eventually close the dialog, your code continues to run which may lead to reloading your form/project, hidden to the user. In any case, the dialog does not disable the entire project (multiple forms displayed), only the form that called it when you also pass the form's hWnd as the owner hWnd. If this doesn't work for you, the easy workaround is: Before calling the dialog, disable all forms but the calling form, call the dialog, and when it closed, re-enable those forms.

*Tip*. The old-style "Browse For Folder" dialog modality is same as the file dialog.

*Tip*. For Vista+ and asking for events, you can change the folder view (list, tiles, small icons, etc) of the dialog via the IFileDialog_SetViewMode method.

*Tip*. Use client GUIDs to have Windows save some of your dialog's last settings, i.e., position, size, last folder visited and a bit more. See CreateClientDataGUID & ShowOpen function comments for details.

----------


## Steve Grant

This is excellent until I needed to dump some files to my 'F:\' drive. It wouldn't select the drive root with browseforfolder! Or am I doing something wrong

----------


## LaVolpe

> This is excellent until I needed to dump some files to my 'F:\' drive. It wouldn't select the drive root with browseforfolder! Or am I doing something wrong


I'm not sure what you mean by "wouldn't select". Are you saying it wouldn't navigate to F root?

Can you be more specific and maybe also include the options you used?  Maybe my logic in the subclassing for a custom mode you might have been using has a logic flaw?

Edited: Yep, a logic flaw in the sample project, but not the dialog class.
The dialog returns the root folder, but the looping I used in the sample failed...


```
...
            sPath = cBrowser.FilePath & "\"
            sItems() = Split(Mid$(cBrowser.FileName, Len(sPath) + 1&), vbNullChar)
```

sPath length is 3 since root folder selected. So the split is trying to split on Mid$(Path, 4). When I get a chance I'll update the sample project. If you actually look at cBrowser.FilePath or .FileName, you should see your folder selected.

However, if multi-selecting root folders using "Browse for Folders", the class does have a logic flaw. I didn't take that into consideration and assumed multi-selection (non-basket mode) would all be in the same parent folder, i.e., same root path. Roots don't quite fit that failed logic. I'll need to tweak that & post the update this weekend.

----------


## Steve Grant

You are so right. Thank you.



```
        If UBound(sItems) > -1 Then
            For lItem = 0 To UBound(sItems)
                lstEvents.AddItem "    Selected: " & sPath & sItems(lItem), lItem + 1&
            Next
        Else
            lstEvents.AddItem "    Selected: " & sPath
        End If
```

----------


## riov

Is it possible to hide/remove Help button in Vista style dialog?

----------


## LaVolpe

If it's an actual window, I would think you can use FindWindow/FindWindowEx to locate it an hide it, but don't know if you can remove it resulting in the other buttons shifting to fill the empty space. That is not an option that is made available by the IFileDialog or its related interfaces. At least not that I know of. Clicking on that help button does not raise an event in the IFileDialog.

----------


## riov

Yes, I can find that button, it is called "&Help". Given that IModalWindow::Show is a blocking call, it would be complicated to use ShowWindow API to hide it. I was hoping to use IFileDialogCustomize interface to remove it.

----------


## LaVolpe

> Yes, I can find that button, it is called "&Help". Given that IModalWindow::Show is a blocking call, it would be complicated to use ShowWindow API to hide it. I was hoping to use IFileDialogCustomize interface to remove it.


If on Vista and higher and using this class, you can opt for events. One of the events is the DialogOnInit event. That event contains the dialog hWnd. Within any event, you can call the class IFileDialog_GetHwnd function. Events are not blocked

----------


## riov

Unfortunately, under further investigation it looks like it is impossible to hide that Help button. The reason is that it has no hwnd as it is drawn by DirectUIHWND. I can get a list of its properties, but I have not found a way to change them.

----------


## LaVolpe

Dialog class and sample project updated. List of primary changes follows:

1. Changed format of FileName property when multiple files/folders selected.
Instead of old format: Path vbNullChar FileName1 vbNullChar FileName2 etc
Changed to: Path\FileName1 vbNullChar Path\FileName2 etc
Reason: Vista+ allows selection of files from different paths, i.e., Library items. Since each selected item can be at a different path, the previous method of identifying just one path for all selected files no longer applies. Though pre-Vista still restricts multiple selections from same path, this new format is used whether the dialog is being displayed pre-Vista or Vista+

2. Exposed IFolderView2 which can be used to change display of the dialog: details, list, small icon, etc
- Added functions to get/set the folder's display mode
IFileDialog_IFolderView2 retrieves interface
IFileDialog_GetViewMode returns current folder's display mode
IFileDialog_SetViewMode sets current folder's display mode- Added new enum to support above functions: IShellViewModeEnum

3. Changed Controls_AddReadOnlyOption method to default Read-Only option to False vs. True
- if checkbox style: checkbox is unchecked by default
- if split-button style: "Open" button defaults to not open as read only

4. Added locale-aware 'All Files' filter item when default filter used. Applies to Vista+


5. Added option within Filter property to allow not showing the filter, if desired. In screenshot below, you can see the Filter in a typical dialog and then the dialog with the hidden filter. Tip: This is a nice alternative when the filter would just be All Files... Why show the filter if it really doesn't apply? Rhetorical.

----------


## LaVolpe

Here's a replacement for the pvProcessCustomMode routine in the class.

*Scenario*: Using options to show both files and folders while browsing for folders. Since files are shown, then can be selected.

*Problem*: Logic flaw. It is possible the user only selects files and selects no folders. The dialog would close when the "Select Folder" button is clicked. This resulted in dialog returning a true value (items selected) when none were actually selected. In this scenario, the parent folder should be returned.

*Solution*: Test the count of actual folders that were selected and ignore button click if zero folders were selected. The existing code removed any files from the selection, but didn't recheck the count afterwards and simply closed the dialog in that case. If user selected only files, then the dialog will return the parent folder.

Changes are bolded and in blue below. I'll include this patch the next time I update the zip in original post above. Will also look at preventing files from being clickable in this scenario.


```
Private Function pvProcessCustomMode() As Long

    ' this routine processes the subclassed "Open" button. User wants to select something
    ' applies only when Controls_SetCustomMode called and a subclass was created
    
    ' overriding default behavior is a bit complex
    
    Dim bCancel As Boolean, nrItems As Long
    Dim lAttrs As Long, oPtr As Long, aPtr As Long
    Dim lOptions As Long, n As Long, aData() As Long
    Dim IFolderView2 As stdole.IUnknown
    Const IfIFolderView2_GetSelection As Long = 32
    
    pvCallInterface ObjPtr(m_Dialog), ifFileDialog_GetOptions, VarPtr(lOptions)
    If (lOptions And DLG_AllowMultiSelect) Then
        ' see IFileDialog_GetCurrentSelection method for comments why we ask for multi-select items this way
        Set IFolderView2 = pvGetIFolderView2
        If Not IFolderView2 Is Nothing Then
            pvCallInterface ObjPtr(IFolderView2), IfIFolderView2_GetSelection, 0&, VarPtr(m_ModeResults)
            Set IFolderView2 = Nothing
        End If
    End If
    If m_ModeResults Is Nothing Then
        Set m_ModeResults = Me.IFileDialog_GetCurrentSelection(True, nrItems)
        If m_ModeResults Is Nothing Then Exit Function      ' no items selected
    Else
        pvCallInterface ObjPtr(m_ModeResults), ifShellItemArray_GetCount, VarPtr(nrItems)
    End If
    
    Select Case (m_StateFlags And emskCustMode)
     
     Case cm_BasketModeFilesOrFolders, cm_PickFilesOrFolders ' can select anything
         If (m_StateFlags And emskCustMode) = cm_BasketModeFilesOrFolders Then
             RaiseEvent DialogAddBasketItem(m_ModeResults, bCancel)
         ElseIf (m_StateFlags And emskAllEvents) Then
             RaiseEvent DialogOnFileOk(bCancel)
         End If
         If bCancel Then
             Set m_ModeResults = Nothing ' function returns 0 to keep dialog open
         ElseIf (m_StateFlags And emskCustMode) = cm_PickFilesOrFolders Then
             pvProcessCustomMode = True   ' closes dialog
         Else
             pvUpdateBasket nrItems      ' dialog remains open
             Set m_ModeResults = Nothing
         End If
         
     Case cm_BrowseFoldersShowFiles, cm_BasketModeFoldersOnlyShowFiles
         For n = 0& To nrItems - 1&                ' test attributes of each selected item
             pvCallInterface ObjPtr(m_ModeResults), ifShellItemArray_GetItemAt, n, VarPtr(oPtr)
             If oPtr Then
                 pvCallInterface oPtr, ifShellItem_GetAttributes, -1&, VarPtr(lAttrs)
                 pvCallInterface oPtr, ifUnknown_Release: oPtr = 0&
                 If (lAttrs And siAttr_FOLDER) = 0& Then Exit For    ' not a folder
                 If (lOptions And DLGex_AllNonStorageItems) = 0 Then ' only system folders (not zip content)?
                     If Not (lAttrs And (siAttr_FILESYSTEM Or siAttr_STREAM)) = siAttr_FILESYSTEM Then Exit For
                 End If
             End If
         Next
         If n < nrItems Then                         ' at least one non-folder selected
             If nrItems = 1& Then                    ' else multi-selection
                 nrItems = 0&
             Else
                 ReDim aData(0 To nrItems - 1&)          ' loop again, keeping only valid items
                 For n = 0& To nrItems - 1&
                     pvCallInterface ObjPtr(m_ModeResults), ifShellItemArray_GetItemAt, n, VarPtr(oPtr)
                     If oPtr Then
                         pvCallInterface oPtr, ifShellItem_GetAttributes, -1&, VarPtr(lAttrs)
                         If (lAttrs And siAttr_FOLDER) Then
                             SHGetIDListFromObject oPtr, aData(aPtr): aPtr = aPtr + 1&
                             If (lOptions And DLGex_AllNonStorageItems) = 0 Then ' only system folders (not zip content)?
                                 If Not (lAttrs And (siAttr_FILESYSTEM Or siAttr_STREAM)) = siAttr_FILESYSTEM Then _
                                     aPtr = aPtr - 1&
                             End If
                         End If
                         pvCallInterface oPtr, ifUnknown_Release: oPtr = 0&
                     End If
                 Next
                 Set m_ModeResults = Nothing: nrItems = aPtr
                 If nrItems > 0& Then                      ' rebuild results, skipping invalid selections
                    SHCreateShellItemArrayFromIDLists nrItems, VarPtr(aData(0)), m_ModeResults
                    For nrItems = 0& To nrItems - 1&
                        CoTaskMemFree aData(nrItems)
                    Next
                End If
            End If
            If (m_StateFlags And emskCustMode) = cm_BrowseFoldersShowFiles And nrItems = 0& Then
                ' only files were selected, choose the files folder & place into IShellItemArray
                pvCallInterface ObjPtr(m_Dialog), ifFileDialog_GetFolder, VarPtr(m_ModeResults)
                If Not m_ModeResults Is Nothing Then
                    SHGetIDListFromObject ObjPtr(m_ModeResults), aPtr
                    Set m_ModeResults = Nothing
                    If aPtr Then
                        SHCreateShellItemArrayFromIDLists 1&, VarPtr(aPtr), m_ModeResults
                        CoTaskMemFree aPtr: nrItems = 1&
                    End If
                End If
            End If
         End If
         If nrItems < 1& Then
            bCancel = True
         ElseIf (m_StateFlags And emskCustMode) = cm_BasketModeFoldersOnlyShowFiles Then
             RaiseEvent DialogAddBasketItem(m_ModeResults, bCancel)
         ElseIf (m_StateFlags And emskAllEvents) Then
             RaiseEvent DialogOnFileOk(bCancel)
         End If
         If bCancel Then
             Set m_ModeResults = Nothing     ' keep dialog open
         ElseIf (m_StateFlags And emskCustMode) = cm_BasketModeFoldersOnlyShowFiles Then
             pvUpdateBasket nrItems          ' keep dialog open
             Set m_ModeResults = Nothing
         Else
             pvProcessCustomMode = True       ' close dialog
         End If
     
     Case cm_CompressedFolderPlusFiles, cm_BasketModeFilesOnly
         For n = 0& To nrItems - 1&
             pvCallInterface ObjPtr(m_ModeResults), ifShellItemArray_GetItemAt, n, VarPtr(oPtr)
             If oPtr Then
                 pvCallInterface oPtr, ifShellItem_GetAttributes, -1&, VarPtr(lAttrs)
                 pvCallInterface oPtr, ifUnknown_Release: oPtr = 0&
                 If (lAttrs And siAttr_FOLDER) Then
                     If Not (lAttrs And (siAttr_FILESYSTEM Or siAttr_STREAM)) = _
                         (siAttr_FILESYSTEM Or siAttr_STREAM) Then Exit For
                 End If
             End If
         Next
         If n < nrItems Then
             Set m_ModeResults = Nothing
         ElseIf (m_StateFlags And emskCustMode) = cm_BasketModeFilesOnly Then
             pvUpdateBasket nrItems
             Set m_ModeResults = Nothing
         Else
             If (m_StateFlags And emskAllEvents) Then RaiseEvent DialogOnFileOk(bCancel)
             If bCancel Then Set m_ModeResults = Nothing Else pvProcessCustomMode = True
         End If
         
    'Case cm_PickFilesOrFolders, cm_BasketModeFoldersOnly  ' these are not subclassed
         
    End Select

End Function
```

Upcoming enhancement: Inclusion of Vista+ SetClientGUID and ClearClientData options. SetClientGUID allows you to assign dialog-startup persisted information to a GUID. This is like having a configuration file for a dialog. You can have as many of these as you'd like, for example, one for a Open dialog, one for a Save dialog, etc. ClearClientData resets associated GUIDs. GUIDs are cached per application. See this for more info. 



> By specifying a GUID, an application can have different persisted states (such as the last visited folder and the position and size of the dialog) for different versions of the dialog within the same application (for example, an import dialog and an open dialog).


Considering other enhancements before the next update.

----------


## LaVolpe

Project updated ... massively.

Important. The class in the zip file is no longer named the same, on purpose. So many changes were made that it could not be kept compatible with the previous version. Old name was OSDialogEx and new class name is CmnDialogEx. Here is a summary of the changes made:

- Includes the old-style "Browse For Folder" dialog. It is likely many still use it. So, decided to include it with the class. Most of the class methods don't apply to that dialog. At top of the class, there are comments to which class methods do apply (about a dozen). This is biggest reason why class became incompatible with previous one. Also included several callback events.

- Since the property Flags is no longer intuitive to which dialog it belongs to, we now have 3 Flags properties:
FlagsDialog is the old Flags property and pertains to the file dialogs
FlagsDialogEx is the old FlagsEx property and pertains to the file dialogs
FlagsFolderBrowsing is a new property and only pertains the "Browse for Folder" dialog

- InitDir, SetRootFolder, SetNavFolders, SetNavFoldersEx have been slightly modified to accept various forms of "folder" references to include: standard folder names, PIDLs, CSIDL constants, Vista+ KnownFolderIDs & control panel path GUIDs

- Added ability to use client GUIDs with the Vista+ file dialog. See previous reply above for more info. Also to support this, included a custom class function to create these GUIDs for you, on demand. There's a bit more to this if you opt to use them and the class CreateClientGUID function is well commented about that. Tweaked the ShowOpen and ShowSave functions for client GUID use and added comments there too.

- A few properties were removed: FileExtension, FilePath & MaxFileTitle. Another became read-only: FileTitle

- Included all known CLSID values and Vista+ KnownFolderIDs to the class via enumerations so you don't have to go looking for them if needed. For example, returning a known folder ID:


```
 ' cBrowser as been declared as CmdDialogEx
    Debug.Print cBrowser.GetKnownFolderGUID(eFOLDERID_ProgramFilesX64)
    ' following printed out: {6D809377-6AF0-444b-8957-A3773F02200E}
```

- Minor bug fixes noted at top of the class comments

- Also updated the sample project to include use of the "Browse for Folder" dialog

----------


## hank15

Thanks very much for making this available. It looks really good and can do more than I need in a number of ways, but perhaps less than I need in another, and that has to do with messages shown to the user.

Is there a way to have a custom message appear when using the Save As version and the file already exists? In many situations, the standard "already exists" message is usable, though not ideal. Using the old OCX, my program shows

    The file already exists!
    Do you want to replace it with a new file?  
    If you answer 'Yes,' the previous contents of the file will be lost.


But in a few cases, I need to offer the choice of adding new content to an existing file. In these cases, my program shows

    File: filename
    The file already exists!
    Do you want to add to the existing file?
    Choose 'Yes' to ADD this new report to the file.
    Choose 'No,' to have this new report REPLACE the previous contents of the file.
    Choose 'Cancel' to enter a DIFFERENT NAME for the new file.

So I need three choices.  They don't have to be worded exactly as above, and could use a dialog box with three options, the way Word does (I could accept Word's "merge" option, if a custom label wasn't possible).  

The way I handled these situations with the old OCX was to have it display the dialog box, retrieve the selected file name, see if it exists and if so give the appropriate message, then after the user makes their choice either close the dialog box and move on or return to it, if a new name is needed.

I tried to find a place where I could do the same thing  check the file name while the dialogue is still shown  but wasn't successful. Is there such a place, or is there a way to tell it to offer three choices and change their labels?

Thank you very much!

----------


## fafalone

You can use the IFileDialog events for all those, I posted a more detailed comment in your original thread.

----------


## Elroy

Hi LaVolpe,

I hate to use your new tool against you, but I found a doubly-naked Redim in this project.  It's in the CmnDialogEx.cls module, the pvGetIFolderView2 procedure, and it's the aData(0 to 7) variable.  Specifically, it's the following line:



```

        ReDim aData(0 To 7) ' need to use 2 GUIDs (service GUID + IFolderView2 GUID)


```


I just deleted the line, as I didn't see the aData array used in that pvGetIFolderView2 procedure.  However, your comment on that line was a bit disconcerting to me.

Best Regards,
Elroy

----------


## LaVolpe

That line is basically dead code since it isn't used in that routine. Deleting it is just fine. Just FYI. Reason it's there is because of a copy & paste from another routine and forgot to clean it up. The comments are valid though. The 2 GUIDs are added to the aGuid() array, same routine, not the aData() array.

Thanx. Once I get that project scanner done, maybe I'll go back and start looking at some of these older projects of mine and clean them up. That scanner would have found that mistake.

----------


## Karl77

I need a custom button in a file dialog.
No problem so far using this class.

From this custom button, I want the dialog navigate to a determined folder.
But I can't find the how to.



```
Private Sub cBrowser_DialogButtonClicked(ByVal ControlID As Long)

'if ControlID = ...

With cBrowser
    .DialogTitle = "CLICKED:" & CStr(Timer)
    .FileName = "C:\Windows"
End With
```

This all works flawless.
How does the navigation work?

Thanks
Karl

----------


## Karl77

No idea reg. post #18?

----------


## LaVolpe

Karl, 

Before the dialog displays, you can set the FileName property to the complete path & file name of the target file or set the InitDir property to the folder to start the dialog with. But I think you know that. There is no method that I provided that will allow you to navigate, change folders, while the dialog is open. Here is how it can be done rather easily...

All of the following apply to Vista+

1. Use the SetNavFolders or SetNavFoldersEx function for the folder(s) you want a user to be able to navigate to easily. This creates a list of those "links" in the navigation pane (left side) of the browser. See the notes in those routines.

2. Dynamically...

a. Create a button or some other control, i.e., a combobox or custom button with menus, for a user to choose different paths. Looks like you were attempting that already.

b. Add the following new routine to the class and call it from the appropriate event, i.e., DialogButtonClicked. Pass folders only, no file names. Afterwards, you can set the FileName property separately if desired.



```
Public Function NavigateTo(Destination As Variant) As Boolean

    If m_StateFlags < 0& Then                   ' else dialog not running or pre-Vista
        Dim oItem As stdole.IUnknown
        Set oItem = pvGetIShellItem(Destination) ' see comments in routine
        NavigateTo = (pvCallInterface(ObjPtr(m_Dialog), ifFileDialog_SetFolder, ObjPtr(oItem)) = 0) 
        Set oItem = Nothing
    End If

End Function
```

Note: In another thread, one user experienced problems in Win7 displaying the dialog -- it errored; the system would not create it. May want to add this as a safety check.
- replace existing Class_Initialize with this:


```
Private Sub Class_Initialize()

    Dim osv As OSVERSIONINFO
    Dim aGuid(0 To 7) As Long, tDialog As stdole.IUnknown
    
    osv.dwOSVersionInfoSize = Len(osv)
    GetVersionEx osv
    Select Case osv.dwMajorVersion
    Case Is < 5     ' less than Win2K/XP
    Case 5          ' Win2K or XP
        m_StateFlags = 5
    Case Else       ' Vista or better
        CLSIDFromString StrPtr(CLSID_FileOpenDialog), VarPtr(aGuid(0))
        IIDFromString StrPtr(IID_IFileOpenDialog), VarPtr(aGuid(4))
        If CoCreateInstance(VarPtr(aGuid(0)), 0&, CLSCTX_INPROC_SERVER, VarPtr(aGuid(4)), tDialog) = 0& Then
            Set tDialog = Nothing
            m_StateFlags = osv.dwMinorVersion + osv.dwMajorVersion
        Else
            m_StateFlags = 5
        End If
    End Select
    
'     m_StateFlags = 5  ' <<< un-rem to pretend system is pre-Vista

End Sub
```

----------


## Karl77

```
Public Function NavigateTo(Destination As Variant) As Boolean

    If m_StateFlags < 0& Then                   ' else dialog not running or pre-Vista
        Dim oItem As stdole.IUnknown
        Set oItem = pvGetIShellItem(Destination) ' see comments in routine
        NavigateTo = (pvCallInterface(ObjPtr(m_Dialog), ifFileDialog_SetFolder, ObjPtr(oItem)) = 0) 
        Set oItem = Nothing
    End If

End Function
```

Thank you LaVolpe.
NavigateTo works exactly as expected.
Great!

----------


## DerWeise

Hard to believe... VB6 folks still alive, and being rolled over by latest """updates""" of Win10 (aka VISTA II)
This seems like a VERY comprehensive project.

First question:
Will all this work under the latest cruel Win10 "updates", which seem to kill right and left many VB6 calls and traditional controls?

Second question:
GetVersionEx on one of my win7 machine return 5, hence most options disabled.  
No idea why (Win10 returns 6).

3rd question:
We just need no-frills "Open" and "Save" options, with fixed hard-coded filter, and NO other options from the multitude you provide. 
We wish to have only the new style Vista+.
It seems that we need only fraction of the code.
Do you happen to have a more timid version with less code to follow?

Thanks.

----------


## DerWeise

Hard to believe... VB6 folks still alive, and being rolled over by latest """updates""" of Win10 (aka VISTA II)
This seems like a VERY comprehensive project.

First question:
Will all this work under the latest cruel Win10 "updates", which seem to kill right and left many VB6 calls and traditional controls?

Second question:
GetVersionEx on one of my win7 machine return 5, hence most options disabled.  
No idea why (Win10 returns 6).

3rd question:
We just need no-frills "Open" and "Save" options, with fixed hard-coded filter, and NO other options from the multitude you provide. 
We wish to have only the new style Vista+.
It seems that we need only fraction of the code.
Do you happen to have a more timid version with less code to follow?

Thanks.

----------


## Karl77

> Will all this work under the latest cruel Win10 "updates"


Yes, it works.
What is so cruel?




> which seem to kill right and left many VB6 calls and traditional controls?


Hmm?
What exactly is killed?

----------


## gilman

> Second question:
> GetVersionEx on one of my win7 machine return 5, hence most options disabled.  
> No idea why (Win10 returns 6).
> 
> Thanks.


I have the same issue, but only in IDE, when I compile and run the exe program runs OK and the major version in exe version is 6.
I'm used Win7 64b

----------


## Karl77

> I have the same issue, but only in IDE, when I compile and run the exe program runs OK and the major version in exe version is 6.
> I'm used Win7 64b


Can it be that you have set the version to report in the VB6.EXE compatibility settings?

----------


## LaVolpe

> First question:
> Will all this work under the latest cruel Win10 "updates", which seem to kill right and left many VB6 calls and traditional controls?


The easy answer is yes. The real answer is: who knows? Since it uses Windows interfaces and APIs, see no reason why it should fail




> Second question:
> GetVersionEx on one of my win7 machine return 5, hence most options disabled.  
> No idea why (Win10 returns 6).


Not sure why Win7 should return 5. Any application that does not run identifying itself compatible with Win8.1 and/or above via a manifest, will report as running in lower compatibility mode when O/S is higher than 8. You might want to open TaskManager and in the Details tab, add the "Operating System Context" column and see what Windows is reporting. That does affect the how windows 'lies' to your app.




> 3rd question:
> We just need no-frills "Open" and "Save" options, with fixed hard-coded filter, and NO other options from the multitude you provide.


Plenty of examples on this site using the APIs GetOpenFileName & GetSaveFileName. That's what you are looking for then

In any case, any version of 6+ allows usage of the newer Vista+ dialogs. Version 7+ may be needed for custom portions of the class. Windows dictates what dialog options are available per version. And a manifest can resolve most of the problems if the app is reported to Windows as supporting its current O/S.

Per MSDN



> With the release of Windows 8.1, the behavior of the GetVersionEx API has changed in the value it will return for the operating system version. The value returned by the GetVersionEx function now depends on how the application is manifested.
> 
> Applications not manifested for Windows 8.1 or Windows 10 will return the Windows 8 OS version value (6.2). Once an application is manifested for a given operating system version, GetVersionEx will always return the version that the application is manifested for in future releases.

----------


## LaVolpe

> I have the same issue, but only in IDE, when I compile and run the exe program runs OK and the major version in exe version is 6.
> I'm used Win7 64b


See my reply to DerWeise above regarding manifests. My VB6.exe is manifested to be recognized as compatible up to Win10. I have no problems, otherwise, version 6 would be reported too

----------


## Elroy

Say Keith,

Would you mind showing us the .manifest you're using for your VB6.exe program?

Thanks,
Elroy

----------


## LaVolpe

> Would you mind showing us the .manifest you're using for your VB6.exe program?


Shown at bottom of post. Save to file alongside vb6.exe and name this file: vb6.exe.manifest

Getting it activated on modern O/S can be difficult at times. If never having one before, may just need to close VB (if opened) and restart it. Otherwise, try a reboot. 

Now, if you make changes to it down the road, sometimes, you need to "touch" vb6.exe, meaning change its file creation time, using APIs or other means. Even this can fail for me at times and is quite annoying to change due to Windows caching of manifests. As a last resort, I'll tweak a registry setting, restart, undo the registry setting. Here's a post from another thread of mine



> If you want your VB6.exe to use an external manifest, unless you plan on using some 3rd party software to embed one into the exe (and hope it doesn't corrupt it), some annoyances are expected. Simply adding an external manifest along side of VB6.exe doesn't guarantee it will be used. You may very well need to change a registry setting to make it happen. After you are sure your manifest is being recognized, you will likely want to go and restore the setting:
> Key: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\SideBySide
> Value: PreferExternalManifest
> Data: 1 = prefer, 0 = don't prefer
> After making the change and manifest still appears to not take effect, reboot. It is getting harder and harder to use external manifests within Windows -- and that's probably not a bad thing.




```
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
	<assemblyIdentity name="Microsoft.Visual.Basic" version="6.00.9782.0" type="win32" processorArchitecture="x86"/>
	<description>VB6</description>
	<dependency>
		<dependentAssembly>
			<assemblyIdentity name="Microsoft.Windows.Common-Controls" version="6.0.0.0" type="win32" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
		</dependentAssembly>
	</dependency>
	<dependency>
		<dependentAssembly>
			<assemblyIdentity name="Microsoft.Windows.GdiPlus" version="1.1.0.0" type="win32" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"/>
		</dependentAssembly>
	</dependency>
	<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
		<security>
			<requestedPrivileges>
				<requestedExecutionLevel level="highestAvailable" uiAccess="false"/>
			</requestedPrivileges>
		</security>
	</trustInfo>
	<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
		<application>
			<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
			<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
			<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
			<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
	 		<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>

		</application>
	</compatibility>
	<application xmlns="urn:schemas-microsoft-com:asm.v3">
		<windowsSettings>
			<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
			<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">system</dpiAwareness>
		</windowsSettings>
	</application>
</assembly>
```

Note: GDI+ inclusion shouldn't be necessary any longer on Win8 and above. Maybe Win7? Don't recall which OS version changed to defaulting to GDI+ v1.1 versus v1.0. Definitely exclude it on XP systems.

----------


## DerWeise

> Yes, it works.
> What is so cruel?
> 
> 
> Hmm?
> What exactly is killed?


Well, we developed industrial software in VB6 over 20 years ago, and sold it to hundreds of customers worldwide. It runs in German, Chinese, Japanese and English all over.
In the XP years it was proven to be robust and stable. It was running nicely on 32 AND ON 64.
When VISTA came along the problems started. Namely, "it was working yesterday and now it crash".
The first control to suffer was COMDLG32, of course, which, as you state in your code, went through "all new" revolution.
We could not make it work, we had dozens of customers worldwide reporting crash / hang on ShowSave.
We fought it for 3 years until MS themselves understood VISTA is a disaster, but we already suffered damage of trust (who would believe US saying VISTA is crap?)
The second control to go wild was MSXML6 and its frequent variations, and the third was  MSCOMCT32 and his 'brothers'.
Endless issues and hangs, UNTIL WIn10 came along, with its "update-or-die" brutal way, and shot us out the water completely,
We run as add-on inside SOLIDWORKS using their VBA API. Many users of EXCEL worldwide, not just US, now say "it worked yesterday".
Sometimes MS themselves say specifically as notes to an "update" something like "It affects MSXML6 but we are working on providing a solution", etc. Yeah? And what should we tell our industrial customers, that MS promised to solve it?
I don't really want to get started on this. I started before DOS an saw all the revolutions until the whole world was left with ONE TYRANNIC dictator who is able to put things INTO YOUR OWN COMPUTER against your will.
The subject now is how to build alternative to COMDLG32 that will survive latest WIN10 updates including 1809, which we now try to do with the help of your code.
Everything we do we immediately check on 5 PC here, two Win7 and 3 Win10 (Chinese, Japanese and English) in USER or ADMIN accounts.
MS "updates" are NOT identical in those cases. For example, ShowSave can crash in Japanese USER but work on English ADMIN, but few days later it is different. There is no end for this. We also check remote computers in Australia and Japan on different accounts.
All I can say FOR SURE, that since about mid 2018, VB6 suffered many attacks which practically shut us down.
BTW, with the code I adopted form you we now see crash of DUser.DLL , never seen this before using your code. It only crash on Save but not on Open. We implemented a very small portion of the code, per your examples "Standard Open" and "Standard Save". 
The latest crash es happen so far on Win7 (latest updates few days ago) but not yet on Win10 latest (probably will join in few days).

I hope it answers.
(I have decades of VB on me. since BASIC on mainframes in 1979, through all the variations of VB except NET.). I still maintain it is the quickest and most efficient high-level language with the fastest road to market).

----------


## Karl77

> Well, we developed industrial software in VB6 over 20 years ago, and sold it to hundreds of customers worldwide. It runs in German, Chinese, Japanese and English all over.
> In the XP years it was proven to be robust and stable. It was running nicely on 32 AND ON 64.
> When VISTA came along the problems started. Namely, "it was working yesterday and now it crash".
> The first control to suffer was COMDLG32, of course, which, as you state in your code, went through "all new" revolution.
> We could not make it work, we had dozens of customers worldwide reporting crash / hang on ShowSave.
> We fought it for 3 years until MS themselves understood VISTA is a disaster, but we already suffered damage of trust (who would believe US saying VISTA is crap?)
> The second control to go wild was MSXML6 and its frequent variations, and the third was  MSCOMCT32 and his 'brothers'.
> Endless issues and hangs, UNTIL WIn10 came along, with its "update-or-die" brutal way, and shot us out the water completely,
> We run as add-on inside SOLIDWORKS using their VBA API. Many users of EXCEL worldwide, not just US, now say "it worked yesterday".
> ...


While my story is quite similar, I had more luck.
Trouble with COMDLG32.OCX in the VB3 times, so we used a class using the DLL directly.
And no problems since then, on every OS including Vista, except XP64.
Also we never used MSCOMCTL.OCX.

Could you post a snippet around your ShowSave that crashes?
As a simple demo?
Perhaps it helps us to not fall into the same traps as you.
In our tests here in Win7s, all VMs, we don't find a problem, that's why I ask.

----------


## Elroy

Truth be told, DerWeise's story about problems was a bit puzzling to me.

I suppose my story is also rather similar, but I also didn't have those kinds of problems.

However, early on, I implemented a SxS approach for all my OCX dependencies, and I *very rarely* update those.  Also, through the years, I've managed to cut out a couple of those dependencies.  Here's the complete list I currently use:

mscomct2.ocx 6.1.98.16mscomctl.ocx 6.1.98.39msflxgrd.ocx 6.1.98.14richtx32.ocx 6.1.98.16tabctl32.ocx 6.1.98.16
I've often thought of cutting those out, and using Krool's and Eduardo's alternatives, but that comes down to priorities.  Also, I have a rather strong "if it ain't broke, don't fix it" attitude about many things.  But I am being asked to consider language translations, which is pushing me into Unicode considerations.  So, who knows what the future will bring.

Also, LaVolpe, I apologize if this is hijacking your thread too much.  DerWeise, if you'd like to continue this discussion, we should probably move over to the regular Q&A section with a new thread.  @Moderators:  You may want to do that anyway, starting with post #31.

Y'all Take Care,
Elroy

----------


## DerWeise

I implemented the sample code, and have a hysterical bug. Took months to isolate, but not yet to solve.
ShowSave crash bigtime in CERTAIN cases (lots off crunching away from the SAVE code).
Once ".Filter =" is specified - it crashes for sure. The line ".Filter = " omitted - open like charm.

Hint: It almost always happen when the filter is two-letter, not three. (e,g, "LK|*.LK" crash, "LKA|*LKA" works).
Tried with and without EXT, same results.

(in your sample it never happens).

Any clue?
Thanks.

----------


## LaVolpe

@DerWise. With the sample project, this doesn't happen, correct? But it happens with your project?

Can you post your code where you are setting the dialog properties along with the actual call to ShowSave? 
Also which Windows version are your running? Does it happen when the dialog is attempting to display or after you close it?

P.S. A bug does exist in the Filter Get routine & isn't related to length of extension. Extra vbNullChar is wrong
this line: n = InStrRev(ofn.lpstrFilter, vbNullChar* & vbNullChar*)
needs changing to: n = InStrRev(ofn.lpstrFilter, vbNullChar)

----------


## DerWeise

> @DerWise. With the sample project, this doesn't happen, correct? But it happens with your project?
> 
> Can you post your code where you are setting the dialog properties along with the actual call to ShowSave? 
> Also which Windows version are your running? Does it happen when the dialog is attempting to display or after you close it?
> 
> P.S. A bug does exist in the Filter Get routine & isn't related to length of extension. Extra vbNullChar is wrong
> this line: n = InStrRev(ofn.lpstrFilter, vbNullChar* & vbNullChar*)
> needs changing to: n = InStrRev(ofn.lpstrFilter, vbNullChar)


Thanks.

Here is the code in the form:

_Public Sub exec(ByVal Fl As String, ByVal Extn As String, ByVal ind As String, ByVal Mds As String)
   Set cBrowser = New CmnDialogEx

    With cBrowser
        .Filter = Fl
        .DefaultExt = Extn
        .InitDir = ind
        Select Case Mds
            Case "OPEN"
                bReturn = cBrowser.ShowOpen(Me.hWnd, 0)
            Case "SAVE"
                bReturn = cBrowser.ShowSave(Me.hWnd, 0)
            Case "FOLDER"
                .FlagsDialog = DLG__BaseOpenDialogFlags Or DLGex_PickFolders
                .InitDir = ind
                bReturn = cBrowser.ShowOpen(Me.hWnd, 0)

        End Select
    End With

    Set cBrowser = Nothing

End Sub_

I added the fix but it didn't solve it. :Confused: 
It happens in Win7 in the IDE (I know now that whatever crash in my IDE will crash on Win10 in EXE...) It also happens to our users in US and Japan on Win7 and 10 as EXE.
When it crash you get white screen (when it gets to the line:
            lReturn = pvCallInterface(ObjPtr(m_Dialog), ifModalWindow_Show, ownerHwnd)

Then any click just gets PING, need to close VB6 and the running app by Task Manager.
Usually the Fault module Name is DUser.dll, exception 5 at offset 2cf1

If I comment the line *.Filter = fl* above, [B]IT DOESN'T CRASH[B] the form opens OK, but then it shows all extensions.

Many thanks.

----------


## LaVolpe

@DerWise. I think I may have it but am not sure. 

In your copy of the class, can you make the following changes, for testing only. We are going to take the sItems() array out of pvInitStructure and move it to the declarations section:

1. Add this at top of declarations section: Private sItems() As String
2. In pvInitStructure method...
a. remove sItems from the DIM statements
b. remove the line: Erase sItems()

Now try it.

----------


## DerWeise

> @DerWise. I think I may have it but am not sure. 
> 
> In your copy of the class, can you make the following changes, for testing only. We are going to take the sItems() array out of pvInitStructure and move it to the declarations section:
> 
> 1. Add this at top of declarations section: Private sItems() As String
> 2. In pvInitStructure method...
> a. remove sItems from the DIM statements
> b. remove the line: Erase sItems()
> 
> Now try it.


Sorry, still hangs but slightly differently.
1. It now takes about 1 second after I click SAVE before the hang
2. The Fault Module shown is propsys.dll

One more question:
We use MANY absolute variables declared in several modules.
I didn't see any in your code.
Still is there a chance we step on something of yours?
(I found one, pIndex, and changed it to be safe, but there can be many others.

Thanks!

----------


## LaVolpe

> Still is there a chance we step on something of yours?


Absolutely not. In a VB class, variables referenced are those in the procedure first, class declarations second, then global ones (not applicable here).

One thing has me confused still. You said that the sample project that came with the zip did not crash? Even if you go and change the filter in the sample code? 

Grasping at straws here. If you set the Flags property to DLG__BaseSaveDialogFlags any change? Another question, is this only with the Save dialog or the Open dialog also, when using the same filter?

And finally, do you have the most recent Win7 service pack installed? I can find many hits on the web for SetFileTypes (filter) crashing Win7, but am not finding anything that looks like it applies to the class I wrote. Your description sounds almost like the Dialog is trying to access some variable after it closes, after the class released and cleared all of its variables. To test that idea, simply do not set the class to nothing and see if the error continues.

----------


## DerWeise

I'm trying to answer, but by the time I finish the answer it shows me logged out and all my writing is gone....
I can show you everything and discuss it seriously if you skype me.
How can I write you my contact not public?
Thanks.

----------


## DerWeise

I'm trying to answer, but by the time I finish the answer it shows me logged out and all my writing is gone....
I can show you everything and discuss it seriously if you skype me.
How can I write you my contact not public?
Thanks.

----------


## LaVolpe

> How can I write you my contact not public?
> Thanks.


Click on a user's handle/user name in left side of posting and choose to send a PM (private message).

----------


## AAraya

Hi LaVolpe - I used the old version of this class from the thread:

http://www.vbforums.com/showthread.p...-%28No-TLBs%29

But I just found an issue today.  When I navigate to Win\Sys32 in my app, I see the contents of Win\SysWOW64.  I'm aware that this is due to file system redirection.  So I used the same code I use elsewhere in my app to disable FS redirection before using the class but it doesn't fix the problem.  I still see SysWow64 contents.  Is this a bug you're aware of?  Is this resolved in the new one?  Before I replace the old one with a new one I'd like to know.

Thanks!

----------


## Victor Bravo VI

> Is this resolved in the new one?


I don't know if LaVolpe already has a fix for this but I found a possible solution here. The attached zip file below contains a VB6 port of the C++ code from that link plus a demo. If you don't find that solution satisfactory, you might want to investigate this alternative approach: IFileDialogEvents::OnFolderChanging + IFileDialog::SetFolder + SysNative.

----------


## hl88

```
' basket mode. select files across various folders. there are 5 basket mode options
 cBrowser.Controls_SetCustomMode cm_BasketModeFoldersOnly
```

cm_BaketModeFoldersOnly isn't working, dialog show only folder but can't add any folder.  I use cm_BasketModeFilesOrFolders instead

----------

