Summary
An application that uses WMI to check for USB drives attached to a PC.
Where this started
So, one of the cooler pieces of schwag I managed to get hold of at tech-ed was this Internet Explorer 8 memory stick. Its design is neat, and though its capacity is only 2Gb, it makes a rather cool keychain. The problem with that is, of course, that it has my keys attached to it, which makes it a rather annoying thing to leave behind you in the office.
No prizes for guessing what happened yesterday.
Being rather annoyed with this lapse of memory, today I decided to write an application that would help me remember to pick up my keys. Since I always shut down my PC before I leave work, I figured that something that would stop me from shutting down while the keychain was still plugged in would be enough. Cue this application.
The sources for this application are available here.
… and all I need now is to find an RFID enabled washing machine running Windows Embedded so I don’t accidentally wash the memory stick with my trousers. Not.
How it works
The actual act of preventing a shutdown is quite easy using the Application’s SessionEnding event. While this doesn’t fire under all situations (it doesn’t get fired when the shutdown is invoked by the shutdown command line command, for example), it’s enough for me since I only need it for manual shutdowns. If we see that a device we’re interested is still plugged in, we set the Cancel property on the event’s SessionEndingCancelEventArgs to true, which stops the shutdown for us. The really interesting part is where we get the information about connected devices: WMI
Querying WMI for connected devices
The Win32_LogicalDisk class gives us all the information we could possibly need about our removable disks. In our case, we don’t need all of it – we’re just going to get the drive letter, the name of the drive (if any) and the serial number.
We’ll use the letter and the name to help us identify the drive, and the serial number to keep track of the drive between sessions. That way, we can identify the drive between sessions.
Win32_LogicalDisk also has a DriveType property which we can use. We’re concerned with DriveType 2, which represents removable media. Using the following gives us all the information we need:
1: RemovableDiskQuery = "Select Caption, VolumeName, VolumeSerialNumber from Win32_LogicalDisk where DriveType = 2";
2: ...
3: ...
4: ObjectQuery query = new ObjectQuery(RemovableDiskQuery);
5:
6: using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(query))
7: {
8: ManagementObjectCollection removables = searcher.Get();
9: ...
10: }
Caption is, incidentally, the drive letter.
Listening for WMI Events
So now we can grab a list of devices, which, while nice, isn’t enough. We also need to keep track of devices being plugged in and removed while the application is running.
In addition to the queries, the management namespace also provides support for WMI Events. We can hook into these using the ManagementEventWatcher class, and providing a query and an event handler.
WMI Queries are practically identical to other queries. The main difference is that they query against event information, and include a WITHIN clause, as follows:
SELECT * FROM __InstanceDeletionEvent WITHIN 10 WHERE TargetInstance ISA "Win32_LogicalDisk"
The WITHIN clause specifies the interval at which the event is polled. It’s important to keep this in mind when using these handlers. You don’t want hundreds of then going at once!
The event watchers are also simple to add:
1: deviceRemovedWatcher = new ManagementEventWatcher(RemovableDiskRemovedWatcherQuery);
2: deviceRemovedWatcher.EventArrived += (object sender, EventArrivedEventArgs e) =>
3: {
4: ManagementBaseObject targetInstance = e.NewEvent["TargetInstance"] as ManagementBaseObject;
5:
6: if (!IsRemovableDisk(targetInstance))
7: return;
8:
9: ...
10: ...
11: };
12:
13: deviceRemovedWatcher.Start();
Just remember to start them after you initialize them, and dispose them when you’re done. As we can see above, the event arguments will contain information about a disk in the “TargetInstance” property of the NewEvent. Just after that, we’re checking to see if the disk is actually removable; the WMI event described here fires whenever any sort of logical disk is added or removed.
Conclusion
WMI is an extremely interesting part of the Windows environment; it’s also incredibly useful, so I’d definitely recommend looking into it.
I hope this article is useful – should you have any comments, or ideas about how to improve this, please let me know!
Note that using “DriveType = 2” will also select Floppy drives. This is a “feature” that you may not encounter until your script runs on an older machine, and much shenanigans result.
Thanks for the information, Andy! Interesting “feature” there 🙂