我是初学者,如果这是一个愚蠢的问题,请原谅我......我有一个机器列表,然后我想要提取 WMI 信息(在本例中是操作系统信息),然后更新表单中的 ListView。我正在寻找最简单的方法来以有效的方式使以下代码多线程,也许使用任务工厂?根据我的阅读,似乎每台计算机都需要放入一个集合中,然后每个线程都需要更新集合中的对象?
这是我的原始代码:
Imports System.Management
Public Class Form1
Public PC As New pc
Public WMI As New WMIConnect
Public i As Integer = 1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
With lv_Inventory
.Visible = True
.UseCompatibleStateImageBehavior = False
.View = View.Details
.Scrollable = True
.Sort()
.HideSelection = False
.FullRowSelect = True
.GridLines = True
.AllowColumnReorder = True
.Columns.Add("Name", 100)
.Columns.Add("LastBoot", 150)
.Columns.Add("OS", 125)
.Columns.Add("Version", 100)
.Columns.Add("SP", 75)
End With
For Each Machine As ListViewItem In lv_Machines.SelectedItems
lv_Inventory.Items.Add(Machine.Text)
'Dim PC As New pc
WMI.WMIConnect(Machine.Text, tb_user.Text, tb_pass.Text)
PC.Name = Machine.Text
GetOS()
lv_Inventory.Items(i - 1).SubItems.Add(PC.LastBootTime)
lv_Inventory.Items(i - 1).SubItems.Add(PC.OperatingSystem)
lv_Inventory.Items(i - 1).SubItems.Add(PC.OSVersion)
lv_Inventory.Items(i - 1).SubItems.Add(PC.ServicePack)
i += 1
Next
End Sub
Public Sub GetOS()
On Error Resume Next
Dim lastboot As String = Nothing
Dim m As ManagementObject
Dim queryCollection As ManagementObjectCollection
queryCollection = wmi.wmiQuery _
("SELECT Caption, Lastbootuptime, version, csdversion, csname, SystemDirectory FROM Win32_OperatingSystem")
If queryCollection Is Nothing Then
' Me.LostConnection()
Exit Sub
End If
For Each m In queryCollection
pc.Hostname = UCase(m("csname"))
pc.LastBootTime = m("LastBootUpTime")
pc.OSVersion = m("Version")
pc.ServicePack = m("CSDVersion")
pc.OperatingSystem = m("Caption")
Next
End Sub
End Class
Public Class WMIConnect
Public Shared wmiScope As Management.ManagementScope
Public Shared RegScope As Management.ManagementScope
Public Sub WMIConnect(ByVal Machine As String, ByVal Username As String, ByVal Pass As String)
Dim wmiConnectionOptions As New Management.ConnectionOptions
With wmiConnectionOptions
.Impersonation = System.Management.ImpersonationLevel.Impersonate
.Timeout = New TimeSpan(0, 0, 10)
.Authentication = System.Management.AuthenticationLevel.Packet
.Username = Username
.Password = Pass
.EnablePrivileges = True
End With
Try
Dim wmiScope As New Management.ManagementScope("\\" & _
Machine & "\root\cimv2", wmiConnectionOptions)
wmiScope.Connect()
Dim RegScope As New Management.ManagementScope("\\" & _
Machine & "\root\default:StdRegProv", wmiConnectionOptions)
RegScope.Connect()
Catch e As Exception
MsgBox("Error: " & e.ToString)
End Try
End Sub
Public Function wmiQuery(ByVal QueryString As String) As Management.ManagementObjectCollection
Try
Dim query As Management.ObjectQuery
query = New Management.ObjectQuery(QueryString)
Dim searcher As Management.ManagementObjectSearcher
searcher = New Management.ManagementObjectSearcher(wmiScope, query)
Dim queryCollection As Management.ManagementObjectCollection
queryCollection = searcher.Get()
Return queryCollection
Catch
Dim queryCollection As Management.ManagementObjectCollection = Nothing
Return queryCollection
End Try
End Function
End Class
Public Class pc
Public Shared Name As String
Public Hostname As String
Public OperatingSystem As String
Public ServicePack As String
Public OSVersion As String
Public LastBootTime As String
End Class
以下是并行任务中更新后的代码:
Parallel.ForEach(lv_Machines.SelectedItems.Cast(Of Object), _
Sub(machine)
' ... work with currentElement
Try
Dim WMI As New wmiConnection
Dim PC As New pc
Dim i As Integer = 1
Dim int As Integer = 1
tb_Log.Text += "working on machine " & machine.text & vbCrLf
WMI.WMIConnect(machine.Text, tb_user.Text, tb_pass.Text)
tb_Log.Text += "WMI connect to machine " & machine.text & vbCrLf
PC.Name = machine.Text
GetOS(PC, WMI)
GetHardware(PC, WMI)
GetNetwork(PC, WMI)
With lv_Inventory
.Items.Add(PC.Name.ToString)
With .Items(.Items.Count - 1).SubItems
.Add(PC.LastBootTime.ToString & "")
.Add(PC.OperatingSystem.ToString & "")
.Add(PC.OSVersion.ToString & "")
.Add(PC.ServicePack.ToString & "")
.Add(PC.SerialNumber.ToString & "")
.Add(PC.ChassisType.ToString & "")
.Add(PC.CPU.ToString & "")
.Add(PC.PhysicalMemory.ToString & "")
.Add(PC.Model.ToString & "")
.Add(PC.Manufacturer.ToString & "")
.Add(PC.MacAddress.ToString & "")
End With
End With
tb_Log.Text += "Processing: " & machine.Text & " Thread ID: " & Thread.CurrentThread.ManagedThreadId & vbCrLf
Catch ex As Exception
tb_Log.Text += "Error: " & ex.ToString & vbCrLf
End Try
End Sub)
我认为使用新代码我现在可能正在与线程同步问题作斗争......跟踪日志似乎表明 WMI 对象似乎被上一个/下一个任务覆盖,因此我的列表视图中的最终信息最终只是与我们创建并连接到的最后一个 PC 对象具有相同的结果集(遇到竞争条件)。我是否需要在 WMI 连接上执行同步锁定并在完成获取数据后释放它?如果是这样,那么它是否比非多线程 for/each 快得多,因为我需要同时锁定 WMI 对象和 PC 对象?我基本上只需要一种方法来获取计算机列表并连接到每台计算机并以多线程方式获取详细信息。这是我的跟踪日志的输出:
- 在机器 col-01 上工作
- 在机器 col-02 上工作
- WMI 连接机器 WI-01
- 获取 cpu 信息...对于 WI-01
- 获取驱动器信息...对于 WI-01
- 获取外壳类型。用于 WI-01
- 处理:WI-01 线程 ID:10
- WMI 连接到机器 sta-01
- WMI 连接到机器 sta-02
- 获取 cpu 信息... WI-01
- 获取 cpu 信息... WI-01
- 获取驱动器信息... WI-01
- 获取驱动器信息... WI-01
- 获取外壳类型。用于 WI-01
- 获取外壳类型。用于 WI-01
- 处理:sta-01 线程 ID:9
- 处理:sta-02 线程 ID:6