Detecting USB Mass Storage Insertion from C#

I need to detect the insertion and removal of USB Mass Storage Devices from C#. I have found some code (http://www.codeproject.com/dotnet/devicevolumemonitor.asp) which solves most of my problem however it also triggers when a user maps a network drive. Can someone suggest modifications or an alternative method.
/// <summary>
        /// WndProc method that traps all messages sent to the Handle
        /// </summary>
        /// <param name="aMessage">A Windows message</param>
        protected override void WndProc(ref Message aMessage)
        {
            BroadcastHeader lBroadcastHeader;
            Volume          lVolume;
            DeviceEvent     lEvent;
 
            base.WndProc(ref aMessage);
            if(aMessage.Msg==WM_DEVICECHANGE && fMonitor.Enabled)
            {
                lEvent = (DeviceEvent)aMessage.WParam.ToInt32();
                if (lEvent==DeviceEvent.Arrival || lEvent==DeviceEvent.RemoveComplete)
                {
                    lBroadcastHeader = (BroadcastHeader)Marshal.PtrToStructure(aMessage.LParam,typeof(BroadcastHeader));
                    if(lBroadcastHeader.Type==DeviceType.Volume)
                    {
                        lVolume = (Volume)Marshal.PtrToStructure(aMessage.LParam,typeof(Volume));
                        if((lVolume.Flags & (int)VolumeFlags.Media) == 0)
                        { 
                            fMonitor.TriggerEvents(lEvent==DeviceEvent.Arrival,lVolume.Mask);
                        }
                    }
                }
            }
        }

Open in new window

LVL 4
S31B1Asked:
Who is Participating?
 
Bob LearnedCommented:
Both the Volume and BroadcastHeader structures have a DeviceType.

Bob
0
 
prosh0tCommented:
it looks like somebody else had the same problem, maybe this can help:

http://www.thescripts.com/forum/thread264280.html

also look at:
http://www.codeproject.com/dotnet/devicevolumemonitor.asp?df=100&forumid=15152&exp=0&fr=26

see the flags DBTF_MEDIA and DBTF_NET
0
 
S31B1Author Commented:
@The Learned One
On my system The Device Type returned for Network shares and Mass Storage devices are both Volume (0x00000002).

@prosh0t
So I'm looking at the VolumeFlags now, it seems that, in my limited testing:-

NET Share: Volume.Flags = 2
USB: Volume.Flags = 0
CDROM: Volume.Flags = 2002452481

Could you expalin any more about the Flags?

If I just test for Volume.Flags == 0 will this detect only the USB in all cases?
0
Cloud Class® Course: MCSA MCSE Windows Server 2012

This course teaches how to install and configure Windows Server 2012 R2.  It is the first step on your path to becoming a Microsoft Certified Solutions Expert (MCSE).

 
S31B1Author Commented:
as I understand it the Code above is calling into non-managed code, is there something in the framework I should be using in preference?

What is the "ManagementEventWatcher" all about?
0
 
Bob LearnedCommented:
If you want to go the WMI route, here is a class that will function as a USB device watcher.

Bob
// Add a reference to System.Management.dll to the project.
 
using System;
using System.Management;
 
public class Win32_UsbDriveWatcher
{
  private ManagementEventWatcher m_watcher;
 
  public delegate void DeviceInsertedEventHandler(object sender, Win32_UsbDriveInsertEventArgs e);
 
  public event DeviceInsertedEventHandler DeviceInserted;
  public event EventHandler DeviceRemoved;
 
  public void Start(int pollingInterval)
  {
    string queryString =
      "SELECT * " +
      " FROM __InstanceOperationEvent" +
      " WITHIN " + pollingInterval +
      " WHERE TargetInstance ISA 'Win32_DiskDrive'";
 
    EventQuery processQuery = new EventQuery(queryString);
 
    m_watcher = new ManagementEventWatcher(processQuery);
    m_watcher.EventArrived += new System.Management.EventArrivedEventHandler(EventArrived);
    m_watcher.Start();
  }
 
  private void EventArrived(object sender, System.Management.EventArrivedEventArgs e)
  {
    PropertyData instance = ((PropertyData)(e.NewEvent.Properties["TargetInstance"]));
    ManagementBaseObject obj = (ManagementBaseObject)instance.Value;
    Win32_UsbDriveInsertEventArgs args = new Win32_UsbDriveInsertEventArgs();
    if ((string)obj["InterfaceType"] == "USB")
    {
      args.IsCreated = (obj.ClassPath.ClassName == "__InstanceCreationEvent");
      args.DriveName = this.GetDriveLetterFromDisk((string)obj["Name"]);
      if (args.IsCreated)
      {
        if (DeviceInserted != null)
        {
          DeviceInserted(this, args);
        }
      }
      else
      {
        if (DeviceRemoved != null)
        {
          DeviceRemoved(this, EventArgs.Empty);
        }
      }
    }
  }
 
  private string GetDriveLetterFromDisk(string name)
  {
    name = name.Replace("\\", "\\\\");
 
    string query = "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + name + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition";
    ObjectQuery queryDrive = new ObjectQuery(query);
    ManagementObjectSearcher searcherDrive = new ManagementObjectSearcher(queryDrive);
    foreach (ManagementObject drive in searcherDrive.Get())
    {
      query = "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" +
        drive["DeviceID"] +
        "'} WHERE AssocClass = Win32_LogicalDiskToPartition";
      ObjectQuery queryPartition = new ObjectQuery(query);
      ManagementObjectSearcher searcherPartition = new ManagementObjectSearcher(queryPartition);
      foreach (ManagementObject disk in searcherPartition.Get())
      {
        return (string)disk["Name"];
      }
    }
    return string.Empty;
  }
 
}
 
public class Win32_UsbDriveInsertEventArgs : EventArgs
{
  public bool IsCreated;
  public string DriveName;
}

Open in new window

0
 
Bob LearnedCommented:
Example:

Win32_UsbDriveWatcher watcher = new Win32_UsbDriveWatcher();
watcher.DeviceInserted += OnUsbDeviceInserted;
watcher.DeviceRemoved += OnUsbDeviceRemoved;
watcher.Start(5);

...

private void OnUsbDeviceInserted(object sender, Win32_UsbDriveInsertEventArgs e)
{
}

private void OnUsbDeviceRemoved(object sender, EventArgs e)
{
}

Bob
0
 
S31B1Author Commented:
Thanks, for the code I'm having a look at it now. A couple of issues. When I close the test program I have written I get an InvalidComObjectException. "Com object has been seperated from its underlying RCW cannot be used"

And it never seems to trigger!

It gets into the GetDriveLetterFromDisk function, attempts to itterate over the searcherDrive.Get() fails and drops right out, I don't get any errors either.

not sure if the two problems are related?
0
 
S31B1Author Commented:
Another thing, all things equal, If I get this new code working which is the preferred way of trapping this event in my c# winform?
0
 
S31B1Author Commented:
So, I've commented out the call to this.GetDriveLetterFromDisk and I now get my event raised. however it is always a Device Removed Event regardless of action.
0
 
S31B1Author Commented:
Sorry!

Next Question.

I notice there is a poling interval (as a parameter of the constructor) and in the query is this a value in seconds? Does this mean I could be waiting up to 5 seconds before i get a notification?

If so what is the smallest window of time I can sample and what would be the effect on system performance?
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.