#.net #vb.net #multithreading #backgroundworker
#.net #vb.net #многопоточность #backgroundworker
Вопрос:
У меня есть действительно простая форма с кнопкой, которая запускает созданный мной подраздел, который собирает данные из 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 = "(amp;(objectCategory=Person)(objectClass=User))"
strAttributes = "mail,userPrincipalName,givenName,sn," amp; _
"initials,displayName,physicalDeliveryOfficeName," amp; _
"telephoneNumber,mail,wWWHomePage,profilePath," amp; _
"scriptPath,homeDirectory,homeDrive,title,department," amp; _
"company,manager,homePhone,pager,mobile," amp; _
"facsimileTelephoneNumber,ipphone,info," amp; _
"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://" amp; strRoot amp; ">;" amp; strfilter amp; ";" amp; _
strAttributes amp; ";" amp; 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:usersvsandodesktopexport.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
Обратите внимание на ExportFromAD
подраздел, который у меня есть в CodeFile.vb
. Это то, что на самом деле выполняет работу. В цикле For each, который добавляет данные в Excel, я поместил MainForm.MyBackgroundWorker.ReportProgress(i * 10)
.
Проблема в том, что на самом деле это не обновляет метку в форме. Что я нахожу довольно странным, потому что форма на самом деле не висит или что-то в этом роде. Он пытается получить доступ к другому потоку или что-то в этом роде? То есть форма запускается в своем собственном потоке, к которому невозможно получить доступ из моего второго потока?
Комментарии:
1. Вы уже получили ответ в одном из своих тегов: самый простой способ сделать это — использовать
BackgroundWorker
компонент. Пример реализации на MSDN уже очень хорош.2. Из примера кода:
If (worker.CancellationPending = True) Then
… фу! И снова они позволяют стажеру написать пример.3. @Konrad: Фу, я согласен. Но более вероятно, что они сделали это в тщетной попытке «прояснить» код.
Ответ №1:
Лучше всего использовать BackgroundWorker
, поскольку этот класс разработан именно для этого варианта использования.
Это также позволяет вам перезванивать форме для обновления строки состояния.
Комментарии:
1. Пожалуйста, ознакомьтесь с обновленной темой. Я попытался добавить фонового рабочего. И я думаю, что это работает, но я не могу заставить индикатор выполнения или метку statusstrip фактически обновляться. Я попытался добавить точку останова, чтобы увидеть фактическое значение метки во время выполнения. И это фактически установлено. Это просто не отображается. Вот почему я считаю, что он пытается отобразить метку, которая находится в другом потоке?
Ответ №2:
Класс BackgroundWorker — это то, что вам нужно использовать. Для передачи данных обратно в индикатор выполнения формы вы устанавливаете для WorkerReportsProgress
свойства значение true и обрабатываете ProgressChanged
событие, чтобы установить значение индикатора выполнения. Из метода длительного выполнения вы можете отправлять прогресс следующим образом:
backgroundworker.ReportProgress(10)