VB.NET - Doing work in separate thread to prevent Form from hanging
I have a really simple form with a button that fires a Sub I created which gathers data from ActiveDirectory and adds it to an Excel Sheet.
The problem is, when I click this button the whole form hangs. So I figured the operation that gathers the data and adding it to the Excel sheet should be run in it's own thread, so that the form won't hang. Possibly it would be great to add a progressbar as well. The progressbar however is located at the Main userform that starts up once the projects is run.
What do I need to do in order to get this the way I want?
Edit: Added some of my code. I've got one MainForm.vb and one CodeFile.vb. I want most of the code in the CodeFile.vb so it's tidier.
MainForm.vb
Imports User_edit.CodeFile
Imports System.ComponentModel
Public Class MainForm
Private Sub btnImportData_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnImportData.Click
If MyBackgroundWorker.IsBusy <> True Then
MyBackgroundWorker.RunWorkerAsync()
End If
End Sub
Private Sub BackgroundWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles MyBackgroundWorker.DoWork
ExportADUsers()
End Sub
Private Sub BackgroundWorker_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles MyBackgroundWorker.ProgressChanged
statusBarLabel.Text = (e.ProgressPercentage.ToString)
End Sub
Private Sub BackgroundWorker_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles MyBackgroundWorker.RunWorkerCompleted
statusBarLabel.Text = "Finished"
End Sub
End Class
CodeFile.vb
Imports System.DirectoryServices
Imports System.ComponentModel
Imports System.Threading
Module CodeFile
Public Sub ExportADUsers()
MainForm.MyBackgroundWorker.WorkerReportsProgress = True
MainForm.MyBackgroundWorker.WorkerSupportsCancellation = True
Dim i As Integer
Dim objRootDSE, strRoot, strfilter, strAttributes, strScope
objRootDSE = GetObject("LDAP://RootDSE")
strRoot = objRootDSE.GET("DefaultNamingContext")
strfilter = "(&(objectCategory=Person)(objectClass=User))"
strAttributes = "mail,userPrincipalName,givenName,sn," & _
"initials,displayName,physicalDeliveryOfficeName," & _
"telephoneNumber,mail,wWWHomePage,profilePath," & _
"scriptPath,homeDirectory,homeDrive,title,department," & _
"company,manager,homePhone,pager,mobile," & _
"facsimileTelephoneNumber,ipphone,info," & _
"streetAddress,postOfficeBox,l,st,postalCode,c"
'Scope of the search. Change to "onelevel" if you didn't want to search child OU's
MainForm.statusBarLabel.Text = "Collecting data"
strScope = "subtree"
Dim cn, cmd, rs
cn = CreateObject("ADODB.Connection")
cmd = CreateObject("ADODB.Command")
cn.open("Provider=ADsDSOObject;")
cmd.ActiveConnection = cn
cmd.commandtext = "<LDAP://" & strRoot & ">;" & strfilter & ";" & _
strAttributes & ";" & strScope
rs = cmd.EXECUTE
Dim objExcel, objWB, objSheet
开发者_开发问答 objExcel = CreateObject("Excel.Application")
objWB = objExcel.Workbooks.Add
objSheet = objWB.Worksheets(1)
For i = 0 To rs.Fields.Count - 1
MainForm.MyBackgroundWorker.ReportProgress(i * 10)
objSheet.Cells(1, i + 1).Value = rs.Fields(i).Name
objSheet.Cells(1, i + 1).Font.Bold = True
Next
Dim strExportFile
strExportFile = "C:\users\vsando\desktop\export.xls"
objSheet.Range("A2").CopyFromRecordset(rs)
objSheet.SaveAs(strExportFile)
'Clean up
rs.Close()
cn.Close()
objSheet = Nothing
objWB = Nothing
objExcel.Quit()
objExcel = Nothing
End Sub
Notice the ExportFromAD
Sub I've got in the CodeFile.vb
. This is what is actually doing the work. In the For each loop that adds data to the Excel is where I've put the MainForm.MyBackgroundWorker.ReportProgress(i * 10)
.
Problem is, it doesn't actually update the label on the form. Which I find pretty weird because the form isn't really hanging or anything. Is it trying to access a different thread or something? Meaning, the form is run on it's own thread which can't be accessed from my second thread?
Your best bet is to use a BackgroundWorker
since this class is designed for this precise use-case.
This also allows you to call back to the form to update the status bar.
The BackgroundWorker class is what you need to use. For passing data back to the form's progress bar you set the WorkerReportsProgress
property to true and handle the ProgressChanged
event to set the progress bar's value. From the long running method you can send progress like this:
backgroundworker.ReportProgress(10)
精彩评论