Tuesday, June 12, 2007

Using Ruby & WMI to Detect a USB Drive

While I was away, a reader asked, "Anyone know how to monitor Mass Storage Insert events (eg. When someone inserts a USB drive) from Ruby?" So let's take a look at how to use Windows Management Instrumentation (WMI) to determine if a "USB Mass Storage Device" is inserted.

Microsoft says that WMI "is the primary management technology for Microsoft® Windows® operating systems." WMI is installed and already running on all recent versions of Windows, so we'll connect to it using the win32ole library's connect method:


require 'win32ole'
wmi = WIN32OLE.connect("winmgmts://")

Next, we'll call WMI's ExecQuery method to query the Win32_USBControllerDevice to obtain a collection of USB Controller Device objects:

devices = wmi.ExecQuery("Select * From Win32_USBControllerDevice")

Each device in the collection has a Dependent method that returns a string something like this:

\\CASABLANCA\root\cimv2:Win32_PnPEntity.DeviceID="USB\\ROOT_HUB20\\3&41601B64&0"

To extract the Device ID value, we want to remove the double-quotes and grab the portion of the string after the "=":

device_name = device.Dependent.gsub('"', '').split('=')[1]

Now we'll use that Device ID to query the Win32_PnPEntity to obtain a collection of USB Device objects:

usb_devices = wmi.ExecQuery("Select * From Win32_PnPEntity Where DeviceID = '#{device_name}'")

Finally, we will iterate over the collection and call the Description method for each, looking for a value of 'USB Mass Storage Device':

for usb_device in usb_devices do
if usb_device.Description == 'USB Mass Storage Device'
# DO SOMETHING HERE
end
end

Putting it all together, we have something like the following:

require 'win32ole'

wmi = WIN32OLE.connect("winmgmts://")

devices = wmi.ExecQuery("Select * From Win32_USBControllerDevice")
for device in devices do
device_name = device.Dependent.gsub('"', '').split('=')[1]
usb_devices = wmi.ExecQuery("Select * From Win32_PnPEntity Where DeviceID = '#{device_name}'")
for usb_device in usb_devices do
puts usb_device.Description
if usb_device.Description == 'USB Mass Storage Device'
# DO SOMETHING HERE
end
end
end

The above code is functional, but can certainly be improved upon and modified to meet your specific needs.

That's all for now. Got a question or suggestion? Post a comment or send me email.

Thanks for stopping by!

Digg my article

3 comments:

ginius said...

Can you tell more about WMI? For example - how to use data from Perfomance Monitor (especialy on remote machine)

Anko said...

Is there any way to actually get the mass storage insert EVENTS?

This method requires you to poll for mass storage devices every so often.

Another way to find removable devices is via the FileSystemObject;
require 'win32ole'

oFS = WIN32OLE.new("Scripting.FileSystemObject")
oDrives = oFS.Drives
oDrives.each() do |x|
#driveType 1 is removable disk
if x.DriveType == 1 && x.IsReady
#do something
end
end

this isn't ideal, but is another way to do a similar thing, depending on your application.

Nick

Anonymous said...

Hi, that was very useful, but is it possible to get the drive the particular usb is associated? eg, if i have two usbs drives, how do i get the contents of each?

thanks and kind regards -botp