使用DeviceWatcher监控USB驱动器并检索设备信息?

时间:2014-07-24 22:09:50

标签: .net vb.net winforms usb device

我是 WinForms 开发人员,我已经知道如何使用WMI监控连接或断开连接的USB,但是之前我发现了适用于Modern Windows Apps的DeviceWatcher类,该类似乎第一次感兴趣'因为它似乎是一个非常改进和有效的替代品,可以替换所有那些解释如何通过互联网监控驱动器的旧'WMI代码,但直到昨天(感谢this post)我避风港想知道如何在WinForms项目中使用DeviceWatcher,但现在我在WinForms项目中使用了DeviceWatcher。

问题是,我可能错了,但我认为这不是我所期望的,只是我找不到任何关于DeviceWatcher的文档(只有上面的MSDN示例),我找不到检索驱动事件的必要信息的方法,我试图处理DeviceWatcher的所有事件,在调试控制台中打印出参数中包含的所有数据,希望找到可以帮助我的东西。 ..但不是,我非常坚持使用 DeviceWatcher 类,我无法想象如何进行。

当我连接或断开USB时,我只看到两件事,硬件ID和'InterfaceEnabled'属性(我不知道它是否决定了设备可用性),没有什么有趣的。

  

我所取得的成就:

·检索硬件设备ID。

  

我想要完成的事情:

·在设备连接,断开连接和断开连接时检索设备类型(以区分USB和其他类型的设备)。

·在设备连接,断开连接和断开连接时,检索设备可用性(我的意思是设备是否可以读取/写入数据)。

·设备连接,断开连接和断开连接时检索设备字母。

·在设备连接,断开连接和断开连接时检索设备标签描述。

  

代码:

Public Class DeviceWatcher_Test

    Private WithEvents dw As DeviceWatcher = DeviceInformation.CreateWatcher

    ' It's suposed that these properties should exist in the "e.properties" on the "dw_updated" event?, not in my case.
    ' Dim props As String() = {"System.ItemNameDisplay", "System.Devices.ModelName", "System.Devices.Connected"}

    Private Sub Test() Handles MyBase.Load

        dw.Start()

    End Sub

    Private Sub dw_Added(ByVal sender As DeviceWatcher, ByVal e As DeviceInformation) _
    Handles dw.Added

        Dim sb As New System.Text.StringBuilder

        With sb
            .AppendLine("dw_added")
            .AppendLine("********")
            .AppendLine(String.Format("Interface ID.: {0}", e.Id))
            .AppendLine(String.Format("Friendly Name: {0}", e.Name))
            .AppendLine(String.Format("Is Enabled?..: {0}", e.IsEnabled))
        End With

        Debug.WriteLine(sb.ToString)

    End Sub

    Private Sub dw_Removed(ByVal sender As DeviceWatcher, ByVal e As DeviceInformationUpdate) _
    Handles dw.Removed

        Dim sb As New System.Text.StringBuilder

        With sb
            .AppendLine("dw_Removed")
            .AppendLine("**********")
            .AppendLine(String.Format("Interface ID:{0}", e.Id))

            For Each item As KeyValuePair(Of String, Object) In e.Properties
                .AppendLine(String.Format("TKey:{0}, TVal:{1} (TVal Type:{2})",
                                          item.Key, item.Value.ToString, item.Value.GetType.Name))
            Next

        End With

        Debug.WriteLine(sb.ToString)

    End Sub

    Private Sub dw_Updated(ByVal sender As DeviceWatcher, ByVal e As DeviceInformationUpdate) _
    Handles dw.Updated

        Dim sb As New System.Text.StringBuilder

        With sb
            .AppendLine("dw_Updated")
            .AppendLine("**********")
            .AppendLine(String.Format("Interface ID: {0}", e.Id))

            For Each item As KeyValuePair(Of String, Object) In e.Properties

                If item.Key.EndsWith("InterfaceEnabled", StringComparison.OrdinalIgnoreCase) Then
                    Dim Result As Boolean = CBool(item.Value)
                    ' I'm not sure whether the 'Result' value really determines this:
                    .AppendLine(String.Format("The device is accessible?:{0}", CStr(Result)))

                Else
                    .AppendLine(String.Format("TKwy:{0}, TVal:{1} (TVal Type:{2})",
                                              item.Key, item.Value.ToString, item.Value.GetType.Name))

                End If

            Next

        End With

        Debug.WriteLine(sb.ToString)

    End Sub

    Private Sub dw_Stopped(ByVal sender As DeviceWatcher, ByVal e As Object) _
    Handles dw.Stopped

        Dim sb As New System.Text.StringBuilder

        With sb
            .AppendLine("dw_Stopped")
            .AppendLine("**********")
            .AppendLine(String.Format("e:{1} (e Type:{2})",
                                      e.ToString, e.GetType.Name))

        End With

        Debug.WriteLine(sb.ToString)

    End Sub

    Private Sub dw_EnumerationCompleted(ByVal sender As DeviceWatcher, ByVal e As Object) _
    Handles dw.EnumerationCompleted

        If e IsNot Nothing Then

            Dim sb As New System.Text.StringBuilder

            With sb
                .AppendLine("EnumerationCompleted")
                .AppendLine("********************")
                .AppendLine(String.Format("e:{1} (e Type:{2})",
                                          e.ToString, e.GetType.Name))

            End With

            Debug.WriteLine(sb.ToString)

        End If

    End Sub

End Class

3 个答案:

答案 0 :(得分:5)

这将监视USB驱动器的到达和移除,并报告DriveLetter,卷标和设备序列号。它为您提供了2个活动:

Public Event DeviceAdded(sender As Object, e As USBWatcherEventArgs)
Public Event DeviceRemoved(sender As Object, e As USBWatcherEventArgs)

这只会监视添加/删除的新驱动器,它不会检测到弹出或插入的CD,或将介质插入读卡器插槽等。

Imports System.Management

Public Class USBWatcher

    ' alternate method to use one event with an extra
    ' event property to report the action
    'Public Enum WatcherActions
    '    DriveInserted = 1
    '    DriveRemoved = 2
    'End Enum

    ' USB device added/removed args
    Public Class USBWatcherEventArgs
        Inherits EventArgs

        'Public Property WatcherAction As WatcherActions
        Public Property DriveLetter As String
        Public Property VolumeName As String
        Public Property VolumeSerial As String

        Friend Sub New(drv As String, vol As String, ser As String)
            DriveLetter = drv
            VolumeName = vol
            VolumeSerial = ser
            'WatcherAction = act
        End Sub

    End Class

    Private WithEvents Watcher As ManagementEventWatcher

    Public Event DeviceAdded(sender As Object, e As USBWatcherEventArgs)
    Public Event DeviceRemoved(sender As Object, e As USBWatcherEventArgs)

    Private pnpCol As Dictionary(Of String, Management.ManagementObject)

    Public Sub New()

    End Sub

    Public Sub StartWatching()

        ' get USBs currently attached
        pnpCol = GetUSBDevices()

        Dim arriveQuery = New WqlEventQuery("Select * from Win32_DeviceChangeEvent")
        Watcher = New ManagementEventWatcher(arriveQuery)

        ' we are watching you
        Watcher.Start()

    End Sub

    Public Sub StopWatching()
        Watcher.Stop()

    End Sub

    Private Function GetUSBDevices() As Dictionary(Of String,
                        Management.ManagementObject)

        Dim col As New Dictionary(Of String, Management.ManagementObject)

        Dim moSearch As New Management.ManagementObjectSearcher("Select * from Win32_LogicalDisk")
        Dim moReturn As Management.ManagementObjectCollection = moSearch.Get


        For Each mo As Management.ManagementObject In moReturn
            'Console.WriteLine("====")
            'DebugProperties(mo)

            ' some USB external drives report as DriveType 3 (local disk), but are
            ' in fact removable.  So monitor all disk drives.
            If col.ContainsKey(mo("DeviceID").ToString) = False Then
                col.Add(mo("DeviceID").ToString, mo)
            End If

        Next

        Return col
    End Function

    Private inEvent As Boolean = False

    Private Sub arrive_EventArrived(ByVal sender As Object, 
                    ByVal e As System.Management.EventArrivedEventArgs) _
                    Handles Watcher.EventArrived

        If inEvent Then Exit Sub
        inEvent = True

        Dim col As Dictionary(Of String, Management.ManagementObject) = GetUSBDevices()

        Select Case col.Count
            Case Is > pnpCol.Count
                ' device arrived
                ProcessArrival(col)
            Case Is < pnpCol.Count
                ' device removed
                ProcessRemoval(col)
            Case Is = pnpCol.Count
                ' noise...this is a chatty rascal
        End Select

        inEvent = False

    End Sub

    Private Sub ProcessArrival(col As Dictionary(Of String,
               Management.ManagementObject))
        For Each kvp As KeyValuePair(Of String, 
                 Management.ManagementObject) In col
            If pnpCol.ContainsKey(kvp.Key) = False Then

                'Console.WriteLine("{0} {1} ", kvp.Key, kvp.Value)
                'DebugProperties(kvp.Value)

                Dim ea As New USBWatcherEventArgs(kvp.Value("DeviceID").ToString,
                                      SafeString(kvp.Value("VolumeName")),
                                      SafeString(kvp.Value("VolumeSerialNumber")))

                RaiseEvent DeviceAdded(Me, ea)

                'rebuild baseline for next event
                pnpCol = col

            End If
        Next

    End Sub

    Private Sub ProcessRemoval(col As Dictionary(Of String,
                    Management.ManagementObject))
        For Each kvp As KeyValuePair(Of String, 
                          Management.ManagementObject) In pnpCol
            If col.ContainsKey(kvp.Key) = False Then

                'Console.WriteLine("{0} {1} ", kvp.Key, kvp.Value)
                'DebugProperties(kvp.Value)

                Dim ea As New USBWatcherEventArgs(kvp.Value("DeviceID").ToString,
                                      SafeString(kvp.Value("VolumeName")),
                                      SafeString(kvp.Value("VolumeSerialNumber")))

                RaiseEvent DeviceRemoved(Me, ea)

                'rebuild baseline for next event
                pnpCol = col

            End If
        Next

    End Sub

    ' lots of things can be NOTHING depending on the manufacturer's
    ' attention to detail.  try to avoid NRE
    Private Function SafeString(obj As Object) As String

        If obj.GetType Is GetType(String) Then
            Return CType(obj, String)
        Else
            If obj IsNot Nothing Then
                Return obj.ToString
            Else
                Return "???"
            End If
        End If

    End Function

    ' debug tool to poll a management object to get the properties and values
    Private Sub DebugProperties(mo As Management.ManagementObject)

        For Each pd As PropertyData In mo.Properties
            If pd.Value IsNot Nothing Then
                Console.WriteLine("{0} {1}", pd.Name,
                                  If(pd.Value IsNot Nothing,
                                     pd.Value.ToString,
                                     "Nothing"))
            End If

        Next
    End Sub

End Class

如何实施USBWatcher

' local variable to catch events
Private WithEvents watcher As USBWatcher

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    watcher = New USBWatcher
    watcher.StartWatching()     ' or add to a button click
    ' I DONT have it starting automatically when you create the watcher

End Sub

设备观察程序在不同的线程上运行,因此如果要向控件发布通知,则必须使用委托:

Delegate Sub AddListItem(text As String)
Private myDelegate As AddListItem = New AddListItem(AddressOf AddNewListItem)

Private Sub AddNewListItem(text As String)
    myListBox.Items.Add(text)
End Sub

然后从Device Added事件中,例如:

Private Sub watcher_DeviceAdded(sender As Object, 
            e As USBWatcher.USBWatcherEventArgs) Handles watcher.DeviceAdded

    Console.Beep()

    Dim msg As String = String.Format("Drive {0} ({1})   {2}", 
                                       e.DriveLetter,
                                       e.VolumeName, "Inserted")
    If myListBox.InvokeRequired Then
        myListBox.Invoke(myDelegate, New Object() {msg})
    Else
        myListBox.Items.Add(msg)
    End If

End Sub

除了“已删除”作为消息文本中的第三个参数,DeviceRemoved将是相同的。

USBWatcher 还有一种StopWatching方法可以暂时关闭观察程序,StartWatching可以启动并重新启动它。应用程序结束时,您的应用应致电StopWatching以防止出现COM错误;只需在表单关闭事件中添加watcher.StopWatching

这可以满足你的需求 - 在插入可移动媒体时引发事件并返回有关它们的信息 - 只是不完全你想要它的方式。它使用久经考验的WMI而不是Win8方法,它在MSDN上只有稀疏文档,只能在Win8上运行。

答案 1 :(得分:4)

如果插入了笔式驱动器,您可以使用WMI来检测。 您必须添加对dotnet框架的System.Management dll的引用。

您可以获取Devide详细信息:

Public Function SerialNO(ByVal DriveNameWithColon As String) As String
    Dim disk As ManagementObject = New ManagementObject("win32_logicaldisk.deviceid='" + DriveNameWithColon + "'")
    disk.Get()
    Return disk("VolumeSerialNumber").ToString()
End Function

答案 2 :(得分:4)

您可以从界面ID获取PnPObject,这将为您提供更多设备特定信息。

public static IAsyncOperation<PnpObject> CreateFromIdAsync(
  PnpObjectType type, 
  string id, 
  IEnumerable<string> requestedProperties
)

似乎非常有趣的fellah。您有几个不同的选项来为 PnpObjectType

指定

DeviceInterface | deviceInterface - PnpObject表示设备接口。

DeviceContainer | deviceContainer - PnpObject表示设备容器。

设备 | device -PnpObject代表一个设备。

DeviceInterfaceClass | deviceInterfaceClass - PnpObject表示设备接口类。

id实际上是您已收到的接口ID。

另请注意,您有可以要求的属性列表( requstedProperties 参数):http://msdn.microsoft.com/en-us/library/hh464997.aspx#ListOfCanonicalProperties

对你而言,这并不是太多,但不要害怕。其中有很多:http://msdn.microsoft.com/en-us/library/ff553416.aspx

如果检查devpkey.h,您可以找到有趣的属性,例如:

  • DEVPKEY_Device_DevType
  • DEVPKEY_Storage_Removable_Media
  • DEVPKEY_Storage_Portable
  •   -

有些东西是设备specfc,有些则不是。您可以通过 devmgmt.msc (详细信息 - &gt;属性)获得更多属性。注册表中还有很多东西。统一设备属性模型讨论了这一点:http://msdn.microsoft.com/en-us/library/ff553515(v=vs.85).aspx

PnP api有以下方法:

Await DeviceInformation.FindAllAsync()
Await Pnp.PnpObject.FindAllAsync(Pnp.PnpObjectType.DeviceContainer, ...)
Await Pnp.PnpObject.FindAllAsync(Pnp.PnpObjectType.Device, ...)
Await Pnp.PnpObject.FindAllAsync(Pnp.PnpObjectType.DeviceInterface, ...) 
Await Pnp.PnpObject.FindAllAsync(Pnp.PnpObjectType.DeviceInterfaceClass, ...)  
Await Pnp.PnpObject.CreateFromIdAsync(..., id, ...)

您可以阅读这篇文章:http://www.codeproject.com/Articles/458550/Device-enumeration-in-Windows

它包含所有内容,源代码和密钥,并显示如何将设备中的所有属性转储到文件中。这样您就可以看到USB记忆棒具有哪些属性。