Multi wildcard filter for files like "*.exe/*.dll" with non-recursive searching.

Started by Theo Gottwald, February 03, 2024, 09:37:01 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Theo Gottwald

⚡️ Major speed boost for an updated example! 💻

🛠 Now featuring a multi wildcard filter for files like "*.exe/*.dll" with non-recursive searching.

🚫 Code updated to disable Wow64 redirection.

🏎 Leveraging Alessandro Cantatore's WildMatch routine for performance that's 4x faster than PathMatchSpec()!

🌐 Unicode support confirmed. ✅

#TechUpdate #Coding #Programmers #SoftwareDevelopment #PerformanceEnhancement #Unicode #Wow64 #ITNews 🚀🖥💾🔍♾️

---

'Multi wildcard filter, like "*.exe/*.dll".
'Pretty fast
'Non recursive.
'Code disable Wow64 redirection.
'Use of Alessandro Cantatore WildMatch routine wich is four times faster than PathMatchSpec().
'Unicode
'Etc...

#COMPILE EXE '#Win 10.04# 'José Roca includes compatible
#DIM ALL
%Unicode = 1
#INCLUDE "Win32Api.inc"
'_____________________________________________________________________________

FUNCTION FileTimeToInternationalDate(File_Time AS FileTime) AS WSTRING
 LOCAL Local_File_Time AS FileTime
 LOCAL Sys_Time        AS SystemTime

 FileTimeToLocalFileTime(File_Time, Local_File_Time) 'Convert (UTC) Universal Time Coordinate to local time
 FileTimeToSystemTime(Local_File_Time, Sys_Time)     'Convert to SystemTime

 'Make a human readable string date and time
 FUNCTION = FORMAT$(Sys_Time.wYear, "0000") & "/" & FORMAT$(Sys_Time.wMonth,  "00") & "/" & _
            FORMAT$(Sys_Time.wDay,    "00") & " " & FORMAT$(Sys_Time.wHour,   "00") & ":" & _
            FORMAT$(Sys_Time.wMinute, "00") & ":" & FORMAT$(Sys_Time.wSecond, "00") & "." & _
            FORMAT$(Sys_Time.wMilliSeconds, "000") & " " & _
            CHOOSE$(Sys_Time.wDayOfWeek + 1, "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")

END FUNCTION
'_____________________________________________________________________________

FUNCTION WildMatchAC7(BYVAL sString AS WSTRING, BYVAL sFilter AS WSTRING) AS LONG
 'Validate data using wildard * ?
 '(?) Any single character, (*) None or more character, 4 time faster than PathMatchSpec
 '7th routine. C version by by Alessandro Cantatore. http://xoomer.virgilio.it/acantato/dev/wildcard/wildmatch.html#c_cpp_userjournal_algo
 LOCAL pStringStart AS WORD POINTER
 LOCAL pStringIndex AS WORD POINTER
 LOCAL pFilterStart AS WORD POINTER
 LOCAL pFilterIndex AS WORD POINTER
 LOCAL Star         AS LONG

 pStringStart = STRPTR(sString) : pFilterStart = STRPTR(sFilter)
 'CharLowerBuff(BYVAL pStringStart, LEN(sString)) : CharLowerBuff(BYVAL pFilterStart, LEN(sFilter)) 'For console

 pStringIndex = pStringStart : pFilterIndex = pFilterStart
 DO
   IF @pStringIndex = 0 THEN EXIT DO

   SELECT CASE @pFilterIndex

     CASE 42 '(*)
       Star = %TRUE
       pStringStart = pStringIndex : pFilterStart = pFilterIndex
       WHILE @pFilterStart = 42 '(*) For consecutive (*). While-wend may be removed if multi* is shrinked to single* before calling the function
         INCR pFilterStart
       WEND
       IF @pFilterStart = 0 THEN FUNCTION = %TRUE : EXIT FUNCTION
       pStringIndex = pStringStart : pFilterIndex = pFilterStart
       ITERATE DO

     CASE 63 '(?)
       'Let it go

     CASE ELSE
       IF @pStringIndex <> @pFilterIndex THEN
         IF star = 0 THEN EXIT FUNCTION
         INCR pStringStart
         pStringIndex = pStringStart : pFilterIndex = pFilterStart
         ITERATE DO
       END IF

   END SELECT
   INCR pStringIndex : INCR pFilterIndex
 LOOP

 WHILE @pFilterIndex = 42 '(*)
   INCR pFilterIndex
 WEND

 IF @pFilterIndex = 0 THEN FUNCTION = %TRUE

END FUNCTION
'_____________________________________________________________________________

FUNCTION FileEnum(BYVAL sFolder AS WSTRING, BYVAL sFilter AS WSTRING, _
                  BYREF FileDataArray() AS WIN32_FIND_DATA) AS LONG
 LOCAL FileData            AS WIN32_FIND_DATA
 LOCAL hFile               AS DWORD
 LOCAL hLib                AS DWORD
 LOCAL pProc               AS DWORD
 LOCAL uBoundSubFolder     AS DWORD
 LOCAL uBoundFileDataArray AS DWORD
 LOCAL Wow64RedirectionVal AS DWORD
 LOCAL SubFolderCount      AS LONG
 LOCAL FileCount           AS LONG
 LOCAL FilterIndex         AS LONG
 LOCAL FilterCount         AS LONG
 LOCAL index               AS LONG

 REDIM FileDataArray(0 TO 50000) AS WIN32_FIND_DATA
 uBoundFileDataArray = UBOUND(FileDataArray())

 DIM zSubFolder(0 TO 1000) AS WSTRINGZ * %MAX_PATH
 uBoundSubFolder = UBOUND(zSubFolder)

 'DO
 '  REPLACE "//" WITH "/" IN sFilter
 'LOOP UNTIL INSTR(sFilter, "//") = 0

 FilterCount = PARSECOUNT(sFilter, "/")
 DIM sFilterArray(1 TO FilterCount) AS STRING
 PARSE sFilter, sFilterArray(), "/"
 FOR index = 1 TO FilterCount
   sFilterArray(index) = TRIM$(sFilterArray(index))
 NEXT

 IF ASC(sFolder, - 1) <> 92 THEN sFolder = sFolder & "\"

 'The following Wow64DisableWow64FsRedirection disable file system redirection
 ' so that a 32-bit application that is running under WOW64 can open
 ' the 64-bit version of files in %SystemRoot%\System32
 ' instead of being redirected to the 32-bit version in %SystemRoot%\SysWOW64.
 'Disables file system redirection for the calling thread. File system redirection is enabled by default.
 Wow64DisableWow64FsRedirection(Wow64RedirectionVal) 'Use with XPpro64, Vista, Server2003sp1, and newer

 DO
   hFile = FINDFIRSTFILE(sFolder & "*", FileData)
   IF hFile <> %INVALID_HANDLE_VALUE THEN
     DO
       IF (FileData.dwFileAttributes AND %FILE_ATTRIBUTE_DIRECTORY) THEN  'It's a folder
         IF ASC(FileData.cFileName, -1) <> 46 THEN 'Check for "." and "..", only those can end with a dot.
           INCR SubFolderCount
           IF SubFolderCount > uBoundSubFolder THEN
             uBoundSubFolder = uBoundSubFolder + 1000
             REDIM PRESERVE zSubFolder(0 TO uBoundSubFolder)
           END IF
           zSubFolder(SubFolderCount) = sFolder & FileData.cFileName & "\"
         END IF
       ELSE 'It's a file, have a cigar!
         FOR FilterIndex = 1 TO FilterCount
           'If .cFilename is SFN then .cAlternateFileName will be empty
           'If .cFilename is LFN then .cAlternateFileName will contain SFN
           IF WildMatchAC7(FileData.cFileName, sFilterArray(FilterIndex)) THEN 'Four time faster than PATHMATCHSPEC()
             FileDataArray(FileCount) = FileData
             FileDataArray(FileCount).cFileName = sFolder & FileData.cFileName
             'Also available FileDataArray(FileCount).cAlternateFileName (Short filename)
             'Also available FileDataArray(FileCount).dwFileAttributes
             'Also available FileDataArray(FileCount).ftCreationTime
             'Also available FileDataArray(FileCount).ftLastAccessTime
             'Also available FileDataArray(FileCount).nFileSizeHigh
             'Also available FileDataArray(FileCount).nFileSizeLow
             INCR FileCount
             IF FileCount > uBoundFileDataArray THEN
               uBoundFileDataArray = uBoundFileDataArray + 50000
               REDIM PRESERVE FileDataArray(0 TO uBoundFileDataArray)
             END IF
             EXIT FOR
           END IF
         NEXT
       END IF
     LOOP WHILE FINDNEXTFILE(hFile, FileData)
     FINDCLOSE(hFile)
   END IF
   IF SubFolderCount = 0 THEN EXIT LOOP
   sFolder = zSubFolder(SubFolderCount)
   DECR SubFolderCount
 LOOP

 Wow64RevertWow64FsRedirection(Wow64RedirectionVal) 'Use with XPpro64, Vista, Server2003sp1, and newer

 REDIM PRESERVE FileDataArray(FileCount)
 FUNCTION = FileCount

END FUNCTION
'_____________________________________________________________________________

FUNCTION PBMAIN() AS LONG
 DIM   FileDataArray2(0 TO 0) AS WIN32_FIND_DATA
 LOCAL sFolder                AS WSTRING
 LOCAL sFilter                AS WSTRING
 LOCAL sLog                   AS WSTRING
 LOCAL TotalByte              AS QUAD
 LOCAL FileCount              AS DWORD
 LOCAL TimeStart              AS DWORD
 LOCAL index                  AS LONG

 sFolder = "C:\" : sFilter = "*.exe/*.dll*"
 'sFolder = "D:\" : sFilter = "*.bas/*.exe/*.rc"

 TimeStart = GetTickCount()
 FileCount = FileEnum(sFolder, sFilter, FileDataArray2()) '32 sec on first else .546

 sLog = "Folder: " & $TAB & sFolder & $CRLF & "Filter: " & $TAB & sFilter & $CRLF & $CRLF

 FOR index = 0 TO MIN(15, FileCount - 1)
   sLog &= FORMAT$(index, "0000") & ") " & FileDataArray2(index).cFileName & $CRLF
   TotalByte = TotalByte + MAK(QUAD, FileDataArray2(index).nFileSizeLow ,FileDataArray2(index).nFileSizeHigh)
 NEXT

 sLog &= "..." & $CRLF & $CRLF &  FORMAT$((GetTickCount() - TimeStart) / 1000, "###,###.000") & " seconds elapsed" & $CRLF & $CRLF & _
         "File count " & FORMAT$(FileCount - 1, "#,#") & _
         $CRLF & $CRLF & "Total byte " & FORMAT$(TotalByte, "#,#") & $CRLF

 MessageBox(%HWND_DESKTOP, (sLog), "FINDFIRS/NEXTTFILE", %MB_OK OR %MB_SYSTEMMODAL OR %MB_TOPMOST)

END FUNCTION
'_____________________________________________________________________________
'�