This will watch for the Arrival and Removal of USB drives, and report the DriveLetter, Volume Label and device serial number. It raises 2 Events for your convenience:
Public Event DeviceAdded(sender As Object, e As USBWatcherEventArgs)
Public Event DeviceRemoved(sender As Object, e As USBWatcherEventArgs)
This only watches for a new drive added/removed, it will not detect things like a CD being ejected or inserted, or inserting media into a card reader slot.
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
How to Implement 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
The Device Watcher runs on a different thread so if you want to post a notice to a control, you will have to use a delegate:
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
Then from the Device Added event, for example:
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 would be the same except "Removed" as the third param in the message text.
USBWatcher also has a StopWatching
method to turn off the watcher for a time, and StartWatching
to start and restart it. Your app should call StopWatching
when the app ends to prevent COM errors; just add watcher.StopWatching
in the form close event.
This does what you want - raise events when removable media is inserted and return the information about them - just not exactly how you wanted it. It uses tried and tested WMI rather than the Win8 method, which is as yet has only sparse documention on MSDN and would only work on Win8.