Reputation: 462
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
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