Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

PBDLL: OpenFileDialog with Multiple File Selection

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • PBDLL: OpenFileDialog with Multiple File Selection

    Here is a version of the OpenFileDialog function that has been slightly modified, to allow the selection of multiple files. It does not use the old-style (16-bit) file-selection dialog (which is the default when the multi-file option is used) and it does allow the use of long file names.

    The new function is called OpenFilesDialog (Files not File), to avoid conflicting with the original function. (This function uses equates and TYPE structures that are found in the COMDLG32.INC file, which contains the original function.)

    The output string that is returned by the third parameter of this function is delimited by CHR$(0). The first segment of the string is the drive/path where all of the selected files can be found, and it is followed by segments that contain individual file names (without paths).

    For complete information about the various options that this function supports, see the GetOpenFileName entry in the Microsoft WIN32.HLP file.

    Code:
    FUNCTION OpenFilesDialog(BYVAL hWnd AS LONG, _                ' parent window
                             BYVAL Caption AS STRING, _           ' caption
                             Filespec AS STRING, _                ' filename (input/output)
                             BYVAL InitialDir AS STRING, _        ' start directory
                             BYVAL Filter AS STRING, _            ' filename filter
                             BYVAL DefExtension AS STRING, _      ' default extension
                             Flags AS LONG) AS LONG               ' flags
     
      LOCAL sFile         AS STRING
     
      LOCAL Ofn           AS OPENFILENAME
      LOCAL zFileTitle    AS ASCIIZ * 256
      LOCAL zFilter       AS ASCIIZ * 256
      LOCAL zInitialDir   AS ASCIIZ * 256
      LOCAL zTitle        AS ASCIIZ * 256
      LOCAL zDefExt       AS ASCIIZ * 10
     
      REPLACE "|" WITH CHR$(0) IN Filter
     
      IF LEN(InitialDir) = 0 THEN
        InitialDir = CURDIR$
      END IF
     
      zFilter     = Filter + CHR$(0)
      zInitialDir = InitialDir + CHR$(0)
       
      sFile = STRING$(4096,0)  'PLENTY OF ROOM FOR MULTIPLE FILES
      MID$(sFile,1) = FileSpec
     
      zDefExt     = DefExtension + CHR$(0)
      zTitle      = Caption + CHR$(0)
     
      ofn.lStructSize       = SIZEOF(ofn)
      ofn.hWndOwner         = hWnd
      ofn.lpstrFilter       = VARPTR(zFilter)
      ofn.nFilterIndex      = 1
      ofn.lpstrFile         = STRPTR(sFile)
      ofn.nMaxFile          = LEN(sFile)
      ofn.lpstrFileTitle    = VARPTR(zFileTitle)
      ofn.nMaxFileTitle     = SIZEOF(zFileTitle)
      ofn.lpstrInitialDir   = VARPTR(zInitialDir)
      IF LEN(zTitle) THEN
        ofn.lpstrTitle        = VARPTR(zTitle)
      END IF
      ofn.Flags             = Flags
      ofn.lpstrDefExt       = VARPTR(zDefExt)
     
      FUNCTION = GetOpenFilename(ofn)
     
      Filespec = RTRIM$(sFile,CHR$(0))
      Flags    = ofn.Flags
     
    END FUNCTION
    And here is a very simple program to demonstrate how this function can be used...

    Code:
    $COMPILE EXE
    $DIM ALL
    $REGISTER NONE
    #INCLUDE "C:\PBDLL60\WINAPI\WIN32API.INC"
    #INCLUDE "C:\PBDLL60\WINAPI\COMDLG32.INC"
     
    (paste the OpenFilesDialog function here)
     
    FUNCTION PBMain AS LONG
     
            DIM sFileName AS LOCAL STRING
            DIM sPath     AS LOCAL STRING
            DIM lStyle    AS LOCAL LONG
     
            sFileName = "*.*"
            sPath  = CURDIR$
            lStyle = %OFN_ALLOWMULTISELECT OR %OFN_EXPLORER OR _
                     %OFN_FILEMUSTEXIST OR %OFN_NODEREFERENCELINKS
     
            IF OpenFilesDialog(0, _
                              "Select multiple files by holding down CTRL and clicking...", _
                              sFileName, _
                              sPath, _
                              "All Files (*.*)|*.*|Text files  (*.TXT, *.BAT)|*.TXT;*.BAT|", _
                              "", _
                              lStyle) THEN
     
    			'make the string easy to display...
                            REPLACE CHR$(0) WITH CHR$(13) IN sFileName
                            MSGBOX "Directory = " + sFileName, _
                                   %MB_OK, _
                                   FORMAT$(PARSECOUNT(sFileName,CHR$(13))-1)+" files selected..."
     
    	ELSE
    		MSGBOX "No file(s) selected.",%MB_OK,"Error"
            END IF
     
    END FUNCTION


    -------------
    Perfect Sync: Perfect Sync Development Tools
    Email: mailto:[email protected][email protected]</A>

    "Not my circus, not my monkeys."

  • #2
    One caveat.
    If Microsoft did not lift the 2048 byte limit on the ofn.lpstrFile member of the
    OPENFILENAME structure, setting sFile to 4096 bytes is a waste of memory. To
    get around this limit you have to use a hook procedure and read the entire string
    from the edit control. However, that string will not be NULL delimited but formatted
    as follows-
    User selects a single file: VisualPB.h\0
    User selects multiple files: "VisualPB.bas" "VPBCtrls.h" "GUIDDlgs.inc" \0
    Note, you have to get the directory using the Windows API or PowerBasic's CURDIR$.


    ------------------
    Dominic Mitchell
    Phoenix Visual Designer
    http://www.phnxthunder.com

    Comment


    • #3
      Dominic --

      I'm not seeing the problem that you're describing. As a stress-test I increased the target-string length from 4096 to 32000 bytes and tagged all of the files in my NT machine's System32 directory... a string that was almost 18k was returned. No problems.

      I am able to select huge numbers of files on Windows 95b, 98, and NT4 using that function, and easily get lists of file names in the 10k+ range.

      Am I missing something?

      -- Eric


      ------------------
      Perfect Sync: Perfect Sync Development Tools
      Email: mailto:[email protected][email protected]</A>



      [This message has been edited by Eric Pearson (edited February 11, 2000).]
      "Not my circus, not my monkeys."

      Comment


      • #4
        There were a couple of nasty bugs in COMMDLG.DLL of version 3.5 of the
        win32 SDK. I guess MS fixed some of them.

        ------------------
        Dominic Mitchell
        Phoenix Visual Designer
        http://www.phnxthunder.com

        Comment


        • #5
          Ok, thanks for the heads-up. Attention to detail is what this business is all about!

          -- Eric


          ------------------
          Perfect Sync: Perfect Sync Development Tools
          Email: mailto:[email protected][email protected]</A>

          "Not my circus, not my monkeys."

          Comment


          • #6
            This is similar to Eric's and the differences are 1) If you
            select just one file, if is not seperated from the path easily
            because with long filenames, spaces are all over the place. That
            is fixed in this.

            The OpenFileName item is now a INClude file and named OpenFilesDialogto not interfer with the original one.

            First, here is OpenFilesDialog.Inc:

            Code:
            ' Name of file: OpenFilesDialog.Inc
            ' Purpose: Correct return of long file names and one or more files
            ' original Author: Eric Pearson
            ' Modified by: Barry Erick [email protected]
            ' Modifications: for stand-alone include and name change
            ' How to use: Place this in your Include or Win32Inc folder named as OpenFilesDialog.
            '   note the name change. The original is OPENFILEDIALOG and this is OPENFILESDIALOG with an 'S'
            
            #If Not %Def(%OpenFilesDialog_Inc)
            
            %OpenFilesDialog_Inc = 1
            
            '
            ' Pull in Windows includes if necessary
            '
            #If Not %Def(%ComDlg32_Inc)
              #Include "comDlg32.inc"
            #EndIf
            
             'following is modifed for multiple long filenames on PB Forums and is very similar to OpenFileDialog
            Function OpenFilesDialog (ByVal hWnd As Long, _           ' parent window  'was dword
                                     ByVal Caption As String, _             ' caption  'byval is new
                                     Filespec As String, _            ' filename
                                     ByVal InitialDir As String, _          ' start directory 'byval is new
                                     ByVal Filter As String, _              ' filename filter 'byval is new
                                     ByVal DefExtension As String, _        ' default extension    'byval is new
                                     Flags As Long _                 ' flags 'was dword
                                    ) As Long
            
                Local sfile As String       'added
            
                Local Ofn           As OPENFILENAME
                Local zFileTitle    As Asciiz * 256
                Local zFilter       As Asciiz * 256
                Local zInitialDir   As Asciiz * 256
                Local zTitle        As Asciiz * 256
                Local zDefExt       As Asciiz * 10
            
                Replace "|" With Chr$(0) In Filter
            
                If Len(InitialDir) = 0 Then
                    InitialDir = CurDir$
                End If
            
                zFilter     = Filter + Chr$(0)
                zInitialDir = InitialDir + Chr$(0)
                sFile = String$(32000,0) 'plenty of room for multiple files... can even be more
                Mid$(sFile,1) = FileSpec    'added
                zDefExt     = DefExtension + Chr$(0)
                zTitle      = Caption + Chr$(0)
            
                ofn.lStructSize       = SizeOf(ofn)
                ofn.hWndOwner         = hWnd
                ofn.lpstrFilter       = VarPtr(zFilter)
                ofn.nFilterIndex      = 1
                ofn.lpstrFile         = StrPtr(sFile) 'was varptr
                ofn.nMaxFile          = Len(sFile) 'was SIZEOF(zFile)
                ofn.lpstrFileTitle    = VarPtr(zFileTitle)
                ofn.nMaxFileTitle     = SizeOf(zFileTitle)
                ofn.lpstrInitialDir   = VarPtr(zInitialDir)
                If Len(zTitle) Then
                    ofn.lpstrTitle    = VarPtr(zTitle)
                End If
                ofn.Flags             = Flags
                ofn.lpstrDefExt       = VarPtr(zDefExt)
            
                Function = GetOpenFilename(ofn)
            
                Filespec = RTrim$(sFile, Chr$(0))
                Flags    = ofn.Flags
            
            End Function
            #EndIf
            Here is the example code that takes care of this for one or more
            files. Also, if you select more than 38, it used a dialog listbox
            instead of a message box to display the selections.

            Code:
             The original change posted here will not seperate the path from the file if only one file is found
            '   and you have %OFN_ALLOWMULTISELECT OR %OFN_EXPLORER set. Microsoft changes the delimiters with
            '   Explorer to a single null character between the files, but a space between the path and the
            '   first file if only one file is fouund. With Long File Names (which the Explorer defaults to),
            '   spaces can be in the path and filename. The only way to seperate the path from the file is
            '   to check to see what portion is a valid path. Luckily Windows changes the Current Directory
            '   to the path you picked the file from. With this we can parse the directory name and get
            '   one and only one file is selected.
            #Compile Exe
            #Dim All
            '#Register None
            #Include "Win32api.inc"
            #Include "Comdlg32.inc"
            #Include "OpenFilesDialog.Inc"
            Global hdlg As Long
            Function SepFileAndPath(s As String) As String
                 ' finds the path delimited with a space from the one file carried
                 ' Note: OpemFile always changes to the current directory that the selected files are in.
                 Dim temp As String
                 If Tally(s,CurDir$) = 1 Then
                     ' We should always get here
                     If Tally(CurDir$,"\") = 1 Then ' we are at root
                        temp = Mid$(s,1,Len(CurDir$))+Chr$(13)
                     Else
                        temp = Mid$(s,1,Len(CurDir$)+1)+Chr$(13) 'not at root
                     End If
                     If Mid$(s,Len(CurDir$)+1,1) = "\" Then   'fix certain conditions.
                         temp = temp + Mid$(s,Len(CurDir$)+2)
                     Else
                        temp = temp + LTrim$(Mid$(s,Len(CurDir$)+1))
                     End If
                     Function = temp
                     Exit Function
                 Else
                     'we have no idea because open always changes the current directory to the one the file is found in.
                     MsgBox "Huh?? There ain't no path"
                     Function = s
                 End If
            End Function
            
            CallBack Function OkBtn()
             Dialog End hdlg
            End Function
            
            
            Sub ShowAsDialog (s As String)
                Dim i As Long
                Dim ip As Long
                ip = ParseCount(s,Chr$(13))
                Dim st(ip) As String
            
                st(0) = "Directory: " & Parse$(s,Chr$(13),1)
                st(1) = "Files:"
                For i = 2 To ip
                    st(i)=Parse$(s,Chr$(13),i)
                Next
                Dialog New 0,  Format$(ip-1)+" file(s) selected", _
                             ,, 280,200  ,, To hdlg
                Control Add ListBox, hdlg, 101, st(), 10,10,260,170, %WS_VSCROLL Or %WS_HSCROLL
                Control Send hdlg,101, %LB_SethorizontalExtent, 500,0
                Control Add Button, hdlg,102,"OK", 120,180,40,14 Call OKBtn
                Beep ' well, MsgBox beeps
                Dialog Show Modal hdlg
            End Sub
            
            
            Function PbMain()
                Dim sFileName As Local String
                Dim sPath As Local String
                Dim lStyle As Local Long
            
                sFilename = "*.*"
                sPath = CurDir$
                lStyle = %OFN_ALLOWMULTISELECT Or %OFN_EXPLORER Or _
                            %OFN_FILEMUSTEXIST Or %OFN_NODEREFERENCELINKS
            
                If OpenFilesDialog(0, _
                    "Select Multiple files by holding down CTRL and clicking...", _
                        sFileName, _
                        sPath, _
                        "All Files (*.*)|*.*|Text Files (*.TXT, *.BAT)|*.TXT;*.BAT|", _
                        "", _
                        lStyle) Then
                    ' make the string easy to display
                    'We can't change back to our directory here as we need it if only one file is returned.
                    Replace Chr$(0) With Chr$(13) In sFileName
                    ' If we have only one file, seperate it from the rest...
                    ' (following this operation the directory can be changed.)
                    If ParseCount(sFileName,Chr$(13))=1 Then
                        sFileName = SepFileAndPath(sFileName)
                    End If
                    If ParseCount(sFilename,Chr$(13))-1 > 38 Then  'msgbox can't show nicely too many
                        showAsDialog sFileName
                        Else
                    MsgBox "Directory = " + sfilename, _
                        %MB_OK, _
                        Format$(ParseCount(sFileName,Chr$(13))-1)+" file(s) selected"
                    End If
                Else
                    MsgBox "No file(s) selected.",%MB_OK,"Error"
                End If
                'now, if you want to get back to the original directory, do it here:
                ChDir spath
            End Function



            ------------------
            Barry

            Comment


            • #7
              Here's my version, works fine...
              Code:
              Declare Function MyOpenFileDialog(ByVal hWnd As Long, _                ' parent window
                                      ByVal Caption As String, _           ' caption
                                      Filespec As String, _                ' filename
                                      InitialDir As String, _        ' start directory
                                      ByVal Filter As String, _            ' filename filter
                                      ByVal DefExtension As String, _      ' default extension
                                      Flags As Long) As Long               ' flags
              
              'This is case %IDOPEN or something similiar, some extraneous code is for menu's etc
              
              
                                   lStyle = %OFN_FILEMUSTEXIST Or %OFN_HIDEREADONLY Or %OFN_LONGNAMES Or %OFN_EXPLORER Or %OFN_ALLOWMULTISELECT
                                      g_Result = MyOpenFileDialog(ByVal hDLg,_
                                                                  ByVal Caption,_
                                                                  FileSpec,_
                                                                  CurrentDir,_
                                                                  ByVal Filter,_
                                                                  ByVal DefExtension,_
                                                                  lStyle)
              
              
                                      lResult = Tally(FileSpec,Chr$(0))
                                      If lResult <> 0 Then  'Parse out the multiple files
                                         CurrentDir = Parse$(FileSpec,Chr$(0),1)
                                         FileSpec = Right$(FileSpec,Len(FileSpec) - Instr(FileSpec,Chr$(0))) 'REMOVE the directory
                                         'Now FileSpec contains NULL separated files, pull those out
                                         For lLoop = 1 To lResult
                                             sTmp = CurrentDir & "\" & Parse$(FileSpec,Chr$(0),lLoop)
                                             Array Scan g_Files(), Collate Ucase, = sTmp, To DuplicateCheck
              
                                             If DuplicateCheck Then
                                                g_Result = MessageBox(CbHndl, "The file " & g_Files(DuplicateCheck) & " is already added",ByVal StrPtr(g_szCCS),ByVal %MB_ICONINFORMATION)
                                             Else
                                                Incr g_FileIndex
                                                If g_FileIndex > %MAX_FILES Then
                                                   g_Result = MessageBox(CbHndl, LimitMsg,ByVal StrPtr(g_szCCS),ByVal %MB_ICONSTOP)
                                                   Exit Function
                                                End If
                                                g_Files(g_FileIndex) = sTmp
                                                ListBox Add CbHndl,%IDLISTBOX1,g_Files(lLoop)
                                                g_Filesize(lLoop) = FileSize(g_Files(lLoop)) 'Grab file size here
                                             End If
                                         Next
              
              '                        If IsFalse g_FileIndex Then g_FileIndex = 1 'Prevent GPF if at zero
                                      Else   'Add the single file but First make sure it's not in the list already
                                         CurrentDir = CurDir$
                                         If IsFalse Len(FileSpec) Then Exit Function 'User pressed escape or hit cancel
                                         Incr g_FileIndex
                                          If g_FileIndex > %MAX_FILES Then
                                                   g_Result = MessageBox(CbHndl, LimitMsg,ByVal StrPtr(g_szCCS),ByVal %MB_ICONSTOP)
                                             Exit Function
                                          End If
              
                                          sTmp = Remove$(FileSpec,Chr$(0))
                                          Array Scan g_Files(), Collate Ucase, = sTmp, To DuplicateCheck
                                          If DuplicateCheck Then
                                              g_Result = MessageBox(CbHndl, "The file " & g_Files(DuplicateCheck) & " is already added",ByVal StrPtr(g_szCCS),ByVal %MB_ICONINFORMATION)
                                              Exit Function
                                          End If
                                          g_Files(g_FileIndex) = FileSpec
                                          g_Filesize(g_FileIndex) = FileSize(g_Files(g_FileIndex)) 'Grab file size here
                                          ListBox Add hDlg, %IDLISTBOX1, g_Files(g_FileIndex)
                                      End If
                                      If g_FileIndex > 0 Then
                                         Control Enable CbHndl, %IDCOMPILE
                                         g_Result = EnableMenuItem(g_hMenu,%IDCOMPILE,%MF_ENABLED)
                                      End If
              
                                      If g_FileIndex = %MAX_FILES Then Control Disable hDlg,%IDADDFILES
              
                               End If
              ------------------
              Scott
              Scott Turchin
              MCSE, MCP+I
              http://www.tngbbs.com
              ----------------------
              True Karate-do is this: that in daily life, one's mind and body be trained and developed in a spirit of humility; and that in critical times, one be devoted utterly to the cause of justice. -Gichin Funakoshi

              Comment

              Working...
              X