VBA File Dialogs

A file dialog is a window that allows a user to manually select a directory or files. The FileDialog class defined in the Office Object Library can be used by certain Office applications to create a file dialog window. In applications where the FileDialog class is not available, Windows API functions can be used to create a file dialog.

FileDialog Class

The FileDialog class can be used to create a file dialog window to select elements of the file system and return a path string. The FileDialog class can be accessed as a property of the Application object in Excel, Word, Access, PowerPoint, and Publisher. The FileDialog class is not available in Outlook, Project, or Visio. The FileDialog class has flexible properties that can be set to configure the file dialog for specific situations. The FileDialog property takes a required argument from the MsoFileDialogType enum to create a specific type of file dialog. The various properties of the FileDialog object can be set to tailor the dialog to a specific use and then the Show method can be called to launch the window:

Member Description
Execute Executes a user's action after the Show method is called when an Open or SaveAs dialog box is used.
Show Shows the file dialog window and returns -1 if the action button is clicked and 0 if cancel is clicked.
AllowMultiSelect When set to True, multiple files can be selected. Does not work for the folder picker dialog or the save as dialog.
Application Returns the Application object for the application containing the FileDialog object.
ButtonName Sets the text on the action button.
Creator Returns a 32-bit integer representing the application containing the FileDialog object.
DialogType Returns the MsoFileDialogType representing the type of dialog.
FilterIndex Sets the starting filter from the Filters collection for when the dialog window opens.
Filters A FileDialogFilters collection object containing FileDialogFilter objects representing file type filters for the FileDialog object.
InitialFileName Sets the initial path when the dialog window opens.
InitialView A MsoFileDialogView representing the initial view type when the dialog window opens.
Item Returns the text associated with the object.
Parent Returns the parent object of the FileDialog object.
SelectedItems A FileDialogSelectedItems collection object containing the selected file paths as strings.
Title Sets the title text of the dialog window.

Select a Single File

Return the value at the first index of the SelectedItems collection.

Public Sub SelectSingleFile()

    Dim FD As Office.FileDialog
    Set FD = Application.FileDialog(msoFileDialogFilePicker)

    With FD
        .AllowMultiSelect = False
        .ButtonName = "Select File"
        .Filters.Clear
        .Filters.Add "All Files", "*.*"
        .Filters.Add "Excel Files", "*.xl*"
        .FilterIndex = 1
        .InitialFileName = Environ$("USERPROFILE") & "\Desktop\"
        .InitialView = msoFileDialogViewDetails
        .Title = "Select a single file"
        If .Show = -1 Then
            Debug.Print .SelectedItems(1)
        Else
            Debug.Print "No file selected."
        End If
    End With

End Sub

Select Multiple Files

Hold the Ctrl key to select multiple files.

Public Sub SelectMultipleFiles()

    Dim FD As Office.FileDialog
    Set FD = Application.FileDialog(msoFileDialogFilePicker)

    With FD
        .AllowMultiSelect = True
        .ButtonName = "Select File(s)"
        .Filters.Clear
        .Filters.Add "All Files", "*.*"
        .Filters.Add "Excel Files", "*.xl*"
        .FilterIndex = 1
        .InitialFileName = Environ$("USERPROFILE") & "\Desktop\"
        .InitialView = msoFileDialogViewDetails
        .Title = "Select files(s)"
        If .Show = -1 Then
            Dim i As Long
            For i = 1 To .SelectedItems.Count
                Debug.Print .SelectedItems(i)
            Next i
        Else
            Debug.Print "No files selected."
        End If
    End With

End Sub

Select a Folder

Properties that only pertain to selecting files do not apply to the folder picker dialog.

Public Sub SelectFolder()

    Dim FD As Office.FileDialog
    Set FD = Application.FileDialog(msoFileDialogFolderPicker)

    With FD
        .ButtonName = "Select Folder"
        .InitialFileName = Environ$("USERPROFILE") & "\Desktop\"
        .InitialView = msoFileDialogViewDetails
        .Title = "Select a Folder"
        If .Show = -1 Then
            Debug.Print .SelectedItems(1)
        Else
            Debug.Print "No folder selected."
        End If
    End With

End Sub

Open/SaveAs

Call the Execute method after using the Show method to execute the action of the dialog.

Public Sub OpenExcelFiles()

    Dim FD As Office.FileDialog
    Set FD = Application.FileDialog(msoFileDialogOpen)

    With FD
        .AllowMultiSelect = True
        .ButtonName = "Select Excel File"
        .Filters.Clear
        .Filters.Add "Excel Files", "*.xl*"
        .FilterIndex = 1
        .InitialFileName = Environ$("USERPROFILE") & "\Desktop\"
        .InitialView = msoFileDialogViewDetails
        .Title = "Select Excel files to open"
        If .Show = -1 Then
            .Execute
        Else
            Debug.Print "No file selected."
        End If
    End With

End Sub

Windows API

The OpenFileNameA function or the GetSaveFileNameA function can be used to open a file dialog window and select a file path. Both functions take the OpenFileName struct as a parameter. If the user selects a file path and click OK the functions will return a nonzero value. If the user cancels or an error occurs the functions will return zero. To get detailed error information when the functions do not work as expected use the CommDlgExtendedError function. Setting the values of the OpenFileName struct members and returning errors from these functions requires detailed implementation so it is best to use wrapper functions to call these functions and hide implementation details from the user.

Option Explicit

Public Sub Example()

    On Error GoTo HandleError

    Dim FileName As String
    FileName = GetOpenFileNameWinAPI()

    Debug.Print FileName

    Exit Sub

HandleError:
    If Err.Number - (vbObjectError + 512) = 2 Then
        Debug.Print "No file selected."
    Else
        Err.Raise _
            Number:=Err.Number, _
            Description:=Err.Description
    End If

End Sub
Option Explicit

#If VBA7 = 1 Then

    Private Type OPENFILENAME
        lStructSize         As Long
        hwndOwner           As LongPtr
        hInstance           As LongPtr
        lpstrFilter         As String
        lpstrCustomFilter   As String
        nMaxCustFilter      As Long
        nFilterIndex        As Long
        lpstrFile           As String
        nMaxFile            As Long
        lpstrFileTitle      As String
        nMaxFileTitle       As Long
        lpstrInitialDir     As String
        lpstrTitle          As String
        flags               As Long
        nFileOffset         As Integer
        nFileExtension      As Integer
        lpstrDefExt         As String
        lCustData           As LongPtr
        lpfnHook            As LongPtr
        lpTemplateName      As String
        '#if (_WIN32_WINNT >= 0x0500)
        pvReserved          As LongPtr
        dwReserved          As Long
        FlagsEx             As Long
        '#endif // (_WIN32_WINNT >= 0x0500)
    End Type

    Private Declare PtrSafe Function GetOpenFileName _
        Lib "comdlg32.dll" _
        Alias "GetOpenFileNameA" ( _
        pOpenfilename As OPENFILENAME) As Long

    Private Declare PtrSafe Function CommDlgExtendedError _
        Lib "comdlg32.dll" () As Long

#Else

    Private Type OPENFILENAME
        lStructSize        As Long
        hwndOwner          As Long
        hInstance          As Long
        lpstrFilter        As String
        lpstrCustomFilter  As String
        nMaxCustFilter     As Long
        nFilterIndex       As Long
        lpstrFile          As String
        nMaxFile           As Long
        lpstrFileTitle     As String
        nMaxFileTitle      As Long
        lpstrInitialDir    As String
        lpstrTitle         As String
        flags              As Long
        nFileOffset        As Integer
        nFileExtension     As Integer
        lpstrDefExt        As String
        lCustData          As Long
        lpfnHook           As Long
        lpTemplateName     As String
    '#if (_WIN32_WINNT >= 0x0500)
        pvReserved         As Long
        dwReserved         As Long
        FlagsEx            As Long
    '#endif // (_WIN32_WINNT >= 0x0500)
    End Type

    Private Declare Function GetOpenFileName _
        Lib "comdlg32.dll" _
        Alias "GetOpenFileNameA" ( _
        pOpenfilename As OPENFILENAME) As Long

    Private Declare Function CommDlgExtendedError _
        Lib "comdlg32.dll" () As Long

#End If

Private Const OFN_READONLY = &H1
Private Const OFN_OVERWRITEPROMPT = &H2
Private Const OFN_HIDEREADONLY = &H4
Private Const OFN_NOCHANGEDIR = &H8
Private Const OFN_SHOWHELP = &H10
Private Const OFN_ENABLEHOOK = &H20
Private Const OFN_ENABLETEMPLATE = &H40
Private Const OFN_ENABLETEMPLATEHANDLE = &H80
Private Const OFN_NOVALIDATE = &H100
Private Const OFN_ALLOWMULTISELECT = &H200
Private Const OFN_EXTENSIONDIFFERENT = &H400
Private Const OFN_PATHMUSTEXIST = &H800
Private Const OFN_FILEMUSTEXIST = &H1000
Private Const OFN_CREATEPROMPT = &H2000
Private Const OFN_SHAREAWARE = &H4000
Private Const OFN_NOREADONLYRETURN = &H8000&
Private Const OFN_NOTESTFILECREATE = &H10000
Private Const OFN_NONETWORKBUTTON = &H20000
Private Const OFN_NOLONGNAMES = &H40000          '  force no long names for 4.x modules
Private Const OFN_EXPLORER = &H80000             '  new look commdlg
Private Const OFN_NODEREFERENCELINKS = &H100000
Private Const OFN_LONGNAMES = &H200000           '  force long names for 3.x modules

Private Const OFN_SHAREFALLTHROUGH = 2
Private Const OFN_SHARENOWARN = 1
Private Const OFN_SHAREWARN = 0

Private Const CDERR_DIALOGFAILURE = &HFFFF&

Private Const CDERR_GENERALCODES = &H0
Private Const CDERR_STRUCTSIZE = &H1
Private Const CDERR_INITIALIZATION = &H2
Private Const CDERR_NOTEMPLATE = &H3
Private Const CDERR_NOHINSTANCE = &H4
Private Const CDERR_LOADSTRFAILURE = &H5
Private Const CDERR_FINDRESFAILURE = &H6
Private Const CDERR_LOADRESFAILURE = &H7
Private Const CDERR_LOCKRESFAILURE = &H8
Private Const CDERR_MEMALLOCFAILURE = &H9
Private Const CDERR_MEMLOCKFAILURE = &HA
Private Const CDERR_NOHOOK = &HB
Private Const CDERR_REGISTERMSGFAIL = &HC

Public Function GetOpenFileNameWinAPI() As String

    Const MAX_BUFFER As Long = 255

    Dim OFN As OPENFILENAME

    With OFN
        '.hwndOwner
        '.hInstance
        .lpstrFilter = "All Files" & vbNullChar & "*.*" & String$(2, vbNullChar)
        '.lpstrCustomFilter
        '.nMaxCustFilter
        .nFilterIndex = 1
        .lpstrFile = String$(MAX_BUFFER - 1, " ") & vbNullChar
        .nMaxFile = Len(.lpstrFile)
        .lpstrFileTitle = String$(MAX_BUFFER - 1, " ") & vbNullChar
        .nMaxFileTitle = Len(.lpstrFileTitle)
        .lpstrInitialDir = "C:\" & vbNullChar
        '.lpstrTitle
        .flags = OFN_FILEMUSTEXIST Or OFN_PATHMUSTEXIST
        '.nFileOffset
        '.nFileExtension
        '.lpstrDefExt
        '.lCustData
        '.lpfnHook
        '.lpTemplateName
        '.pvReserved
        '.dwReserved
        '.FlagsEx
        .lStructSize = LenB(OFN)
    End With

    Dim Result As Long
    Result = GetOpenFileName(OFN)

    If Result <> 0 Then
        GetOpenFileNameWinAPI = Trim$(Replace(OFN.lpstrFile, vbNullChar, " "))
    Else
        Dim CDErr As Long
        CDErr = CommDlgExtendedError()
        Dim UDErr As Long
        Dim ErrMsg As String
        Select Case CDErr
            Case CDERR_DIALOGFAILURE
                UDErr = 1
                ErrMsg = "DIALOG FAILURE"
            Case CDERR_GENERALCODES
                UDErr = 2
                ErrMsg = "GENERAL CODES"
            Case CDERR_STRUCTSIZE
                UDErr = 3
                ErrMsg = "STRUCT SIZE"
            Case CDERR_INITIALIZATION
                UDErr = 4
                ErrMsg = "INITIALIZATION"
            Case CDERR_NOTEMPLATE
                UDErr = 5
                ErrMsg = "NO TEMPLATE"
            Case CDERR_NOHINSTANCE
                UDErr = 6
                ErrMsg = "NO HINSTANCE"
            Case CDERR_LOADSTRFAILURE
                UDErr = 7
                ErrMsg = "LOAD STR FAILURE"
            Case CDERR_FINDRESFAILURE
                UDErr = 8
                ErrMsg = "FIND RES FAILURE"
            Case CDERR_LOADRESFAILURE
                UDErr = 9
                ErrMsg = "LOAD RES FAILURE"
            Case CDERR_LOCKRESFAILURE
                UDErr = 10
                ErrMsg = "LOCK RES FAILURE"
            Case CDERR_MEMALLOCFAILURE
                UDErr = 11
                ErrMsg = "MEM ALLOC FAILURE"
            Case CDERR_MEMLOCKFAILURE
                UDErr = 12
                ErrMsg = "MEM LOCK FAILURE"
            Case CDERR_NOHOOK
                UDErr = 13
                ErrMsg = "NO HOOK"
            Case CDERR_REGISTERMSGFAIL
                UDErr = 14
                ErrMsg = "REGISTER MSG FAIL"
            Case Else
                UDErr = 15
                ErrMsg = "Unknown Error"
        End Select
        Err.Raise _
            Number:=vbObjectError + 512 + UDErr, _
            Description:=ErrMsg
    End If

End Function