Does anyone knows fastest way to search a filename in specific folder?

I need help for searching specific filename in a specific directory on windows and then the function return the full file path including the filename.

On searching i have found this thread which using windows API.

Based on code by @Jim Cramer in that thread, i have the following

Public Function FastSearch(path As folderitem, filename As String) as FolderItem
  #if Not TargetWin32 or TargetWindows
    return nil
  #endif
  // Return all of the files within a folder in the quickest way possible.
  // courtesy: http://forums.realsoftware.com/viewtopic.php?f=1&t=13692&st=0&sk=t&sd=a&hilit=folderItem+aaron&start=15
  // With modificaitons by me to use NativePath, corrected a minor bug and ignore ".", ".." entries.
  // NOTE: This is windows only....XP to present.
  
  Soft Declare Function FindFirstFileA Lib "Kernel32" ( path as CString, data as Ptr ) as Integer
  Soft Declare Function FindFirstFileW Lib "Kernel32" ( path as WString, data as Ptr ) as Integer
  Soft Declare Function FindNextFileA Lib "Kernel32" ( handle as Integer, data as Ptr ) as Boolean
  Soft Declare Function FindNextFileW Lib "Kernel32" ( handle as Integer, data as Ptr ) as Boolean
  Declare Sub FindClose Lib "Kernel32" ( handle as Integer )
  
  dim ret( -1 ) as FolderItem
  dim handle as Integer
  
  // Sanity check
  if not path.Directory then return ret
  
  dim data as MemoryBlock
  if System.IsFunctionAvailable( "FindFirstFileW", "Kernel32" ) then
    data = new MemoryBlock( 592 )
    handle = FindFirstFileW( path.NativePath + filename, data )
  else
    data = new MemoryBlock( 318 )
    handle = FindFirstFileA( path.NativePath + filename, data )
  end if
  
  if handle <> -1 then
    // Loop over all of the items in using the handle and the find data
    dim done as Boolean
    do
      // Add the current item
      dim temp as FolderItem
      if System.IsFunctionAvailable( "FindFirstFileW", "Kernel32" ) then
        temp = GetFolderItem(path.NativePath+data.WString(44),FolderItem.PathTypeNative)   //FolderItem( data.WString( 44 ), FolderItem.PathTypeAbsolute )
        
        // Loop to the next item
        done = FindNextFileW( handle, data )
      else
        temp = GetFolderItem(path.NativePath+data.WString(44),FolderItem.PathTypeNative)   //FolderItem( data.WString( 44 ), FolderItem.PathTypeAbsolute )
        
        // Loop to the next item
        done = FindNextFileA( handle, data )
      end if
      
      // Add the current item to our list
      // Ignore . and ..
      Dim tmpString as String = temp.Name
      If tmpString <> "." and tmpString <> ".." then
        ret.Append( temp )
      End If
    loop until not done
    
    FindClose( handle )
  end if
  
  return ret
End Function

Button action

Var DriverStorePath As FolderItem
Var Search As FolderItem
Var Result As String
Var nvidiaopencl32filename As String
Var nvidiaopencl64filename As String


DriverStorePath = New FolderItem("C:\\Windows\\System32\\DriverStore\\FileRepository\", FolderItem.PathModes.Native)
nvidiaopencl32filename = "nvopencl32.dl"

Search = FastSearch(DriverStorePath, nvidiaopencl32filename)

Result = Search.PathModes.Native

But getting error :

And this error below confused me

Also what does “dim ret( -1 ) as FolderItem” means? the “(-1)” part.

It set the array to nothing (initialize). So, the first added information will be in ret(0)…

For the main part of the question: I certainly do not understand the question.

If I search a file named “Test.txt” in a folder (a specific folder, not anywhere in the hard disk), I will check if the FolderItem Exists.

Code example (seach Test.txt beside the application):

Var f As New FolderItem("", FolderItem.PathModes.Native) f = f.Child("Test.txt") If f.Exists Then // The file exists End If

Sorry if i am confusing you, what i have meant is to recursively to search a specific filename in a specific folder location. It need to traversing and recursively search under that folder location/child folder underneath the specified folder input and listed all path + filename matches, if the same filename happens to be found the two or more child/root folder then it will listed it out.

So the function FastSearch above is returning FolderItem array?. if yes then how to called the method and process the method output?

Because modified into

Var DriverStorePath,Search() As FolderItem
Var FileClass As new File
Var Result As String
Var nvidiaopencl32filename As String
Var nvidiaopencl64filename As String


DriverStorePath = new FolderItem("C:\\Windows\\System32\\DriverStore\\FileRepository\", FolderItem.PathModes.Native)
nvidiaopencl32filename = "nvopencl64.dl"

Search = FileClass.FastSearch(DriverStorePath, nvidiaopencl32filename)

For i As Integer = 1 To Search.Count
  MessageBox(Search(i).PathModes.Native)
Next

Still getting the

Silly me, Pathmodes is a an enum?, so i have modified it now like this :

Var DriverStorePath,Search() As FolderItem
Var FileClass As new File
Var Result As String
Var nvidiaopencl32filename As String
Var nvidiaopencl64filename As String


DriverStorePath = new FolderItem("C:\\Windows\\System32\\DriverStore\\FileRepository", FolderItem.PathModes.Native)
nvidiaopencl32filename = "nvopencl64.dll"

Search = FileClass.FastSearch(DriverStorePath, nvidiaopencl32filename)

For i As Integer = 0 To Search.Count
  MessageBox(Search(i).NativePath)
Next
//myArray.RemoveAllRows

// myArray is now empty
//(DriverStorePath, nvidiaopencl32filename)

//Result = Search.PathModes.Native

Running it and it cannot found the files, the Search.Count is always 0 just like the file is not exist?, i have double checked it and it exist, already tried to run the compiled app as administrator and still cannot found it, changed path to other normal user access also cannot found the files.

Commenting these line is fixed it :

//#if Not TargetWin32 or TargetWindows
//return nil
//#endif

But no matter what i have done, it can only searches at normal user directory not “c:\windows\system32”!, i have tried to run the compiled app as administrator but also still no possible to search at “c:\windows\system32”, how to search in that system folder then?

The code is now the following :

//#if Not TargetWin32 or TargetWindows
//return nil
//#endif
// Return all of the files within a folder in the quickest way possible.
// courtesy: http://forums.realsoftware.com/viewtopic.php?f=1&t=13692&st=0&sk=t&sd=a&hilit=folderItem+aaron&start=15
// With modificaitons by me to use NativePath, corrected a minor bug and ignore ".", ".." entries.
// NOTE: This is windows only....XP to present.

Soft Declare Function FindFirstFileA Lib "Kernel32" ( path as CString, data as Ptr ) as Integer
Soft Declare Function FindFirstFileW Lib "Kernel32" ( path as WString, data as Ptr ) as Integer
Soft Declare Function FindNextFileA Lib "Kernel32" ( handle as Integer, data as Ptr ) as Boolean
Soft Declare Function FindNextFileW Lib "Kernel32" ( handle as Integer, data as Ptr ) as Boolean
Declare Sub FindClose Lib "Kernel32" ( handle as Integer )

dim ret( -1 ) as FolderItem
dim handle as Integer

// Sanity check
if not path.Directory then 
  return ret
end if

dim data as MemoryBlock
if System.IsFunctionAvailable( "FindFirstFileW", "Kernel32" ) then
  data = new MemoryBlock( 592 )
  handle = FindFirstFileW( path.NativePath + filename, data )
  //MessageBox(path.NativePath + filename)
else
  data = new MemoryBlock( 318 )
  handle = FindFirstFileA( path.NativePath + filename, data )
end if

if handle <> -1 then
  // Loop over all of the items in using the handle and the find data
  dim done as Boolean
  do
    // Add the current item
    dim temp as FolderItem
    if System.IsFunctionAvailable( "FindFirstFileW", "Kernel32" ) then
      temp = new FolderItem(path.NativePath+data.WString(44),FolderItem.PathModes.Native)   //FolderItem( data.WString( 44 ), FolderItem.PathTypeAbsolute )
      
      // Loop to the next item
      done = FindNextFileW( handle, data )
    else
      temp = new FolderItem(path.NativePath+data.WString(44),FolderItem.PathModes.Native)   //FolderItem( data.WString( 44 ), FolderItem.PathTypeAbsolute )
      
      // Loop to the next item
      done = FindNextFileA( handle, data )
    end if
    
    // Add the current item to our list
    // Ignore . and ..
    Dim tmpString as String = temp.Name
    If tmpString <> "." and tmpString <> ".." then
      ret.Append( temp )
    End If
  loop until not done
  
  FindClose( handle )
end if

return ret

The window code :

Var DriverStorePath,Search(-1) As FolderItem
Var FileClass As new File
Var Result As String
Var nvidiaopencl32filename As String
Var nvidiaopencl64filename As String


DriverStorePath = new FolderItem("C:\\Windows\\System32\\DriverStore\\FileRepository", FolderItem.PathModes.Native)
nvidiaopencl32filename = "*.dll"

Search = FileClass.FastSearch(DriverStorePath, nvidiaopencl32filename)

For i As Integer = 0 To Search.Count-1
  txtResult.AddText(Search(i).NativePath + EndOfLine.Windows.ToText)
Next

The problem is not about directory access into “C:\Windows\System32\DriverStore\FileRepository” directory but the fastsearch method doesn’t traversing into other child directories, the question is can anyone modified the fastsearch code for also traversing into child directories from the root folder?

Fixing it myself, now i am finally able to traverse searching for either specific filename or file extension, the code is not elegant or newbie approach, so any suggestion welcomed.

FastSearch Method :

Public Sub FastSearch(path As FolderItem, optional fileextension As string, filename As string)
  //#if Not TargetWin32 or TargetWindows
  //return nil
  //#endif
  // Return all of the files within a folder in the quickest way possible.
  // courtesy: http://forums.realsoftware.com/viewtopic.php?f=1&t=13692&st=0&sk=t&sd=a&hilit=folderItem+aaron&start=15
  // With modificaitons by me to use NativePath, corrected a minor bug and ignore ".", ".." entries.
  // NOTE: This is windows only....XP to present.
  
  Soft Declare Function FindFirstFileA Lib "Kernel32" ( path as CString, data as Ptr ) as Integer
  Soft Declare Function FindFirstFileW Lib "Kernel32" ( path as WString, data as Ptr ) as Integer
  Soft Declare Function FindNextFileA Lib "Kernel32" ( handle as Integer, data as Ptr ) as Boolean
  Soft Declare Function FindNextFileW Lib "Kernel32" ( handle as Integer, data as Ptr ) as Boolean
  Declare Sub FindClose Lib "Kernel32" ( handle as Integer )
  
  dim ret( -1 ) as FolderItem
  dim handle as Integer
  dim sNameParts() as String
  dim fullfilename as String = ""
  
  if filename = "" then
    fullfilename = "*" + "." + fileextension
  elseif filename <> "" And fileextension <> "" then
    fullfilename = filename + "." + fileextension
  elseif filename <> "" And fileextension = "" then
    break
  end if
  
  // Sanity check
  if not path.Directory then 
    //return ret
    break
  end if
  
  dim data as MemoryBlock
  if System.IsFunctionAvailable( "FindFirstFileW", "Kernel32" ) then
    data = new MemoryBlock( 592 )
    handle = FindFirstFileW( path.NativePath + fullfilename, data )
    //MessageBox(path.NativePath + filename)
  else
    data = new MemoryBlock( 318 )
    handle = FindFirstFileA( path.NativePath + fullfilename, data )
  end if
  
  if handle <> -1 then
    // Loop over all of the items in using the handle and the find data
    dim done as Boolean
    do
      // Add the current item
      dim temp as FolderItem
      if System.IsFunctionAvailable( "FindFirstFileW", "Kernel32" ) then
        temp = new FolderItem(path.NativePath+data.WString(44),FolderItem.PathModes.Native)   //FolderItem( data.WString( 44 ), FolderItem.PathTypeAbsolute )
        
        // Loop to the next item
        done = FindNextFileW( handle, data )
      else
        temp = new FolderItem(path.NativePath+data.WString(44),FolderItem.PathModes.Native)   //FolderItem( data.WString( 44 ), FolderItem.PathTypeAbsolute )
        
        // Loop to the next item
        done = FindNextFileA( handle, data )
      end if
      
      // Add the current item to our list
      // Ignore . and ..
      Dim tmpString as String = temp.Name
      If tmpString <> "." and tmpString <> ".." then
        ret.Append( temp )
      End If
    loop until not done
    
    FindClose( handle )
  end if
  
  if path<>nil then
    if path.Directory then
      for i as Integer=1 to path.Count
        if path.Item(i).Directory then
          FastSearch(path.Item(i), fileextension, filename)
        else
          sNameParts = Split(path.Item(i).Name, ".")
          if sNameParts.Ubound > 0 And sNameParts.Pop = fileextension And filename = "" then
            OpenCLFixer.SetSearchResultArray(path.Item(i).NativePath)
          elseif path.Item(i).Name = fullfilename then
            OpenCLFixer.SetSearchResultArray(path.Item(i).NativePath)
          end if
        end if
      next
    end if
  end if
  
  //return ret
End Sub

Search Result property :

Public Property SearchResult() as String

SetSearchResultArray :

Public Sub SetSearchResultArray(result As String)
  SearchResult.Append(result)
End Sub

btnSearch :

Sub Action() Handles Action
  Var DriverStorePath,Search(-1) As FolderItem
  Var FileClass As new File
  Var Result As String
  Var nvidiaopencl32filename As String
  Var nvidiaopencl64filename As String
  Var fileextension As String
  
  
  DriverStorePath = new FolderItem("C:\\Windows\\System32\\DriverStore\\FileRepository", FolderItem.PathModes.Native)
  nvidiaopencl32filename = "nvopencl32"
  fileextension = "sys"
  
  FileClass.FastSearch(DriverStorePath, fileextension, nvidiaopencl32filename)
  
  MsgBox("Finished")
  For i As Integer = 0 To SearchResult.Ubound
    lstResult.AddRow(SearchResult(i) + EndOfLine.Windows.ToText)
  Next
  
End Sub

Credit to @Marco Hof for directory traversal codes, and @Jim Cramer for original search method.

The minuses with code above are :

  • The FastSearch method needs to be a void method because of it’s needs to call itself recursively.
  • Directory traversal codes is using Xojo one, might be slower but from my test traversing that root folder above is only around 7 seconds!
  • Not threaded? because searching in big directory like “C:\Users\username” will make it hang.

Hopefully Senior or some members able to improve it a lot by using Windows API for directory traversal. At-least quite useful for me to coding a simple tool.

Now having the code by using mostly windows API :

Public Sub FastSearch(path As FolderItem, optional fileextension As string, filename As string)
  //#if Not TargetWin32 or TargetWindows
  //return nil
  //#endif
  // Return all of the files within a folder in the quickest way possible.
  // courtesy: http://forums.realsoftware.com/viewtopic.php?f=1&t=13692&st=0&sk=t&sd=a&hilit=folderItem+aaron&start=15
  // With modificaitons by me to use NativePath, corrected a minor bug and ignore ".", ".." entries.
  // NOTE: This is windows only....XP to present.
  
  Soft Declare Function FindFirstFileA Lib "Kernel32" ( path as CString, data as Ptr ) as Integer
  Soft Declare Function FindFirstFileW Lib "Kernel32" ( path as WString, data as Ptr ) as Integer
  Soft Declare Function FindNextFileA Lib "Kernel32" ( handle as Integer, data as Ptr ) as Boolean
  Soft Declare Function FindNextFileW Lib "Kernel32" ( handle as Integer, data as Ptr ) as Boolean
  Declare Sub FindClose Lib "Kernel32" ( handle as Integer )
  
  dim ret( -1 ) as String
  dim handle as Integer
  dim sNameParts() as String
  dim fullfilename as String = ""
  
  if filename = "" then
    fullfilename = "*" + "." + fileextension
  elseif filename <> "" And fileextension <> "" then
    fullfilename = filename + "." + fileextension
  elseif filename <> "" And fileextension = "" then
    break
  end if
  
  // Sanity check
  //if not path.Directory then 
  //break
  //end if
  
  dim data as MemoryBlock
  if System.IsFunctionAvailable( "FindFirstFileW", "Kernel32" ) then
    data = new MemoryBlock( 40000 )
    handle = FindFirstFileW(path.NativePath + "*" + Chr(0), data)
    //MessageBox(path.NativePath + fullfilename)
  else
    data = new MemoryBlock( 318 )
    handle = FindFirstFileA( path.NativePath + fullfilename, data )
  end if
  
  If handle > 0 Then
    Do
      dim temp as FolderItem
      if data.int32value(0) = 16 Then
        'if this is a directory then resurse through the directory to get all files
        
        if data.WString(44) <> ".." and data.WString(44) <> "." Then
          temp = new FolderItem(path.NativePath+data.WString(44),FolderItem.PathModes.Native)
          FastSearch(temp, fileextension, filename)
          
        end if
        
      elseif System.IsFunctionAvailable( "FindFirstFileW", "Kernel32" ) then
        temp = new FolderItem(path.NativePath+data.WString(44),FolderItem.PathModes.Native)   //FolderItem( data.WString( 44 ), FolderItem.PathTypeAbsolute )
        sNameParts = Split(temp.Name, ".")
        if sNameParts.Ubound > 0 And sNameParts.Pop = fileextension And filename = "" then
          Testing.Append(temp.NativePath)
        elseif temp.NativePath = fullfilename then
          Testing.Append(temp.NativePath)
        end if
      end if
      
    Loop Until Not FindNextFileW(handle, data)
    
    Call FindClose(handle)
  End If
  
  //return ret
End Sub

The problem is it’s fine for small folder but once you tried in “C:\Users\Username” it only doing the traversal partly, what did i have done wrong?, please help.

See https://forum.xojo.com/39404-read-contents-of-directory/p3#p467436 and change your If to

If (data.int32value(0) And 16) = 16 Then

Also, I would suggest against new’ing a FolderItem just to split it, use PathFindFileNameW and PathFindExtensionW instead.

Here’s a quick fix, it’ll need a little tidy up

https://www.dropbox.com/s/n5zm8cg8tbgvpw0/TestFindForAditya.xojo_binary_project?dl=0

Hope it helps.

On a side note, it might be better/quicker to add the directories into a list then process them off the list. See the following for more info:

https://web.archive.org/web/20151016004624/http://blogs.msdn.com/b/oldnewthing/archive/2005/02/03/366277.aspx

Thank a lot @JulianS , you did it again :-), really appreciated it, an excellent codes and also really really fast , comparable with those using C/C++ language!.

[quote=474177:@JulianS]On a side note, it might be better/quicker to add the directories into a list then process them off the list. See the following for more info:

Why is breadth-first searching better for file system tree walking? - The Old New Thing - Site Home - MSDN Blogs [/quote]

This means it’s useful for something like an server/daemon sort of things right?, if it’s also useful for normal app then we can serialize the output to a file and read from it?, so something like a cache?.

On searching found this, so i assume, we need to use collection in xojo in order to implemented it?, can you share the codes?, i have no clue or found any one in xojo has shared about Breadth First Search.

Here’s some code showing the two principles

https://www.codeproject.com/Articles/93141/Iterative-Implementation-of-Recursively-Enumeratin