VB.NET - 在单独的线程中工作以防止表单挂起
我有一个非常简单的表单,其中有一个按钮可以触发我创建的子程序,该子程序从 ActiveDirectory 收集数据并将其添加到 Excel 工作表中。
问题是,当我单击此按钮时,整个表单都会挂起。所以我认为收集数据并将其添加到 Excel 工作表的操作应该在它自己的线程中运行,这样表单就不会挂起。如果添加一个进度条可能会很棒。 然而,进度条位于项目运行后启动的主用户窗体中。
我需要做什么才能达到我想要的效果?
编辑:添加了我的一些代码。我有一个 MainForm.vb 和一个 CodeFile.vb。 我想要 CodeFile.vb 中的大部分代码,这样它会更整洁。
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
请注意 CodeFile.vb
中的 ExportFromAD
Sub。这就是实际做的工作。在将数据添加到 Excel 的 For every 循环中,我放置了 MainForm.MyBackgroundWorker.ReportProgress(i * 10)
。
问题是,它实际上并没有更新表单上的标签。我觉得这很奇怪,因为表格并没有真正悬挂或任何东西。它是否试图访问不同的线程或其他什么?意思是,表单在它自己的线程上运行,无法从我的第二个线程访问?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
最好的选择是使用
BackgroundWorker
因为此类是专为这种精确的用例而设计的。这还允许您回调表单以更新状态栏。
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.
您需要使用 BackgroundWorker 类。要将数据传递回表单的进度栏,请将
WorkerReportsProgress
属性设置为 true 并处理ProgressChanged
事件以设置进度栏的值。从长时间运行的方法中,您可以像这样发送进度: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 theProgressChanged
event to set the progress bar's value. From the long running method you can send progress like this: