Jump to content

Recommended Posts

Posted (edited)

Hi,

FileOpenDialog failed to work if number of selected files is bigger than 450 (approximately).

Is there a workaround ?

Thanks

Edited by AutoFan
Posted
1 hour ago, AutoFan said:

FileOpenDialog failed to work if number of selected files is bigger than 450 (approximately).

It seems, that the length of the result string from FileOpenDialog is limited to 65,535 characters (16-bit unsigned integer / word). The number of selected files does not matter.

Example -> select 4677 files (StringLen = 65.523) ==> OK ; select 4678 files ==> not OK

#include <MsgBoxConstants.au3>

; just to create xxx testfiles (run once) :
Local $hFileOpen, $sFileSuffix
For $i = 1 To 10000
    $sFileSuffix = StringFormat("%05i", $i)
    $hFileOpen = FileOpen(@ScriptDir & "\testfiles\file" & $sFileSuffix & ".txt", 10)
    FileWrite($hFileOpen, "File " & $sFileSuffix)
    FileClose($hFileOpen)
Next

Example() ; from AutoIt-Help (modified)

Func Example()
    Local Const $sMessage = "Hold down Ctrl or Shift to choose multiple files."
    Local $iError
    ; Display an open dialog to select a list of file(s).
    Local $sFileOpenDialog = FileOpenDialog($sMessage, @ScriptDir & "\testfiles", "All (*.*)", 15)
    $iError = @error
    ConsoleWrite("! Error = " & $iError & "  StringLen = " & StringLen($sFileOpenDialog) & @CRLF)
    If $iError Then
        ; Display the error message.
        MsgBox($MB_SYSTEMMODAL, "", "No file(s) were selected.")
        ; Change the working directory (@WorkingDir) back to the location of the script directory as FileOpenDialog sets it to the last accessed folder.
        FileChangeDir(@ScriptDir)
    Else
        ; Change the working directory (@WorkingDir) back to the location of the script directory as FileOpenDialog sets it to the last accessed folder.
        FileChangeDir(@ScriptDir)
        ; Replace instances of "|" with @CRLF in the string returned by FileOpenDialog.
        $sFileOpenDialog = StringReplace($sFileOpenDialog, "|", @CRLF)
        ; Display the list of selected files.
        ConsoleWrite("You chose the following files:" & @CRLF & $sFileOpenDialog)
    EndIf
EndFunc   ;==>Example

 

Musashi-C64.png

"In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move."

Posted (edited)

Thanks for pointing out.

21 hours ago, Musashi said:
21 hours ago, Musashi said:

It seems, that the length of the result string from FileOpenDialog is limited to 65,535 characters (16-bit unsigned integer / word). The number of selected files does not matter.

Example -> select 4677 files (StringLen = 65.523) ==> OK ; select 4678 files ==> not OK

#include <MsgBoxConstants.au3>

; just to create xxx testfiles (run once) :
Local $hFileOpen, $sFileSuffix
For $i = 1 To 10000
    $sFileSuffix = StringFormat("%05i", $i)
    $hFileOpen = FileOpen(@ScriptDir & "\testfiles\file" & $sFileSuffix & ".txt", 10)
    FileWrite($hFileOpen, "File " & $sFileSuffix)
    FileClose($hFileOpen)
Next

Example() ; from AutoIt-Help (modified)

Func Example()
    Local Const $sMessage = "Hold down Ctrl or Shift to choose multiple files."
    Local $iError
    ; Display an open dialog to select a list of file(s).
    Local $sFileOpenDialog = FileOpenDialog($sMessage, @ScriptDir & "\testfiles", "All (*.*)", 15)
    $iError = @error
    ConsoleWrite("! Error = " & $iError & "  StringLen = " & StringLen($sFileOpenDialog) & @CRLF)
    If $iError Then
        ; Display the error message.
        MsgBox($MB_SYSTEMMODAL, "", "No file(s) were selected.")
        ; Change the working directory (@WorkingDir) back to the location of the script directory as FileOpenDialog sets it to the last accessed folder.
        FileChangeDir(@ScriptDir)
    Else
        ; Change the working directory (@WorkingDir) back to the location of the script directory as FileOpenDialog sets it to the last accessed folder.
        FileChangeDir(@ScriptDir)
        ; Replace instances of "|" with @CRLF in the string returned by FileOpenDialog.
        $sFileOpenDialog = StringReplace($sFileOpenDialog, "|", @CRLF)
        ; Display the list of selected files.
        ConsoleWrite("You chose the following files:" & @CRLF & $sFileOpenDialog)
    EndIf
EndFunc   ;==>Example

 

 

Edited by AutoFan
Posted

Here's a customized version I use in SMF.

Return buffer size is defined in row containing Local $iFileBufferSize = 32768 * 10

#include <Array.au3>
#include <WindowsConstants.au3>
#include <WinAPIDlg.au3>
Global $f_CDN__FileDialogsEx_Start = 1

$sFile = _FileOpenDialog_Ex("SMF _FileOpenDialog_Ex", @ScriptDir, "All (*.*)", 1 + 2, "", 0, 1)

$aFiles = StringSplit($sFile, "|")
_ArrayDisplay($aFiles)


Func _FileOpenDialog_Ex($sTitle, $sInitDir, $sFilter, $iOptions = 0, $sDefaultName = "", $hWnd = 0, $iType = 0)
    #cs
        https://groups.google.com/forum/?hl=en&fromgroups=#!topic/microsoft.public.vc.mfc/HafQr4gIRY0
        The problem is that GetOpenFileName changes the current directory to the
        last browsed one. The current directory and any of its parents cannot be
        deleted.
    #ce

    If Not $sInitDir Then
        If Not FileExists("\\?\" & $sInitDir) Then $sInitDir = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" ; CLSID for "My Computer"
    EndIf

    Local $sWorkingDir = @WorkingDir
    If $iType = 1 Then
        $f_CDN__FileDialogsEx_Start = 1
        Local $sFilename = _FileOpenDialogEx($sTitle, $sInitDir, $sFilter, BitOR($OFN_ENABLESIZING, $OFN_ALLOWMULTISELECT), $sDefaultName, $hWnd)
    Else
        Local $sFilename = FileOpenDialog($sTitle, $sInitDir, $sFilter, $iOptions, $sDefaultName, $hWnd)
    EndIf
    Local $iError = @error
    FileChangeDir($sWorkingDir)

    Return SetError($iError, 0, $sFilename)
EndFunc   ;==>_FileOpenDialog_Ex

Func _FileOpenDialogEx($sTitle = "", $sInitDir = "", $sFilter = "All Files (*.*)", $iOptions = 0, $sDefaultName = "", $hParent = 0, $hTemplate = 0, $sTemplateName = "")
    Local $sRet = _GetOpenSaveFileName('GetOpenFileNameW', $hTemplate, $sTemplateName, $hParent, $sTitle, $sInitDir, $sFilter, $iOptions, $sDefaultName)
    If @error Then SetError(@error)
    Return $sRet
EndFunc   ;==>_FileOpenDialogEx

Func _GetOpenSaveFileName($sFunction, $hTemplate, $sTemplateName, $hParent, $sTitle, $sInitDir, $sFilter, $iOptions, $sDefaultName)
    Local $taFilters, $tFile, $_OFN_HookProc = 0, $iFlagsEx = 0, $iFlagsForced = BitOR($OFN_EXPLORER, $OFN_HIDEREADONLY, $OFN_NODEREFERENCELINKS)
    $iOptions = BitOR($iFlagsForced, $iOptions)
    If BitAND($iOptions, $OFN_EX_NOPLACESBAR) Then
        $iOptions = BitXOR($iOptions, $OFN_EX_NOPLACESBAR)
        $iFlagsEx = $OFN_EX_NOPLACESBAR
    EndIf
    Local $tagBuffer = "wchar[4096]", $iBufferSize = 4095
    Local $aFilters = StringSplit($sFilter, "|"), $saFilters = "", $tagFilters = "", $aFiltSplit, $i
    For $i = 1 To $aFilters[0]
        $aFiltSplit = StringRegExp($aFilters[$i], "(?U)\A\h*(.+)\h*\((.*)\)", 1)
        $saFilters &= $aFilters[$i] & Chr(0) & $aFiltSplit[1] & Chr(0)
    Next
    $tagFilters = "wchar[" & StringLen($saFilters) + 3 & "]"
    $taFilters = DllStructCreate($tagFilters)
    DllStructSetData($taFilters, 1, $saFilters)
    
    Local $iFileBufferSize = 32768 * 10
    
    Local $tagFileBuffer = "wchar[" & $iFileBufferSize & "]"
    $tFile = DllStructCreate($tagFileBuffer) ; Win2000/XP: should be 32k for ansi, unlimited for unicode
    If $sDefaultName <> "" Then DllStructSetData($tFile, 1, $sDefaultName)
    Local $tOFN = DllStructCreate('dword lStructSize;hwnd hwndOwner;hwnd hInstance;' & _
            'ptr lpstrFilter;ptr lpstrCustomFilter;dword nMaxCustFilter;dword nFilterIndex;' & _
            'ptr lpstrFile;dword nMaxFile;ptr lpstrFileTitle;dword nMaxFileTitle;ptr lpstrInitialDir;ptr lpstrTitle;' & _
            'dword Flags;short nFileOffset;short nFileExtension;ptr lpstrDefExt;dword lCustData;ptr lpfnHook;ptr lpTemplateName;' & _
            'dword Reserved[2];dword FlagsEx')

    DllStructSetData($tOFN, 'lStructSize', DllStructGetSize($tOFN))
    If IsHWnd($hParent) Then DllStructSetData($tOFN, 'hwndOwner', $hParent)
    DllStructSetData($tOFN, 'lpstrFilter', DllStructGetPtr($taFilters))
    DllStructSetData($tOFN, 'nFilterIndex', 1)
    DllStructSetData($tOFN, 'lpstrFile', DllStructGetPtr($tFile))
    DllStructSetData($tOFN, 'nMaxFile', $iFileBufferSize)
    DllStructSetData($tOFN, 'FlagsEx', $iFlagsEx)
    If $hTemplate <> 0 Then
        If $sTemplateName <> "" Then
            $iOptions = BitOR($iOptions, $OFN_ENABLETEMPLATE)
            DllStructSetData($tOFN, 'hInstance', $hTemplate)
            Local $tTemplateName = DllStructCreate($tagBuffer) ; 'char[256]')
            DllStructSetData($tTemplateName, 1, $sTemplateName)
            DllStructSetData($tOFN, 'lpTemplateName', DllStructGetPtr($tTemplateName))
        Else
            $iOptions = BitOR($iOptions, $OFN_ENABLETEMPLATEHANDLE)
            DllStructSetData($tOFN, 'hInstance', $hTemplate)
        EndIf
    EndIf

    $iOptions = BitOR($iOptions, $OFN_ENABLEHOOK, $OFN_ENABLEINCLUDENOTIFY)
    $_OFN_HookProc = DllCallbackRegister("_OFN_HookProc", "int", "hwnd;uint;wparam;lparam")
    DllStructSetData($tOFN, 'lpfnHook', DllCallbackGetPtr($_OFN_HookProc))

    If $sTitle <> "" Then
        Local $tTitle = DllStructCreate($tagBuffer)
        DllStructSetData($tTitle, 1, String($sTitle))
        DllStructSetData($tOFN, "lpstrTitle", DllStructGetPtr($tTitle))
    EndIf

    If $sInitDir <> "" Then
        Local $tInitDir = DllStructCreate($tagBuffer)
        DllStructSetData($tInitDir, 1, String($sInitDir))
        DllStructSetData($tOFN, "lpstrInitialDir", DllStructGetPtr($tInitDir))
    EndIf

    DllStructSetData($tOFN, 'Flags', $iOptions)
    Local $aRet = DllCall('comdlg32.dll', 'int', $sFunction, 'ptr', DllStructGetPtr($tOFN))
    Local $iError = @error
    If $_OFN_HookProc <> 0 Then DllCallbackFree($_OFN_HookProc)
    If $iError Then
        Return SetError(2, $iError, "")
    ElseIf $aRet[0] Then
        Local $iChar = 1
        While $iChar < $iFileBufferSize + 1
            If DllStructGetData($tFile, 1, $iChar) = "" Then
                If DllStructGetData($tFile, 1, $iChar + 1) = "" Then ExitLoop
                DllStructSetData($tFile, 1, "|", $iChar)
            EndIf
            $iChar += 1
        WEnd
        Return SetError(0, 0, DllStructGetData($tFile, 1))
    Else
        Return SetError(1, 0, "")
    EndIf
EndFunc   ;==>_GetOpenSaveFileName

Volatile Func _OFN_HookProc($hWnd, $Msg, $wParam, $lParam)
    Switch $Msg
        Case $WM_NOTIFY
            Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR
            $tNMHDR = DllStructCreate("hwnd hWndFrom;int idFrom;int code", $lParam)
            $hWndFrom = DllStructGetData($tNMHDR, "hWndFrom")
            $iIDFrom = DllStructGetData($tNMHDR, "idFrom")
            $iCode = DllStructGetData($tNMHDR, "code")
            Switch $iCode
                Case $CDN_FOLDERCHANGE

                    If $f_CDN__FileDialogsEx_Start Then ; if executing first time

                        Local $aRet = DllCall('user32.dll', 'hwnd', 'FindWindowEx', 'hwnd', $hWndFrom, 'hwnd', 0, 'str', "SHELLDLL_DefView", 'str', "")
                        If $aRet[0] = 0 Then
                            ConsoleWrite("_OFN_HookProc" & @TAB & "_FindWindowEx Error" & @CRLF)
                        Else
                            DllCall('user32.dll', 'int', 'SendMessage', 'hwnd', $aRet[0], 'uint', $WM_COMMAND, 'wparam', 0x702c, 'lparam', 0) ; $ODM_VIEW_DETAIL
                        EndIf

                        WinMove($hWndFrom, "", @DesktopWidth / 2 - 650 / 2, @DesktopHeight / 2 - 600 / 2, 650, 600)

                        $f_CDN__FileDialogsEx_Start = 0 ; to make sure these tweaks happens only once.

                    EndIf

            EndSwitch
        Case Else
    EndSwitch
    Return 0
EndFunc   ;==>_OFN_HookProc

 

Posted
13 hours ago, KaFu said:

Here's a customized version I use in SMF.

Return buffer size is defined in row containing Local $iFileBufferSize = 32768 * 10

#include <Array.au3>
#include <WindowsConstants.au3>
#include <WinAPIDlg.au3>
Global $f_CDN__FileDialogsEx_Start = 1

$sFile = _FileOpenDialog_Ex("SMF _FileOpenDialog_Ex", @ScriptDir, "All (*.*)", 1 + 2, "", 0, 1)

$aFiles = StringSplit($sFile, "|")
_ArrayDisplay($aFiles)


Func _FileOpenDialog_Ex($sTitle, $sInitDir, $sFilter, $iOptions = 0, $sDefaultName = "", $hWnd = 0, $iType = 0)
    #cs
        https://groups.google.com/forum/?hl=en&fromgroups=#!topic/microsoft.public.vc.mfc/HafQr4gIRY0
        The problem is that GetOpenFileName changes the current directory to the
        last browsed one. The current directory and any of its parents cannot be
        deleted.
    #ce

    If Not $sInitDir Then
        If Not FileExists("\\?\" & $sInitDir) Then $sInitDir = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" ; CLSID for "My Computer"
    EndIf

    Local $sWorkingDir = @WorkingDir
    If $iType = 1 Then
        $f_CDN__FileDialogsEx_Start = 1
        Local $sFilename = _FileOpenDialogEx($sTitle, $sInitDir, $sFilter, BitOR($OFN_ENABLESIZING, $OFN_ALLOWMULTISELECT), $sDefaultName, $hWnd)
    Else
        Local $sFilename = FileOpenDialog($sTitle, $sInitDir, $sFilter, $iOptions, $sDefaultName, $hWnd)
    EndIf
    Local $iError = @error
    FileChangeDir($sWorkingDir)

    Return SetError($iError, 0, $sFilename)
EndFunc   ;==>_FileOpenDialog_Ex

Func _FileOpenDialogEx($sTitle = "", $sInitDir = "", $sFilter = "All Files (*.*)", $iOptions = 0, $sDefaultName = "", $hParent = 0, $hTemplate = 0, $sTemplateName = "")
    Local $sRet = _GetOpenSaveFileName('GetOpenFileNameW', $hTemplate, $sTemplateName, $hParent, $sTitle, $sInitDir, $sFilter, $iOptions, $sDefaultName)
    If @error Then SetError(@error)
    Return $sRet
EndFunc   ;==>_FileOpenDialogEx

Func _GetOpenSaveFileName($sFunction, $hTemplate, $sTemplateName, $hParent, $sTitle, $sInitDir, $sFilter, $iOptions, $sDefaultName)
    Local $taFilters, $tFile, $_OFN_HookProc = 0, $iFlagsEx = 0, $iFlagsForced = BitOR($OFN_EXPLORER, $OFN_HIDEREADONLY, $OFN_NODEREFERENCELINKS)
    $iOptions = BitOR($iFlagsForced, $iOptions)
    If BitAND($iOptions, $OFN_EX_NOPLACESBAR) Then
        $iOptions = BitXOR($iOptions, $OFN_EX_NOPLACESBAR)
        $iFlagsEx = $OFN_EX_NOPLACESBAR
    EndIf
    Local $tagBuffer = "wchar[4096]", $iBufferSize = 4095
    Local $aFilters = StringSplit($sFilter, "|"), $saFilters = "", $tagFilters = "", $aFiltSplit, $i
    For $i = 1 To $aFilters[0]
        $aFiltSplit = StringRegExp($aFilters[$i], "(?U)\A\h*(.+)\h*\((.*)\)", 1)
        $saFilters &= $aFilters[$i] & Chr(0) & $aFiltSplit[1] & Chr(0)
    Next
    $tagFilters = "wchar[" & StringLen($saFilters) + 3 & "]"
    $taFilters = DllStructCreate($tagFilters)
    DllStructSetData($taFilters, 1, $saFilters)
    
    Local $iFileBufferSize = 32768 * 10
    
    Local $tagFileBuffer = "wchar[" & $iFileBufferSize & "]"
    $tFile = DllStructCreate($tagFileBuffer) ; Win2000/XP: should be 32k for ansi, unlimited for unicode
    If $sDefaultName <> "" Then DllStructSetData($tFile, 1, $sDefaultName)
    Local $tOFN = DllStructCreate('dword lStructSize;hwnd hwndOwner;hwnd hInstance;' & _
            'ptr lpstrFilter;ptr lpstrCustomFilter;dword nMaxCustFilter;dword nFilterIndex;' & _
            'ptr lpstrFile;dword nMaxFile;ptr lpstrFileTitle;dword nMaxFileTitle;ptr lpstrInitialDir;ptr lpstrTitle;' & _
            'dword Flags;short nFileOffset;short nFileExtension;ptr lpstrDefExt;dword lCustData;ptr lpfnHook;ptr lpTemplateName;' & _
            'dword Reserved[2];dword FlagsEx')

    DllStructSetData($tOFN, 'lStructSize', DllStructGetSize($tOFN))
    If IsHWnd($hParent) Then DllStructSetData($tOFN, 'hwndOwner', $hParent)
    DllStructSetData($tOFN, 'lpstrFilter', DllStructGetPtr($taFilters))
    DllStructSetData($tOFN, 'nFilterIndex', 1)
    DllStructSetData($tOFN, 'lpstrFile', DllStructGetPtr($tFile))
    DllStructSetData($tOFN, 'nMaxFile', $iFileBufferSize)
    DllStructSetData($tOFN, 'FlagsEx', $iFlagsEx)
    If $hTemplate <> 0 Then
        If $sTemplateName <> "" Then
            $iOptions = BitOR($iOptions, $OFN_ENABLETEMPLATE)
            DllStructSetData($tOFN, 'hInstance', $hTemplate)
            Local $tTemplateName = DllStructCreate($tagBuffer) ; 'char[256]')
            DllStructSetData($tTemplateName, 1, $sTemplateName)
            DllStructSetData($tOFN, 'lpTemplateName', DllStructGetPtr($tTemplateName))
        Else
            $iOptions = BitOR($iOptions, $OFN_ENABLETEMPLATEHANDLE)
            DllStructSetData($tOFN, 'hInstance', $hTemplate)
        EndIf
    EndIf

    $iOptions = BitOR($iOptions, $OFN_ENABLEHOOK, $OFN_ENABLEINCLUDENOTIFY)
    $_OFN_HookProc = DllCallbackRegister("_OFN_HookProc", "int", "hwnd;uint;wparam;lparam")
    DllStructSetData($tOFN, 'lpfnHook', DllCallbackGetPtr($_OFN_HookProc))

    If $sTitle <> "" Then
        Local $tTitle = DllStructCreate($tagBuffer)
        DllStructSetData($tTitle, 1, String($sTitle))
        DllStructSetData($tOFN, "lpstrTitle", DllStructGetPtr($tTitle))
    EndIf

    If $sInitDir <> "" Then
        Local $tInitDir = DllStructCreate($tagBuffer)
        DllStructSetData($tInitDir, 1, String($sInitDir))
        DllStructSetData($tOFN, "lpstrInitialDir", DllStructGetPtr($tInitDir))
    EndIf

    DllStructSetData($tOFN, 'Flags', $iOptions)
    Local $aRet = DllCall('comdlg32.dll', 'int', $sFunction, 'ptr', DllStructGetPtr($tOFN))
    Local $iError = @error
    If $_OFN_HookProc <> 0 Then DllCallbackFree($_OFN_HookProc)
    If $iError Then
        Return SetError(2, $iError, "")
    ElseIf $aRet[0] Then
        Local $iChar = 1
        While $iChar < $iFileBufferSize + 1
            If DllStructGetData($tFile, 1, $iChar) = "" Then
                If DllStructGetData($tFile, 1, $iChar + 1) = "" Then ExitLoop
                DllStructSetData($tFile, 1, "|", $iChar)
            EndIf
            $iChar += 1
        WEnd
        Return SetError(0, 0, DllStructGetData($tFile, 1))
    Else
        Return SetError(1, 0, "")
    EndIf
EndFunc   ;==>_GetOpenSaveFileName

Volatile Func _OFN_HookProc($hWnd, $Msg, $wParam, $lParam)
    Switch $Msg
        Case $WM_NOTIFY
            Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR
            $tNMHDR = DllStructCreate("hwnd hWndFrom;int idFrom;int code", $lParam)
            $hWndFrom = DllStructGetData($tNMHDR, "hWndFrom")
            $iIDFrom = DllStructGetData($tNMHDR, "idFrom")
            $iCode = DllStructGetData($tNMHDR, "code")
            Switch $iCode
                Case $CDN_FOLDERCHANGE

                    If $f_CDN__FileDialogsEx_Start Then ; if executing first time

                        Local $aRet = DllCall('user32.dll', 'hwnd', 'FindWindowEx', 'hwnd', $hWndFrom, 'hwnd', 0, 'str', "SHELLDLL_DefView", 'str', "")
                        If $aRet[0] = 0 Then
                            ConsoleWrite("_OFN_HookProc" & @TAB & "_FindWindowEx Error" & @CRLF)
                        Else
                            DllCall('user32.dll', 'int', 'SendMessage', 'hwnd', $aRet[0], 'uint', $WM_COMMAND, 'wparam', 0x702c, 'lparam', 0) ; $ODM_VIEW_DETAIL
                        EndIf

                        WinMove($hWndFrom, "", @DesktopWidth / 2 - 650 / 2, @DesktopHeight / 2 - 600 / 2, 650, 600)

                        $f_CDN__FileDialogsEx_Start = 0 ; to make sure these tweaks happens only once.

                    EndIf

            EndSwitch
        Case Else
    EndSwitch
    Return 0
EndFunc   ;==>_OFN_HookProc

 

Wow, worked flawlessly ! Thanks a lot !

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...