Attribute VB_Name = "FileDialog"
Option Explicit

'------------------------------------------------------
' VB5/6 compatible File Open/Save As Dialog wrapper
'------------------------------------------------------

Private Declare Function GetOpenFileName Lib "comdlg32" Alias "GetOpenFileNameW" (ByVal lpOFN As Long) As Long
Private Declare Function GetSaveFileName Lib "comdlg32" Alias "GetSaveFileNameW" (ByVal lpOFN As Long) As Long
Private Declare Function CommDlgExtendedError Lib "comdlg32" () As Long

' Common dialog error return codes
Private Const CDERR_GENERALCODES = &H0&      ' The user canceled or closed the dialog box.
Private Const FNERR_BUFFERTOOSMALL = &H3003& ' The buffer pointed to by the lpstrFile member of the OPENFILENAME structure is too small for the filename specified by the user. The first two bytes of the lpstrFile buffer contain an integer value specifying the size, in bytes (ANSI version) or 16-bit characters (Unicode version), required to receive the full name.

Private Type POINTAPI
    x As Long
    y As Long
End Type

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Private Type WINDOWPLACEMENT
    Length As Long
    lFlags As Long
    showCmd As Long
    ptMinPosition As POINTAPI
    ptMaxPosition As POINTAPI
    rcNormalPosition As RECT
End Type

Private Declare Function GetWindowPlacement Lib "user32" (ByVal hWnd As Long, lpWndpl As WINDOWPLACEMENT) As Long
Private Declare Sub SetWindowPos Lib "user32" (ByVal hWnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long)

Private Const HWND_TOP As Long = 0
Private Const SWP_SHOWWINDOW = &H40&
Private Const WM_INITDIALOG = &H110&

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal lLenB As Long)
Private Declare Function StrLenW Lib "kernel32" Alias "lstrlenW" (ByVal StrPtr_Str As Long) As Long
Private Declare Function GetParent Lib "user32" (ByVal hWnd As Long) As Long

' The OPENFILENAME structure contains information that the GetOpenFileName and GetSaveFileName
' functions use to initialize an Open or Save As common dialog box. After the user closes the
' dialog box, the system returns information about the user's selection in this structure.

Private Type OPENFILENAME
    lStructSize As Long         ' Specifies the length, in bytes, of the structure
    hWndOwner As Long           ' Identifies the window that owns the dialog box. This member can be any valid window handle, or it can be NULL if the dialog box has no owner
    hInstance As Long           ' If the OFN_ENABLETEMPLATEHANDLE flag is set in the Flags member, hInstance is the handle of a memory object containing a dialog box template. If the OFN_ENABLETEMPLATE flag is set, hInstance identifies a module that contains a dialog box template named by the lpTemplateName member. If neither flag is set, this member is ignored. If the OFN_EXPLORER flag is set, the system uses the specified template to create a dialog box that is a child of the default Explorer-style dialog box. If the OFN_EXPLORER flag is not set, the system uses the template to create an old-style dialog box that replaces the default dialog box.
    lpstrFilter As String       ' Pointer to a buffer containing pairs of null-terminated filter strings. The last string in the buffer must be terminated by two NULL characters. The first string in each pair is a display string that describes the filter (for example, "Text Files"), and the second string specifies the filter pattern (for example, "*.TXT"). To specify multiple filter patterns for a single display string, use a semicolon to separate the patterns (for example, "*.TXT;*.DOC;*.BAK"). A pattern string can be a combination of valid filename characters and the asterisk (*) wildcard character. Do not include spaces in the pattern string. The system does not change the order of the filters. It displays them in the File Types combo box in the order specified in lpstrFilter. If lpstrFilter is NULL, the dialog box does not display any filters.
    lpstrCustomFilter As String ' Pointer to a static buffer that contains a pair of null-terminated filter strings for preserving the filter pattern chosen by the user. The first string is your display string that describes the custom filter, and the second string is the filter pattern selected by the user. The first time your application creates the dialog box, you specify the first string, which can be any nonempty string. When the user selects a file, the dialog box copies the current filter pattern to the second string. The preserved filter pattern can be one of the patterns specified in the lpstrFilter buffer, or it can be a filter pattern typed by the user. The system uses the strings to initialize the user-defined file filter the next time the dialog box is created. If the nFilterIndex member is zero, the dialog box uses the custom filter. If this member is NULL, the dialog box does not preserve user-defined filter patterns.
                                ' If this member is not NULL, the value of the nMaxCustFilter member must specify the size, in bytes (ANSI version) or 16-bit characters (Unicode version), of the lpstrCustomFilter buffer.
    nMaxCustFilter As Long      ' Specifies the size, in bytes or characters, of the buffer identified by lpstrCustomFilter. This buffer should be at least 40 characters long. This member is ignored if lpstrCustomFilter is NULL or points to a NULL string.
    nFilterIndex As Long        ' Specifies the index of the currently selected filter in the File Types control. The buffer pointed to by lpstrFilter contains pairs of strings that define the filters. The first pair of strings has an index value of 1, the second pair 2, and so on. An index of zero indicates the custom filter specified by lpstrCustomFilter. You can specify an index on input to indicate the initial filter description and filter pattern for the dialog box. When the user selects a file, nFilterIndex returns the index of the currently displayed filter. If nFilterIndex is zero and lpstrCustomFilter is NULL, the system uses the first filter in the lpstrFilter buffer. If all three members are zero or NULL, the system does not use any filters and does not show any files in the file list control of the dialog box.
    lpstrFile As String         ' Pointer to a buffer that contains a filename used to initialize the File Name edit control. The first character of this buffer must be NULL if initialization is not necessary. When the GetOpenFileName or GetSaveFileName function returns successfully, this buffer contains the drive designator, path, filename, and extension of the selected file. If the OFN_ALLOWMULTISELECT flag is set and the user selects multiple files, the buffer contains the current directory followed by the filenames of the selected files. For Explorer-style dialog boxes, the directory and filename strings are NULL separated, with an extra NULL character after the last filename. For old-style dialog boxes, the strings are space separated and the function uses short filenames for filenames with spaces. You can use the FindFirstFile function to convert between long and short filenames.
                                ' If the buffer is too small, the function returns FALSE and the CommDlgExtendedError function returns FNERR_BUFFERTOOSMALL. In this case, the first two bytes of the lpstrFile buffer contain the required size, in bytes or characters.
    nMaxFile As Long            ' Specifies the size, in bytes (ANSI version) or 16-bit characters (Unicode version), of the buffer pointed to by lpstrFile. The GetOpenFileName and GetSaveFileName functions return FALSE if the buffer is too small to contain the file information. The buffer should be at least 256 characters long.
    lpstrFileTitle As String    ' Pointer to a buffer that receives the filename and extension (without path information) of the selected file. This member can be NULL.
    nMaxFileTitle As Long       ' Specifies the size, in bytes (ANSI version) or 16-bit characters (Unicode version), of the buffer pointed to by lpstrFileTitle. This member is ignored if lpstrFileTitle is NULL.
    lpstrInitialDir As String   ' Pointer to a string that specifies the initial file directory. NT5+/98+: If lpstrInitalDir is NULL and the current directory contains any files of the specified filter types, the initial directory is the current directory. If lpstrInitalDir is NULL and the current directory does not contain any files of the specified filter types, the initial directory is the personal files directory of the current user. To specify the current user's personal files directory as the initial directory, set lpstrInitialDir to the path returned by calling theSHGetSpecialFolderLocation function with the CSIDL_PERSONAL flag.
    lpstrTitle As String        ' Pointer to a string to be placed in the title bar of the dialog box. If this member is NULL, the system uses the default title (that is, Save As or Open).
    lFlags As Long              ' A set of bit flags you can use to initialize the dialog box. When the dialog box returns, it sets these flags to indicate the user's input. This member can be a combination of the following flags: Flag meaning described below
    nFileOffset As Integer      ' Specifies the zero-based offset, in bytes (ANSI version) or 16-bit characters (Unicode version), from the beginning of the path to the filename in the string pointed to by lpstrFile. For example, if lpstrFile points to the following string, "c:\dir1\dir2\file.ext", this member contains the value 13 to indicate the offset of the "file.ext" string. If the user selects more than one file, nFileOffset is the offset to the first filename.
    nFileExtension As Integer   ' Specifies the zero-based offset, in bytes (ANSI version) or 16-bit characters (Unicode version), from the beginning of the path to the filename extension in the string pointed to by lpstrFile. For example, if lpstrFile points to the following string, "c:\dir1\dir2\file.ext", this member contains the value 18. If the user did not type an extension and lpstrDefExt is NULL, this member specifies an offset to the terminating null character. If the user typed "." as the last character in the filename, this member specifies zero.
    lpstrDefExt As String       ' Points to a buffer that contains the default extension. GetOpenFileName and GetSaveFileName append this extension to the filename if the user fails to type an extension. This string can be any length, but only the first three characters are appended. The string should not contain a period (.). If this member is NULL and the user fails to type an extension, no extension is appended.
    lCustData As Long           ' Specifies application-defined data that the system passes to the hook procedure identified by the lpfnHook member. When the system sends the WM_INITDIALOG message to the hook procedure, the message's lParam parameter is a pointer to the OPENFILENAME structure specified when the dialog box was created. The hook procedure can use this pointer to get the lCustData value.
    lpfnHook As Long            ' Pointer to a hook procedure. This member is ignored unless the Flags member includes the OFN_ENABLEHOOK flag. If the OFN_EXPLORER flag is not set in the Flags member, lpfnHook is a pointer to an OFNHookProcOldStyle hook procedure that receives messages intended for the dialog box. The hook procedure returns FALSE to pass a message to the default dialog box procedure or TRUE to discard the message. If OFN_EXPLORER is set, lpfnHook is a pointer to an OFNHookProc hook procedure. The hook procedure receives notification messages sent from the dialog box. The hook procedure also receives messages for any additional controls that you defined by specifying a child dialog template. The hook procedure does not receive messages intended for the standard controls of the default dialog box.
    lpTemplateName As String    ' Pointer to a null-terminated string that names a dialog template resource in the module identified by the hInstance member. For numbered dialog box resources, this can be a value returned by the MAKEINTRESOURCE macro. This member is ignored unless the OFN_ENABLETEMPLATE flag is set in the Flags member. If the OFN_EXPLORER flag is set, the system uses the specified template to create a dialog box that is a child of the default Explorer-style dialog box. If the OFN_EXPLORER flag is not set, the system uses the template to create an old-style dialog box that replaces the default dialog box.
End Type

' File Open/Save Dialog Flags
Public Enum OFN_FLAGS
    OFN_READONLY = &H1&                ' Causes the Read Only check box to be checked initially when the dialog box is created. This flag indicates the state of the Read Only check box when the dialog box is closed.
    OFN_OVERWRITEPROMPT = &H2&         ' Causes the Save As dialog box to generate a message box if the selected file already exists. The user must confirm whether to overwrite the file.
    OFN_HIDEREADONLY = &H4&            ' Hides the Read Only check box.
    OFN_NOCHANGEDIR = &H8&             ' Restores the current directory to its original value if the user changed the directory while searching for files.
    OFN_SHOWHELP = &H10&               ' Causes the dialog box to display the Help button. The hwndOwner member must specify the window to receive the HELPMSGSTRING registered messages that the dialog box sends when the user clicks the Help button. An Explorer-style dialog box sends a CDN_HELP notification message to your hook procedure when the user clicks the Help button.
    OFN_ENABLEHOOK = &H20&             ' Enables the hook function specified in the lpfnHook member.
    OFN_ENABLETEMPLATE = &H40&         ' Indicates that the lpTemplateName member points to the name of a dialog template resource in the module identified by the hInstance member. If the OFN_EXPLORER flag is set, the system uses the specified template to create a dialog box that is a child of the default Explorer-style dialog box. If the OFN_EXPLORER flag is not set, the system uses the template to create an old-style dialog box that replaces the default dialog box.
    OFN_ENABLETEMPLATEHANDLE = &H80&   ' Indicates that the hInstance member identifies a data block that contains a preloaded dialog box template. The system ignores the lpTemplateName if this flag is specified. If the OFN_EXPLORER flag is set, the system uses the specified template to create a dialog box that is a child of the default Explorer-style dialog box. If the OFN_EXPLORER flag is not set, the system uses the template to create an old-style dialog box that replaces the default dialog box.
    OFN_NOVALIDATE = &H100&            ' Specifies that the common dialog boxes allow invalid characters in the returned filename. Typically, the calling application uses a hook procedure that checks the filename by using the FILEOKSTRING message. If the text box in the edit control is empty or contains nothing but spaces, the lists of files and directories are updated. If the text box in the edit control contains anything else, nFileOffset and nFileExtension are set to values generated by parsing the text. No default extension is added to the text, nor is text copied to the buffer specified by lpstrFileTitle. If the value specified by nFileOffset is less than zero, the filename is invalid. Otherwise, the filename is valid, and nFileExtension and nFileOffset can be used as if the OFN_NOVALIDATE flag had not been specified.
    OFN_ALLOWMULTISELECT = &H200&      ' Specifies that the File Name list box allows multiple selections. If you also set the OFN_EXPLORER flag, the dialog box uses the Explorer-style user interface; otherwise, it uses the old-style user interface. If the user selects more than one file, the lpstrFile buffer returns the path to the current directory followed by the filenames of the selected files. The nFileOffset member is the offset, in bytes or characters, to the first filename, and the nFileExtension member is not used. For Explorer-style dialog boxes, the directory and filename strings are NULL separated, with an extra NULL character after the last filename. This format enables the Explorer-style dialogs to return long filenames that include spaces. For old-style dialog boxes, the directory and filename strings are separated by spaces and the function uses short filenames for filenames with spaces. You can use theFindFirstFile function to convert between long and short filenames.
                                       ' If you specify a custom template for an old-style dialog box, the definition of the File Name list box must contain the LBS_EXTENDEDSEL value.
    OFN_EXTENTIONDIFFERENT = &H400&    ' Specifies that the user typed a filename extension that differs from the extension specified by lpstrDefExt. The function does not use this flag if lpstrDefExt is NULL.
    OFN_PATHMUSTEXIST = &H800&         ' Specifies that the user can type only valid paths and filenames. If this flag is used and the user types an invalid path and filename in the File Name entry field, the dialog box function displays a warning in a message box.
    OFN_FILEMUSTEXIST = &H1000&        ' Specifies that the user can type only names of existing files in the File Name entry field. If this flag is specified and the user enters an invalid name, the dialog box procedure displays a warning in a message box. If this flag is specified, the OFN_PATHMUSTEXIST flag is also used.
    OFN_CREATEPROMPT = &H2000&         ' If the user specifies a file that does not exist, this flag causes the dialog box to prompt the user for permission to create the file. If the user chooses to create the file, the dialog box closes and the function returns the specified name; otherwise, the dialog box remains open. If you use this flag with the OFN_ALLOWMULTISELECT flag, the dialog box allows the user to specify only one nonexistent file.
    OFN_SHAREAWARE = &H4000&           ' Specifies that if a call to the OpenFile function fails because of a network sharing violation, the error is ignored and the dialog box returns the selected filename. If this flag is not set, the dialog box notifies your hook procedure when a network sharing violation occurs for the filename specified by the user. If you set the OFN_EXPLORER flag, the dialog box sends the CDN_SHAREVIOLATION message to the hook procedure. If you do not set OFN_EXPLORER, the dialog box sends the SHAREVISTRING registered message to the hook procedure.
    OFN_NOREADONLYRETURN = &H8000&     ' Specifies that the returned file does not have the Read Only check box checked and is not in a write-protected directory.
    OFN_NOTESTFILECREATE = &H10000     ' Specifies that the file is not created before the dialog box is closed. This flag should be specified if the application saves the file on a create-nonmodify network share. When an application specifies this flag, the library does not check for write protection, a full disk, an open drive door, or network protection. Applications using this flag must perform file operations carefully, because a file cannot be reopened once it is closed.
    OFN_NONETWORKBUTTON = &H20000      ' Hides and disables the Network button.
    OFN_NOLONGNAMES = &H40000          ' Force no long names for 4.x modules. For old-style dialog boxes, this flag causes the dialog box to use short filenames (8.3 format). Explorer-style dialog boxes ignore this flag and always display long filenames.
    OFN_EXPLORER = &H80000             ' New look commdlg. Indicates that any customizations made to the Open or Save As dialog box use the new Explorer-style customization methods. For more information, see Explorer-Style Hook Procedures and Explorer-Style Custom Templates. By default, the Open and Save As dialog boxes use the Explorer-style user interface regardless of whether this flag is set. This flag is necessary only if you provide a hook procedure or custom template, or set the OFN_ALLOWMULTISELECT flag. If you want the old-style user interface, omit the OFN_EXPLORER flag and provide a replacement old-style template or hook procedure. If you want the old style but do not need a custom template or hook procedure, simply provide a hook procedure that always returns FALSE.
    OFN_NODEREFERENCELINKS = &H100000  ' Directs the dialog box to return the path and filename of the selected shortcut (.LNK) file. If this value is not given, the dialog box returns the path and filename of the file referenced by the shortcut.
    OFN_LONGNAMES = &H200000           ' Force long names for 3.x modules. For old-style dialog boxes, this flag causes the dialog box to use long filenames. If this flag is not specified, or if the OFN_ALLOWMULTISELECT flag is also set, old-style dialog boxes use short filenames (8.3 format) for filenames with spaces. Explorer-style dialog boxes ignore this flag and always display long filenames.
    OFN_ENABLESIZING = &H800000        ' Windows NT 5.0, Windows 98: Enables the Explorer-style dialog box to be resized using either the mouse or the keyboard. By default, the Explorer-style Open and Save As dialog boxes allow the dialog box to be resized regardless of whether this flag is set. This flag is necessary only if you provide a hook procedure or custom template. The old-style dialog box does not permit resizing.
    OFN_DONTADDTORECENT = &H2000000    ' Windows NT 4.0, 2000, XP: Prevents the system from adding a link to the selected file in the file system directory that contains the user's most recently used documents. To retrieve the location of this directory, call the SHGetSpecialFolderLocation function with the CSIDL_RECENT flag.
    OFN_FORCESHOWHIDDEN = &H10000000   ' Windows NT 4.0, 2000, XP: Forces the showing of system and hidden files, thus overriding the user setting to show or not show hidden files. However, a file that is marked both system and hidden is not shown.
End Enum

Private tOFN As OPENFILENAME
Private Const ONE_KB = &H400&
Private Const MAX_PATH = 260&
Private m_sPrevDir As String
'

'------------------------------------------------------

' If you wish to specify the default directory to open to, you
' set the InitialDir property before calling ShowOpen or ShowSave.

' This InitialDir setting *will be used for all subsequent calls*
' until it is reset by you.

' Use the FileName property to initialize the File Name edit control.
' If the user cancels this property will be reset to vbNullString.

'-ShowOpen---------------------------------------------
' Returns 0 if user cancels, 1 for single file selection, and -1 for multiple files.
' The Read Only FileNames property retrieves the selected file name(s) by
' populating the byref string array parameter and returns the file count.
' The FileName property string also contains the user selected file name(s).
' If a single file name was selected, this property is the path and file name.
' If multiple file names were selected, the path is specified first, then
' followed by each file name, all separated by the vbNullChar character.
'------------------------------------------------------
Function ShowOpen(Optional ByVal Me_hWnd As Long) As Long
    Dim rc As Long
   On Error GoTo ErrWith
    With tOFN
       .lStructSize = LenB(tOFN)
       .hWndOwner = Me_hWnd
        If LenB(.lpstrTitle) = 0& Then .lpstrTitle = " Open..."
       .lpstrFile = Validate(.lpstrFile) & String$(ONE_KB, vbNullChar)
       .nMaxFile = Len(.lpstrFile)
       .lpstrFileTitle = String$(MAX_PATH, vbNullChar)
       .nMaxFileTitle = Len(.lpstrFileTitle)
        If StrLenW(StrPtr(.lpstrFilter)) = 0& Then
           Filter = " Text Documents     |*.txt;*.log;*.ini;*.diz;*.lst;*.dat;*.inf;*.reg|" & _
                    " Rich Text Format   |*.rtf;*.wri;*.doc;*.hpj;*.cnt;*.hm;*.hhm|" & _
                    " Web Based Files    |*.htm*;*.shtml;*.css;*.vbs;*.jav*;*.js;*.cod|" & _
                    " Initialization     |*.bat;*.ini;*.cfg;*.sys;*.mak;*.inf;*.reg|" & _
                    " Visual Basic Files |*.vbp;*.bas;*.ctl;*.frm;*.cls;*.dsr;*.pag;*.dob|" & _
                    " Source Code Files  |*.pas;*.for;*.pl;*.c;*.h;*.cpp;*.rc;*.asm;*.inc;*.mac;*.js|" & _
                    " Graphic Image Files|*.bmp;*.gif;*.jp*;*.jfif;*.png;*.ico;*.cur;*.ani|" & _
                    " All  File Types    |*.*|"
        End If
        If LenB(.lpstrDefExt) = 0& Then .lpstrDefExt = vbNullChar '!
        If .nFilterIndex = 0& Then .nFilterIndex = 1&
        If .lFlags = 0& Then
            .lFlags = OFN_FILEMUSTEXIST Or OFN_ALLOWMULTISELECT Or OFN_HIDEREADONLY
        End If
       .lFlags = .lFlags Or OFN_EXPLORER Or OFN_ENABLEHOOK
       .lpfnHook = GetAddress(AddressOf OFNHookProc)
ErrWith:
    End With
  If Err Then GoTo ErrOut
  On Error GoTo ErrOut
    ShowOpen = GetOpenFileName(VarPtr(tOFN))
    If ShowOpen <> 0& Then
        If StrLenW(StrPtr(tOFN.lpstrFileTitle)) = 0& Then ShowOpen = -1&
        m_sPrevDir = CurDir$
    Else
        rc = CommDlgExtendedError
        If rc = CDERR_GENERALCODES Then ' User canceled
            tOFN.lpstrFile = vbNullString
        ElseIf rc = FNERR_BUFFERTOOSMALL Then
            tOFN.lpstrFile = tOFN.lpstrFile & String$(ONE_KB, vbNullChar)
            Dim s As String
            s = tOFN.lpstrTitle
            tOFN.lpstrTitle = " Please reselect multiple files..."
            ShowOpen = ShowOpen(Me_hWnd)
            tOFN.lpstrTitle = s
        End If
    End If
ErrOut:
End Function

'-ShowSave---------------------------------------------
' Returns 0 if user cancels, 1 for successful file name selection.
' On return the FileName property specifies the user requested file name.
' The Read Only FileNames property can also retrieve the selected file name.
'------------------------------------------------------
Function ShowSave(Optional ByVal Me_hWnd As Long) As Long
  On Error GoTo ErrWith
    With tOFN
       .lStructSize = LenB(tOFN)
       .hWndOwner = Me_hWnd
        If LenB(.lpstrTitle) = 0& Then .lpstrTitle = " Save As..."
       .lpstrFile = Validate(.lpstrFile) & String$(MAX_PATH, vbNullChar)
       .nMaxFile = Len(.lpstrFile)
        If StrLenW(StrPtr(.lpstrFilter)) = 0& Then
           Filter = " Text Documents     |*.txt;*.log;*.ini;*.diz;*.lst;*.dat;*.inf;*.reg|" & _
                    " Rich Text Format   |*.rtf;*.wri;*.doc;*.hpj;*.cnt;*.hm;*.hhm|" & _
                    " Web Based Files    |*.htm*;*.shtml;*.css;*.vbs;*.jav*;*.js;*.cod|" & _
                    " Initialization     |*.bat;*.ini;*.cfg;*.sys;*.mak;*.inf;*.reg|" & _
                    " Visual Basic Files |*.vbp;*.bas;*.ctl;*.frm;*.cls;*.dsr;*.pag;*.dob|" & _
                    " Source Code Files  |*.pas;*.for;*.pl;*.c;*.h;*.cpp;*.rc;*.asm;*.inc;*.mac;*.js|" & _
                    " Graphic Image Files|*.bmp;*.gif;*.jp*;*.jfif;*.png;*.ico;*.cur;*.ani|" & _
                    " All  File Types    |*.*|"
        End If
        If LenB(.lpstrDefExt) = 0& Then .lpstrDefExt = vbNullChar '!
        If .nFilterIndex = 0& Then .nFilterIndex = 1&
        If .lFlags = 0& Then .lFlags = OFN_OVERWRITEPROMPT Or OFN_PATHMUSTEXIST
       .lFlags = .lFlags Or OFN_EXPLORER Or OFN_ENABLEHOOK
       .lpfnHook = GetAddress(AddressOf OFNHookProc)
ErrWith:
    End With
  If Err Then GoTo ErrOut
  On Error GoTo ErrOut
    ShowSave = GetSaveFileName(VarPtr(tOFN))
    If ShowSave <> 0& Then
        m_sPrevDir = CurDir$
    Else
        tOFN.lpstrFile = vbNullString
    End If
ErrOut:
End Function

'-FileName---------------------------------------------
' Property Let: Sets the suggested file name.
'------------------------------------------------------
Public Property Let FileName(sFileName As String)
    tOFN.lpstrFile = sFileName
End Property

'-FileName---------------------------------------------
' Property Get: Returns user selected file name(s).
'------------------------------------------------------
Public Property Get FileName() As String
    FileName = TrimZZ(tOFN.lpstrFile)
End Property

'-FileNames--------------------------------------------
' The Read Only FileNames property retrieves the selected
' file name(s) by populating the string array parameter.
' FileNames returns the total count of the file names.
' The path occupies element Zero, and the file name(s)
' occupy elements 1,2,3, etc. Returns file count/UB(sA)
' (actual number of array items is the return count+1).
' FileNames splits a single file into path and filename.
'------------------------------------------------------
Public Property Get FileNames(sA() As String) As Long
   On Error GoTo ErrHandler
    Dim cc As Long, c As Long
    Dim i As Long, j As Long
    Dim iA() As Integer
    cc = ParseFileNames(tOFN.lpstrFile, iA)
    If cc = 0& Then Erase sA: Exit Property
    ReDim sA(cc) As String
    i = 1&
    Do: j = iA(c)
        sA(c) = Mid$(tOFN.lpstrFile, i, j)
        i = i + j + 1&
        c = c + 1&
    Loop Until c > cc
    FileNames = cc
ErrHandler:
End Property

'-DialogTitle------------------------------------------
' Read/Write Property: Specifies the File Dialog Title.
'------------------------------------------------------
Public Property Let DialogTitle(sDialogTitle As String)
    tOFN.lpstrTitle = sDialogTitle
End Property

Public Property Get DialogTitle() As String
    DialogTitle = tOFN.lpstrTitle
End Property

'-InitialDir-------------------------------------------
' Read/Write Property: Specifies the initial directory.
'------------------------------------------------------
Public Property Let InitialDir(sInitDir As String)
    tOFN.lpstrInitialDir = sInitDir
End Property

Public Property Get InitialDir() As String
    InitialDir = tOFN.lpstrInitialDir
End Property

'-PreviousDir------------------------------------------
' Read Only Property: Identifies the last accessed directory.
'------------------------------------------------------
Public Property Get PreviousDir() As String
    PreviousDir = m_sPrevDir
End Property

'-Filter-----------------------------------------------
' Read and Write Property: Specifies file types to select.
' To specify multiple filter patterns for a single display string,
' use a semicolon to separate the patterns. No spaces in the pattern!
' Filter = "Text Files (*.txt *.doc)|*.txt;*.doc|All Files (*.*)|*.*"
'                                   '...........!               '...!
' This property can be omitted/cleared to use the Default filters:
' Filter = vbNullString ' Use the default filters, see FilterIndex
'------------------------------------------------------
Public Property Let Filter(sFilter As String)
    Dim i As Long
    Dim s As String
    s = TrimZZ(sFilter)
    i = InStr(s, "|") ' Allows | or Z passed in
    Do While i
        MidI(s, i) = 0 'Mid$(s, i) = vbNullChar
        i = InStr(i + 1&, s, "|")
    Loop
    tOFN.lpstrFilter = s & vbNullChar
End Property

Public Property Get Filter() As String
    Dim i As Integer
    Dim s As String
    s = TrimZZ(tOFN.lpstrFilter)
    i = InStr(s, vbNullChar)
    Do While i
        MidI(s, i) = 124 'Mid$(s, i) = "|"
        i = InStr(i + 1&, s, vbNullChar)
    Loop
    Filter = s
End Property

'-FilterIndex------------------------------------------
' Read and Write Property: Specifies default file types.
' Specifies the index of the currently selected filter
' in the File Types control. The first pair of Filter
' property strings has an index of 1, the second 2, etc.
' You can specify an index on input to indicate the
' initial file types to display in the dialog box.
' When the user has selected a file, this property
' identifies the index of the current filter.
' Additionally, you can use the Default filters by
' omitting/clearing the Filter property and still
' specify the index of the initially selected filter.
'------------------------------------------------------
'     FilterIndex = 1   Text Documents
'     FilterIndex = 2   Rich Text Format
'     FilterIndex = 3   Web Based Files
'     FilterIndex = 4   Initialization Files
'     FilterIndex = 5   Visual Basic Files
'     FilterIndex = 6   Source Code Files
'     FilterIndex = 7   Graphic Image Files
'     FilterIndex = 8   All File Types
'------------------------------------------------------
Public Property Let FilterIndex(ByVal nIndex As Long)
    tOFN.nFilterIndex = nIndex
End Property

Public Property Get FilterIndex() As Long
    FilterIndex = tOFN.nFilterIndex
End Property

'-DefaultExt-------------------------------------------
' Read/Write Property: ShowOpen and ShowSave append this
' extension to the filename if the user fails to type
' an extension. This string can be any length, but only
' the first three characters are appended.
'------------------------------------------------------
Public Property Let DefaultExt(sDefaultExt As String)
    Dim i As Long
    i = InStrRev(sDefaultExt, ".")
    tOFN.lpstrDefExt = Mid$(sDefaultExt, i + 1&)
End Property

Public Property Get DefaultExt() As String
    DefaultExt = tOFN.lpstrDefExt
End Property

'-Flags------------------------------------------------
Public Property Let Flags(ByVal eFlags As OFN_FLAGS)
    tOFN.lFlags = eFlags
End Property

Public Property Get Flags() As OFN_FLAGS
    Flags = tOFN.lFlags
End Property

'------------------------------------------------------

Private Function Validate(sFileName As String) As String
    Dim i As Long, j As Long, ub As Long, s As String
    Const sInvalid As String = "/:*?<>|"
    j = StrLenW(StrPtr(sFileName))
    If j = 0& Then Exit Function
    i = InStrRev(sFileName, "\", j)    ' If preceeding path
    s = Mid$(sFileName, i + 1&, j - i) ' Keep file name only
    ub = Len(s)
    For i = 1& To ub
        ' If char is not valid for a file name then exit for
        If InStr(1, sInvalid, Mid$(s, i, 1)) Then Exit For
    Next
    If i > ub Then Validate = s ' All valid characters
End Function

Private Function GetAddress(ByVal AddrOf_Func As Long) As Long
    GetAddress = AddrOf_Func
End Function

Private Function OFNHookProc(ByVal hWndDlg As Long, ByVal uiMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    If uiMsg = WM_INITDIALOG Then CentreDialog GetParent(hWndDlg)
End Function

Private Sub CentreDialog(ByVal hWndDlg As Long)
    ' Centre the dialog parent window
    On Error GoTo ErrH
    Dim Wndpl As WINDOWPLACEMENT
    Dim w As Long, h As Long

    ' First get target window's properties
    Wndpl.Length = LenB(Wndpl)

    If (GetWindowPlacement(hWndDlg, Wndpl) <> 0&) Then
        ' Set the window's new position
        w = Wndpl.rcNormalPosition.Right - Wndpl.rcNormalPosition.Left
        h = Wndpl.rcNormalPosition.Bottom - Wndpl.rcNormalPosition.Top

        Wndpl.rcNormalPosition.Left = ((Screen.Width / Screen.TwipsPerPixelX) - w) \ 2&
        Wndpl.rcNormalPosition.Top = ((Screen.Height / Screen.TwipsPerPixelY) - h) \ 2&

        SetWindowPos hWndDlg, HWND_TOP, Wndpl.rcNormalPosition.Left, _
                                        Wndpl.rcNormalPosition.Top, _
                                        w, h, SWP_SHOWWINDOW
    End If
ErrH:
End Sub

'------------------------------------------------------
' ParseFileNames scans the Multi-string parameter and
' populates the integer array parameter with the string
' lengths. It returns the total count of the file names.
' The path length occupies element 0, and the file lengths
' occupy elements 1,2,3, etc. Returns total file count.
' ParseFileNames splits single file into path and filename.
'------------------------------------------------------
Private Function ParseFileNames(StrZs As String, iA() As Integer) As Long
  On Error GoTo ErrHandler
   Dim cc As Long, c As Long, lp As Long
   Dim i As Long, j As Long
   ReDim iA(50) As Integer
   cc = Len(StrZs): lp = StrPtr(StrZs)
   j = StrLenW(lp)
   If j <> 0& Then
      c = -1&
      Do: If c = 50& Then ReDim Preserve iA(500) As Integer
         c = c + 1&: iA(c) = j
         i = i + j + 1&
         j = StrLenW(lp + i + i) 'Rd
      Loop Until j = 0& Or (j > cc - i)
      If c = 0& Then
         j = iA(c)
         i = InStrRev(StrZs, "\", j)
         iA(c) = i - 1&: c = 1&
         iA(c) = j - i ' Single file
      End If
   End If
   ParseFileNames = c
ErrHandler:
End Function

'------------------------------------------------------
' TrimZZ trims Multi-strings. It expects a NULL padded
' buffer receiving the character string; this ensures
' correct handling of Single-NULL terminated BStr's.
'------------------------------------------------------
Private Function TrimZZ(StrZZ As String) As String
   Dim cc As Long, lp As Long, i As Long, j As Long
   cc = Len(StrZZ): lp = StrPtr(StrZZ) + 2&
   i = StrLenW(lp - 2&): j = StrLenW(lp + i + i)
   Do While j And (j < cc - i)
      i = i + j + 1&: j = StrLenW(lp + i + i) 'Rd
   Loop
   TrimZZ = LeftB$(StrZZ, i + i)
End Function

'------------------------------------------------------
' Twice as fast as AscW and Mid$ when compiled.
'        iChr = AscW(Mid$(sStr, lPos, 1))
'        iChr = MidI(sStr, lPos)
'------------------------------------------------------
Private Property Get MidI(sStr As String, ByVal lPos As Long) As Integer
    CopyMemory MidI, ByVal StrPtr(sStr) + lPos + lPos - 2&, 2&
End Property

'------------------------------------------------------
'        Mid$(sStr, lPos, 1) = Chr$(iChr)
'        MidI(sStr, lPos) = iChr
'------------------------------------------------------
Private Property Let MidI(sStr As String, ByVal lPos As Long, ByVal iChr As Integer)
    CopyMemory ByVal StrPtr(sStr) + lPos + lPos - 2&, iChr, 2&
End Property

'------------------------------------------------------

'    FileDialog.FileName = vbNullString
'    FileDialog.DialogTitle = "Open file(s)..."
'    FileDialog.InitialDir = sCurPath
'    FileDialog.Filter = "Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
'    FileDialog.FilterIndex = 1&
'    ' File must exist, allow multi-select, use Win95 style dialog, hide read-only checkbox
'    FileDialog.Flags = OFN_FILEMUSTEXIST Or OFN_ALLOWMULTISELECT Or OFN_EXPLORER Or OFN_HIDEREADONLY
'    rc = FileDialog.ShowOpen([Me.hWnd])
'    sFileName = FileDialog.FileName
'
'    If rc = 0 Then
'        ' User didn't return a file name
'        ' sFileName == vbNullString
'    Else
'        sCurPath = FileDialog.PreviousDir
'        If rc = 1 Then
'            ' User chose a single file name
'            OpenFile (sFileName)
'        Else ' rc == -1
'            ' User chose multiple file names
'            ' sFileName == MultiString
'            Cnt = FileDialog.FileNames(aStr())
'            sPath = aStr(0) & "\"
'            For idx = 1 To Cnt
'                OpenFile (sPath & aStr(idx))
'            Next idx
'        End If
'    End If
'
'    FileDialog.FileName = sSuggestedName
'    FileDialog.DialogTitle = "Save file as..."
'    FileDialog.InitialDir = sCurPath
'    ' Overwrite? prompt, requires valid path
'    FileDialog.Flags = OFN_OVERWRITEPROMPT Or OFN_PATHMUSTEXIST
'    rc = FileDialog.ShowSave([Me.hWnd])
'    sFileName = FileDialog.FileName
'
'    If rc = 0 Then
'        ' User didn't return a file name
'        ' sFileName == vbNullString
'    Else ' rc == 1
'        ' User chose a file name
'        sCurPath = FileDialog.PreviousDir
'        SaveFile (sFileName)
'    End If
'
'------------------------------------------------------

'                                                   :›)

