Visual Basic 2008 scan files by extension

disrupt
disrupt used Ask the Experts™
on
I'm currently using the code below to search for files on the desktop, I want to be able to search .csv files on the desktop and my documents and I want it to prevent it from hanging when you press the button.  How can I achieve this? I originally wanted to search the whole drive but I don't want it to hang and show some kind of progress bar or something to show it is searching. How it is setup, when I click a button ShowCSVFiles Form shows up and it executes the search and drops em into a listbox.
For Each foundFile As String In My.Computer.FileSystem.GetFiles( _
My.Computer.FileSystem.SpecialDirectories.Desktop, _
FileIO.SearchOption.SearchAllSubDirectories, "*.csv")
ShowCSVFiles.CSVListBox.Items.Add(foundFile)
Next

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®

Commented:
You will need to utilize multi-threading. This allows applications to create a seperate process thread that runs along side of your program so your application doesn't hang / wait for a process to complete.

I would suggest reading up on it here.
http://msdn.microsoft.com/en-us/library/eed6swsx(VS.80).aspx
Mike TomlinsonHigh School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009

Commented:
Even with simple searches of My Documents the FileSystem.GetFiles() method with the SearchAllSubDirectories approach bombs out when it can't access certain special folders.  This cannot be fixed with a Try/Catch either...

A more robust approach is to write your own recursive search:
http://www.experts-exchange.com/Programming/Languages/.NET/Visual_Basic.NET/Q_24833438.html

So you would use code similar to above but apply multi-threading to it as nezoic suggests.

Author

Commented:
I used ur code it worked nicely, I'm confused to how to add it as a background worker I gave it a shot but couldn't get it working =(
Public Class Form1
 
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim StartingPath As String = My.Computer.FileSystem.SpecialDirectories.MyDocuments
        Dim File As String = "*.csv"
        Dim Matches As New List(Of System.IO.FileInfo)
        FindFiles(StartingPath, File, Matches)
        For Each Match As System.IO.FileInfo In Matches
            Debug.Print(Match.FullName)
        Next
    End Sub
 
    Private Sub FindFiles(ByVal Path As String, ByVal File As String, ByVal Matches As List(Of System.IO.FileInfo))
        Try
            Dim di As New System.IO.DirectoryInfo(Path)
            Try
                For Each fi As System.IO.FileInfo In di.GetFiles(File)
                    Matches.Add(fi)
                Next
            Catch ex As Exception
            End Try
            Try
                For Each SubDI As System.IO.DirectoryInfo In di.GetDirectories
                    FindFiles(SubDI.FullName, File, Matches)
                Next
            Catch ex As Exception
            End Try
        Catch ex As Exception
        End Try
    End Sub
 
End Class

Open in new window

Success in ‘20 With a Profitable Pricing Strategy

Do you wonder if your IT business is truly profitable or if you should raise your prices? Learn how to calculate your overhead burden using our free interactive tool and use it to determine the right price for your IT services. Start calculating Now!

High School Computer Science, Computer Applications, Digital Design, and Mathematics Teacher
Top Expert 2009
Commented:
Here's an example of running the search on another thread via the BackgroundWorker():
Public Class Form1

    Private WithEvents BGW As New System.ComponentModel.BackgroundWorker

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        If Not BGW.IsBusy Then
            BGW.WorkerReportsProgress = True
            Button1.Enabled = False
            CSVListBox.Items.Clear()
            BGW.RunWorkerAsync()
        End If
    End Sub

    Private Sub BGW_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BGW.DoWork
        Dim StartingPath As String = My.Computer.FileSystem.SpecialDirectories.MyDocuments
        Dim File As String = "*.csv"
        FindFiles(StartingPath, File)
    End Sub

    Private Sub FindFiles(ByVal Path As String, ByVal File As String)
        Try
            Dim di As New System.IO.DirectoryInfo(Path)
            Try
                For Each fi As System.IO.FileInfo In di.GetFiles(File)
                    BGW.ReportProgress(-1, fi)
                Next
            Catch ex As Exception
            End Try
            Try
                For Each SubDI As System.IO.DirectoryInfo In di.GetDirectories
                    FindFiles(SubDI.FullName, File)
                Next
            Catch ex As Exception
            End Try
        Catch ex As Exception
        End Try
    End Sub

    Private Sub BGW_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BGW.ProgressChanged
        CSVListBox.Items.Add(e.UserState)
    End Sub

    Private Sub BGW_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BGW.RunWorkerCompleted
        MessageBox.Show("Done Searching!")
        Button1.Enabled = True
    End Sub

End Class

Open in new window

Top Expert 2010

Commented:

Here some code I wrote awhile back to read the MFT(Master File Table). This work only with NTFS partitions of course and you will need administrative rights on Vista and above. It works for standard/limited users on Windows XP because device handle is not subject to administrator privilege. You may see speeds up to 10x faster than managed code when searching an entire volumes.  The code was tailored search for (.csv) files on any NTFS volume. Pretty cool stuff even though it's unmanaged code.
 Just place into a code file and you can use like this:
Dim c As New Class1
c.FindAllFiles("c:")  

Imports System.Runtime.InteropServices


Public Class Class1

    Private Const INVALID_HANDLE_VALUE = (-1)
    Private Const GENERIC_READ = &H80000000
    Private Const FILE_SHARE_READ = &H1
    Private Const FILE_SHARE_WRITE = &H2
    Private Const OPEN_EXISTING = 3

    Private Const FILE_FLAG_BACKUP_SEMANTICS = &H2000000
    Private Const FileNameInformationClass = 9
    Private Const FILE_OPEN_FOR_BACKUP_INTENT = &H4000
    Private Const FILE_OPEN_BY_FILE_ID = &H2000
    Private Const FILE_COMPLETE_IF_OPLOCKED = &H100
    Private Const FILE_SHARE_DELETE = &H4
    Private Const FILE_OPEN = &H1
    Private Const OBJ_CASE_INSENSITIVE = &H40
    Private Const OBJ_KERNEL_HANDLE = &H200

    Private Const FSCTL_ENUM_USN_DATA = &H900B3

    Private Structure MFT_ENUM_DATA
        Dim StartFileReferenceNumber As Long
        Dim LowUsn As Long
        Dim HighUsn As Long
    End Structure

    Private Structure USN_RECORD
        Dim RecordLength As Integer
        Dim MajorVersion As Short
        Dim MinorVersion As Short
        Dim FileReferenceNumber As Long
        Dim ParentFileReferenceNumber As Long
        Dim Usn As Long
        Dim TimeStamp As Long
        Dim Reason As Integer
        Dim SourceInfo As Integer
        Dim SecurityId As Integer
        Dim FileAttributes As Integer
        Dim FileNameLength As Short
        Dim FileNameOffset As Short
    End Structure

    Private Structure IO_STATUS_BLOCK
        Dim Status As Integer
        Dim Information As Integer
    End Structure

    Private Structure UNICODE_STRING
        Dim Length As Short
        Dim MaximumLength As Short
        Dim Buffer As IntPtr
    End Structure

    Private Structure OBJECT_ATTRIBUTES
        Dim Length As Integer
        Dim RootDirectory As IntPtr
        Dim ObjectName As IntPtr
        Dim Attributes As Integer
        Dim SecurityDescriptor As Integer
        Dim SecurityQualityOfService As Integer
    End Structure

    '// MFT_ENUM_DATA
    <DllImport("kernel32.dll", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function DeviceIoControl(ByVal hDevice As IntPtr, ByVal dwIoControlCode As Integer, ByRef lpInBuffer As MFT_ENUM_DATA, ByVal nInBufferSize As Integer, ByVal lpOutBuffer As IntPtr, ByVal nOutBufferSize As Integer, ByRef lpBytesReturned As Integer, ByVal lpOverlapped As IntPtr) As Boolean
    End Function

    <DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function CreateFile(ByVal lpFileName As String, ByVal dwDesiredAccess As Integer, ByVal dwShareMode As Integer, ByVal lpSecurityAttributes As IntPtr, ByVal dwCreationDisposition As Integer, ByVal dwFlagsAndAttributes As Integer, ByVal hTemplateFile As IntPtr) As IntPtr
    End Function

    <DllImport("kernel32.dll", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function CloseHandle(ByVal lpObject As IntPtr) As Int32
    End Function

    <DllImport("ntdll.dll", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function NtCreateFile(ByRef FileHandle As IntPtr, ByVal DesiredAccess As Integer, ByRef ObjectAttributes As OBJECT_ATTRIBUTES, ByRef IoStatusBlock As IO_STATUS_BLOCK, ByVal AllocationSize As Integer, ByVal FileAttribs As Integer, ByVal SharedAccess As Integer, ByVal CreationDisposition As Integer, ByVal CreateOptions As Integer, ByVal EaBuffer As Integer, ByVal EaLength As Integer) As Integer
    End Function

    <DllImport("ntdll.dll", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function NtQueryInformationFile(ByVal FileHandle As IntPtr, ByRef IoStatusBlock As IO_STATUS_BLOCK, ByVal FileInformation As IntPtr, ByVal Length As Integer, ByVal FileInformationClass As Integer) As Integer
    End Function

    Private m_hCJ As IntPtr
    Private m_Buffer As IntPtr
    Private m_BufferSize As Integer
    Private m_DriveLetter As String

    Private Function OpenVolume(ByVal szDriveLetter As String) As IntPtr

        Dim hCJ As IntPtr '// volume handle

        m_DriveLetter = szDriveLetter

        hCJ = CreateFile("\\.\" & szDriveLetter, GENERIC_READ, _
                         FILE_SHARE_READ Or FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, 0)

        Return hCJ

    End Function

    Private Sub Cleanup()

        If m_hCJ <> 0 Then
            ' Close the volume handle.
            CloseHandle(m_hCJ)
            m_hCJ = INVALID_HANDLE_VALUE
        End If

        If m_Buffer <> 0 Then
            ' Free the allocated memory
            Marshal.FreeHGlobal(m_Buffer)
            m_Buffer = IntPtr.Zero
        End If

    End Sub

    Public Sub FindAllFiles(ByVal szDriveLetter As String)

        Dim usnRecord As USN_RECORD
        Dim mft As MFT_ENUM_DATA
        Dim dwRetBytes As Integer
        Dim cb As Integer

        ' This shouldn't be called more than once.
        If m_Buffer.ToInt32 <> 0 Then
            Console.WriteLine("invalid buffer")
            Exit Sub
        End If

        ' Assign buffer size
        m_BufferSize = 65536

        ' Allocate a buffer to use for reading records.
        m_Buffer = Marshal.AllocHGlobal(m_BufferSize + 8)

        ' Open the volume handle 
        m_hCJ = OpenVolume(szDriveLetter)

        ' Check if the volume handle is valid.
        If m_hCJ = INVALID_HANDLE_VALUE Then
            Console.WriteLine("Couldn't open handle to the volume.")
            Cleanup()
            Exit Sub
        End If

        mft.StartFileReferenceNumber = 0
        mft.LowUsn = 0
        mft.HighUsn = 0

        Do
            If DeviceIoControl(m_hCJ, FSCTL_ENUM_USN_DATA, mft, Marshal.SizeOf(mft), m_Buffer, m_BufferSize, dwRetBytes, IntPtr.Zero) Then
                cb = dwRetBytes
                ' Pointer to the first record
                Dim pUsnRecord As New IntPtr(m_Buffer.ToInt32() + 8)

                While (dwRetBytes > 8)
                    ' Copy pointer to USN_RECORD structure.
                    usnRecord = Marshal.PtrToStructure(pUsnRecord, usnRecord.GetType)
                    ' The filename within the USN_RECORD.
                    Dim FileName As String = Marshal.PtrToStringUni(New IntPtr(pUsnRecord.ToInt32() + usnRecord.FileNameOffset), _
                                                                    usnRecord.FileNameLength / 2)


                    If usnRecord.FileAttributes And vbDirectory Then

                        ' directories
                    Else

                        ' files
                        If FileName.Contains(".csv") Then
                            Dim path As String = PathFromFrn(usnRecord.ParentFileReferenceNumber) & "\" & FileName
                            Console.WriteLine(szDriveLetter & path)
                        End If

                    End If

                    ' Pointer to the next record in the buffer.
                    pUsnRecord = New IntPtr(pUsnRecord.ToInt32() + usnRecord.RecordLength)

                    dwRetBytes -= usnRecord.RecordLength

                End While

                ' The first 8 bytes is always the start of the next USN.
                mft.StartFileReferenceNumber = Marshal.ReadInt64(m_Buffer, 0)

            Else

                Exit Do

            End If ' DeviceIoControl

            Application.DoEvents()
        Loop Until cb <= 8

        '// cleanup
        Cleanup()
        Console.WriteLine("Done")

    End Sub

   
    Private Function PathFromFrn(ByVal Id As Long) As String

        Dim fOk As Integer
        Dim FileName As String = Nothing
        Dim UnicodeString As UNICODE_STRING
        Dim ObjAttributes As OBJECT_ATTRIBUTES
        Dim IoStatusBlock As IO_STATUS_BLOCK
        Dim hFile As IntPtr ' out handle 
        Dim Buffer As IntPtr = Marshal.AllocHGlobal(4096) ' Raw buffer
        Dim Refptr As IntPtr = Marshal.AllocHGlobal(8) ' 8 byte FileID
        Dim ObjAtt As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(ObjAttributes)) 'pointer to the unicode string struct

        ' pointer>>fileid
        Marshal.WriteInt64(Refptr, 0, Id)

        ' 8 byte file id
        UnicodeString.Length = 8
        UnicodeString.MaximumLength = 8
        UnicodeString.Buffer = Refptr

        ' copy unicode structure to pointer
        Marshal.StructureToPtr(UnicodeString, ObjAtt, True)

        ' InitializeObjectAttributes Macro
        ObjAttributes.Length = Marshal.SizeOf(ObjAttributes)
        ObjAttributes.ObjectName = ObjAtt
        ObjAttributes.RootDirectory = m_hCJ
        ObjAttributes.Attributes = OBJ_CASE_INSENSITIVE Or OBJ_KERNEL_HANDLE

        fOk = NtCreateFile(hFile, GENERIC_READ, ObjAttributes, IoStatusBlock, 0, 0, _
                           FILE_SHARE_READ Or FILE_SHARE_WRITE, _
                           FILE_OPEN, FILE_OPEN_BY_FILE_ID Or FILE_OPEN_FOR_BACKUP_INTENT, 0, 0)

        If fOk <> INVALID_HANDLE_VALUE Then

            fOk = NtQueryInformationFile(hFile, IoStatusBlock, Buffer, 4096, FileNameInformationClass)

            If fOk = 0 Then

                ' The first 4 bytes is the length
                Dim FileLength As Integer = Marshal.ReadInt32(Buffer, 0)
                ' The filename is just after the first 4 bytes.
                FileName = Marshal.PtrToStringUni(New IntPtr(Buffer.ToInt32() + 4), FileLength / 2)

            End If

        End If
        ' free allocated memory and handles
        CloseHandle(hFile)
        Marshal.FreeHGlobal(Buffer)
        Marshal.FreeHGlobal(ObjAtt)
        Marshal.FreeHGlobal(Refptr)

        Return FileName

    End Function

End Class

Open in new window

Top Expert 2010
Commented:
My bad looked over this again and I forgot to populate mft.HighUsn so not all results would be returned replace the procedure with this updated one and you can play around with it correctly.

Public Sub FindAllFiles(ByVal szDriveLetter As String)

        Dim usnRecord As USN_RECORD
        Dim mft As MFT_ENUM_DATA
        Dim dwRetBytes As Integer
        Dim cb As Integer

        ' This shouldn't be called more than once.
        If m_Buffer.ToInt32 <> 0 Then
            Console.WriteLine("invalid buffer")
            Exit Sub
        End If

        ' Assign buffer size
        m_BufferSize = 65536

        ' Allocate a buffer to use for reading records.
        m_Buffer = Marshal.AllocHGlobal(m_BufferSize + 8)

        ' Open the volume handle 
        m_hCJ = OpenVolume(szDriveLetter)

        ' Check if the volume handle is valid.
        If m_hCJ = INVALID_HANDLE_VALUE Then
            Console.WriteLine("Couldn't open handle to the volume.")
            Cleanup()
            Exit Sub
        End If

        mft.StartFileReferenceNumber = 0
        mft.LowUsn = 0
        mft.HighUsn = Long.MaxValue

        Do
            If DeviceIoControl(m_hCJ, FSCTL_ENUM_USN_DATA, mft, Marshal.SizeOf(mft), m_Buffer, m_BufferSize, dwRetBytes, IntPtr.Zero) Then
                cb = dwRetBytes
                ' Pointer to the first record
                Dim pUsnRecord As New IntPtr(m_Buffer.ToInt32() + 8)

                While (dwRetBytes > 8)
                    ' Copy pointer to USN_RECORD structure.
                    usnRecord = Marshal.PtrToStructure(pUsnRecord, usnRecord.GetType)
                    ' The filename within the USN_RECORD.
                    Dim FileName As String = Marshal.PtrToStringUni(New IntPtr(pUsnRecord.ToInt32() + usnRecord.FileNameOffset), _
                                                                    usnRecord.FileNameLength / 2)

                    'TODO: Stuff here
                    If usnRecord.FileAttributes And vbDirectory Then
                        ' directory
                    Else
                        ' files
                        If FileName.ToLower.Contains(".csv") Then
                            Dim path As String = PathFromFrn(usnRecord.ParentFileReferenceNumber) & "\" & FileName
                            Console.WriteLine(szDriveLetter & path)
                        End If

                    End If

                    ' Pointer to the next record in the buffer.
                    pUsnRecord = New IntPtr(pUsnRecord.ToInt32() + usnRecord.RecordLength)

                    dwRetBytes -= usnRecord.RecordLength

                End While

                ' The first 8 bytes is always the start of the next USN.
                mft.StartFileReferenceNumber = Marshal.ReadInt64(m_Buffer, 0)

            Else

                Exit Do

            End If ' DeviceIoControl

            Application.DoEvents()
        Loop Until cb <= 8

        '// cleanup
        Cleanup()
        Console.WriteLine("Done")

    End Sub

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial