Baku Bakar
Baku Bakar

Reputation: 462

Recursive folder search gets during code execution 'not responding'

I have one button, one textbox and a listview. What I try is just recursivly loop trough my filesystem. For example, C:\Windows. Of course, the app needs go through hundred thousand directory's, this needs CPU and RAM performance. But the problem is, that my application window gets to a 'not responding' state during this time.

Can I avoid this behaviour by optimizing my code?

My VB.NET code:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim rootDir As String = String.Empty
    Dim di As DirectoryInfo

    rootDir = TextBox1.Text.Trim()
    di = New DirectoryInfo(rootDir)

    ' start recursively loop trough filesystem
    recur_getdirectories(di, 0)
End Sub

Public Sub recur_getdirectories(ByVal di As DirectoryInfo, ByRef int As Integer)
    int = int + 1
    Try
        For Each directory As DirectoryInfo In di.GetDirectories()
            ListView1.Items.Add(directory.FullName)
            Label1.Text = int
            recur_getdirectories(directory, int)
        Next
    Catch ex As UnauthorizedAccessException
    
    End Try
End Sub

Upvotes: 0

Views: 68

Answers (1)

Caius Jard
Caius Jard

Reputation: 74605

Can I avoid this behaviour by optimizing my code? - a good first step will be to gain understanding on why the "not responding" occurs and how windows works.

When you launch a windows forms program, a queue is established and a thread is dedicated to processing all the messages in the queue. If the user clicks something, presses key etc, then messages are posted into the queue and the thread gets to work on processing them. If windows ever notices that the queue is growing longer and longer, and messages are no longer being processed, it marks the window as "not responding".

When a message is being processed (a button click) it is the thread that normally consumes the queue that starts off in your code's X_Click button event handler. If you write your code in such a way that it will take seconds (or minutes/hours.. or even longer) for that thread to finish your code and go back to what it was doing (processing the queue) then you will definitely cause a not-responding window

This is what you've done; enumerating all the directories on the hard disk takes a long time. While the thread is doing that it isn't processing any window messages - no clicks will be processed, no UI updates will occur, "not responding" will appear if you tryand interact with the window because windows will notice that the messages you're causing aren't being consumed

You need to use a mechanism that will let the thread go back to what it was doing before, and given that there isn't an async/await route available to you I'd be tempted to go with jdigital's suggestion to use a backgroundworker because it makes things easier, as long as you use the progress reporting mechanism to trigger updates. This is because the worker does the work on a background thread, but windows controls must only be accessed by the thread that created them. Backgroundworker will manage that for you; in short - never access a windows control from within DoWork


Private X_Click() Handles X.Xlixk()
    _bgw.RunWorkerAsync(TextBox1.Text.Trim()) 'pass the start directory in as an argument
End Sub

Private Sub Bgw_DoWork(sender As Object, e As DoWorkEventArgs) Handles _bgw.DoWork
    Dim rootDir As String = String.Empty
    Dim di As DirectoryInfo

    rootDir = e.Argument.ToString()
    di = New DirectoryInfo(rootDir)

    ' start recursively loop trough filesystem
    Recur_GetDirectories(di)
End Sub

Public Sub Recur_GetDirectories(ByVal di As DirectoryInfo)
    Try
        For Each directory As DirectoryInfo In di.GetDirectories()
            _bgw.ReportProgress(0, directory.FullName) 'pass the directory path in the progress report. Do NOT access windows controls from inside DoWork; it does not run on the windowing thread
            Recur_GetDirectories(directory)
        Next
    Catch ex As UnauthorizedAccessException
    
    End Try
End Sub

Private Sub Bgw_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles _bgw.ProgressChanged
    ListView1.Items.Add(e.UserState.ToString()) ' only access windows controls in the ProgressChanged event; it runs on the window thread
End Sub

The next thing you'll probably notice with this, is that you'll be posting tens of thousands of items into the listview, and it might well spend a huge amount of time redrawing itself. Enter Jimi's advice on how to load huge numbers of things into a windows control without too much of a performance hit (i.e. if you load 100 items into a control and it redraws itself 100 times, you can achieve better performance by turning off interim updates, so it doesn't spend large amounts of time endlessly redrawing itself only to be invalidated shortly after and need to redraw again)

Upvotes: 1

Related Questions