【问题标题】:How do I make a vb.net multi-threaded WMI query application如何制作 vb.net 多线程 WMI 查询应用程序
【发布时间】:2012-11-19 04:20:36
【问题描述】:

我是一个初学者,所以如果这是一个愚蠢的问题,请原谅我......我有一个机器列表,然后我想提取 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 对象?我基本上只需要一种方法来获取计算机列表并连接到每台计算机并以多线程方式获取详细信息。这是我的跟踪日志的输出:

  1. 在机器 col-01 上工作
  2. 在机器 col-02 上工作
  3. WMI 连接到机器 WI-01
  4. 正在获取 cpu 信息...用于 WI-01
  5. 正在获取驱动器信息...对于 WI-01
  6. 获取机箱类型。用于 WI-01
  7. 处理:WI-01 线程 ID:10
  8. WMI 连接到机器 sta-01
  9. WMI 连接到机器 sta-02
  10. 正在获取 cpu 信息...WI-01
  11. 正在获取 cpu 信息...WI-01
  12. 正在获取驱动器信息...WI-01
  13. 正在获取驱动器信息...WI-01
  14. 获取机箱类型。用于 WI-01
  15. 获取机箱类型。用于 WI-01
  16. 处理:sta-01 线程 ID:9
  17. 处理:sta-02 线程 ID:6

【问题讨论】:

    标签: .net vb.net multithreading wmi


    【解决方案1】:

    嗯,可能最简单的方法是使用 Threaded.ForEach 。您可以在此处找到详细信息:

    http://msdn.microsoft.com/en-us/library/dd460720(v=vs.100).aspx

    这需要 .net4 或更高版本。

    【讨论】:

    • 好的,所以在进一步挖掘之后,尽管线程化的 forEach 选项没有按预期工作。每次执行 ForEach 时,保存正在查询的每台机器的值的我的 PC 对象/类最终都会被覆盖,因此我的 ListView 最终只复制了一组数据,无论 ForEach 运行了多少次。基于此,我认为我需要创建一个 PC 对象集合(列表中的每台机器 1 个),然后将数据添加到 PC 集合中每个项目的线程化 foreach 语句中的属性中?这是线程安全的方式吗?
    • 您在 ForEach lambda 中使用的所有变量都必须是该 lambda 独有的。这意味着您的变量 PC、WMI 和 i 都需要在 lambda 中声明。此外,如果您正在处理项目并将它们添加到集合中,则应在添加每个项目时对集合进行 SysLock。
    • 好的,所以我修改了我的代码以将 PC、WMI 和 i 都放入 forEach lambda,但它仍然在我的列表视图中显示所有相同的信息。我添加了一些跟踪日志信息,发现 Foreach 似乎在不同时间执行了 lamda 中的所有代码?我猜它是按这种方式设计的,但问题是我的 WMI 连接最终报告说他们为一台机器提取了信息,但机器是错误的,因为那不是首先打开 WMI 连接的线程. 这是我的跟踪的样子:
    • 我在原始帖子中添加了一个代码 sn-p 以显示更新后的 foreach 的样子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多