Link to home
Start Free TrialLog in
Avatar of Programmer_to_be
Programmer_to_be

asked on

Detect USB device Insertion/Removal (drive letter)

hi experts exchange;

I found the following code snippet on ee, and it successfully allows me to detect a USB device (storage device in particular):-

      public enum DeviceEvent : int
      {
            Arrival = 0x8000,                  //DBT_DEVICEARRIVAL
            QueryRemove = 0x8001,                   //DBT_DEVICEQUERYREMOVE
            QueryRemoveFailed = 0x8002,      //DBT_DEVICEQUERYREMOVEFAILED
            RemovePending = 0x8003,                   //DBT_DEVICEREMOVEPENDING
            RemoveComplete = 0x8004,             //DBT_DEVICEREMOVECOMPLETE
            Specific = 0x8005,                         //DBT_DEVICEREMOVECOMPLETE
            Custom = 0x8006                               //DBT_CUSTOMEVENT
      }

      protected override void WndProc(ref Message m)
      {
            base.WndProc(ref m);
            const int WM_DEVICECHANGE = 0x0219;
            DeviceEvent lEvent;

            if(m.Msg == WM_DEVICECHANGE)
            {
                  lEvent = (DeviceEvent) m.WParam.ToInt32();
                        
                  if (lEvent == DeviceEvent.Arrival)
                        MessageBox.Show("USB DEVICE DETECTED");
                  else if (lEvent == DeviceEvent.RemoveComplete)
                        MessageBox.Show("USB DEVICE REMOVED");
            }
      }


My question:-
As far as I understand it, the above code detects any device which is detected, is there a way I can only detect USB devices, I need to know the drive letter of the inserted usb device.

Thanks
Avatar of enwhysee
enwhysee

This post describes how to do it ...

http://groups.google.com/group/microsoft.public.windowsxp.device_driver.dev/msg/d417a756065e7156?hl=en&lr=lang_en&ie=UTF-8&oe=UTF-8

See the Example code section on this site for actual example code on how to do it in C#:
http://lvr.com/hidpage.htm#MyExampleCode
ASKER CERTIFIED SOLUTION
Avatar of graye
graye
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Programmer_to_be

ASKER

Hi graye.

I tried your example by directly copying it in a VB.NET application, and it worked great, when I converted it to C#.NET, it fires the event when the USB device is removed, but I can't seem to get the event fired when the USB device is attached, do you notice anything that I'm doing wrong (below):

using System.Management;

private ManagementEventWatcher m_MediaConnectWatcher;

            private void button1_Click(object sender, System.EventArgs e)
            {
                  string query = "SELECT * FROM __InstanceOperationEvent WITHIN 10 WHERE TargetInstance ISA \"Win32_DiskDrive\"";
                  this.m_MediaConnectWatcher = new ManagementEventWatcher(query);
                  this.m_MediaConnectWatcher.EventArrived += new EventArrivedEventHandler(m_MediaConnectWatcher_EventArrived);
                  this.m_MediaConnectWatcher.Start();
            }

            private void m_MediaConnectWatcher_EventArrived(object sender, EventArrivedEventArgs e)
            {
                  ManagementBaseObject mbo, obj;

                  mbo = (ManagementBaseObject) e.NewEvent;
                  obj = (ManagementBaseObject) mbo["TargetInstance"];

                  switch (mbo.ClassPath.ClassName)
                  {
                        case "__InstanceCreationEvent":
                              
                              if (obj["InterfaceType"].ToString() == "USB")
                              {
                                    this.listBox1.Items.Add(obj["Caption"].ToString() + " (Drive letter " + this.GetDriveLetterFromDisk(obj["Name"].ToString()) + ") has been plugged in");
                              }
                              break;

                        case "__InstanceDeletionEvent":

                              if (obj["InterfaceType"].ToString() == "USB")
                              {
                                    this.listBox1.Items.Add(obj["Caption"].ToString() + " has been unplugged");
                              }
                              break;
                  }
            }

            private string GetDriveLetterFromDisk(string name)
            {
                  ObjectQuery oq_part, oq_disk;
                  ManagementObjectSearcher mos_part, mos_disk;
                  string ans = string.Empty;

                  name = name.Replace(@"\", "\\");

                  oq_part = new ObjectQuery("ASSOCIATORS OF {Win32_DiskDrive.DeviceID=\"" + name + "\"} WHERE AssocClass = Win32_DiskDriveToDiskPartition");
                  mos_part = new ManagementObjectSearcher(oq_part);

                  foreach (ManagementObject obj_part in mos_part.Get())
                  {
                        oq_disk = new ObjectQuery("ASSOCIATORS OF {Win32_DiskPartition.DeviceID=\"" + obj_part["DeviceID"] + "\"} WHERE AssocClass = Win32_LogicalDiskToPartition");
                        mos_disk = new ManagementObjectSearcher(oq_disk);

                        foreach (ManagementObject obj_disk in mos_disk.Get())
                        {
                              ans += obj_disk["Name"] + ",";
                        }
                  }

                  return ans.Trim('c');
            }

            private void button2_Click(object sender, System.EventArgs e)
            {
                  this.m_MediaConnectWatcher.Stop();
            }
hi;

I figured it out, it was the the line

name = name.Replace(@"\", "\\");   -- should have been --  name = name.Replace("\\", "\\\\");

Thanks graye, one last thing, I changed the following query string:-

string query = "SELECT * FROM __InstanceOperationEvent WITHIN 10 WHERE TargetInstance ISA \"Win32_DiskDrive\"";

to

 "..WITHIN 2.."

and it seems to detect the usb device faster, was there any reason for you to initially have WITHIN 10? is it ok to lower that ? because it still seems to work ok but faster!

thanks again.
I see some minor "translation errors"...

               name = name.Replace(@"\", @"\\");
                                                          ^
               return ans.Trim(',');
                                       ^
After those two minor changes, it works for me...
No particular reason... I was just trying to reduce the "poll time", so that the program wouldn't use as many resources.
Thanks graye, it works a treat :-)
Hi graye; I'm sorry to post in a closed question but I couldnt think of any other way to get your attention on a problem that I'm having with the code you provided. I'll open another question to give you further points relating to this question.

When the EventArrived event fires and indicates that a USB device has been attached, I am trying to open another form, the form opens but does not respond at all, it simply changes to the colour white and when I hover the mouse over the opened form, the loading curser appears. To try and convey what I'm trying to do, see below:-

          private void m_MediaConnectWatcher_EventArrived(object sender, EventArrivedEventArgs e)
          {
               ManagementBaseObject mbo, obj;

               mbo = (ManagementBaseObject) e.NewEvent;
               obj = (ManagementBaseObject) mbo["TargetInstance"];

               switch (mbo.ClassPath.ClassName)
               {
                    case "__InstanceCreationEvent":
                         
                         if (obj["InterfaceType"].ToString() == "USB")
                         {
                             Form f2 = new Form();
                             f2.Show(); //Form 2 opens, but does not respond
                         }
                         break;

                    case "__InstanceDeletionEvent":

                         System.Windows.Forms.MessageBox.Show("Device removed");
                         break;
               }
          }



If your able to recreate this problem, would you know why this is happening?
Many thanks.
Let's start with the basics.... normally you'd create an new instance of a form class by it's full name (but I'm assuming that that's just a typo)

       Form2 f2 = new Form2();

Secondly, this is really a multi-threaded application (even though you didn't really do anything that specifically asked for it to be).  That's just the way that WMI was designed to operate.  So, what's happening, is that the form is shown properly, but control is passed back to the calling routine and the new form never gets focus.  Rather than bore you with all of the reasons why, let's just try this simple fix instead.  

       f2.ShowDialog();
graye I can't thank you enough for the help you've provided.

Its working great now. What I originally was trying to do was that when a USB device was detected I was raising an event, the main form of my application subscribed to the event and when the event got fired the event handling method would add a new TabPage to a TabControl. The TabPage was added OK, but its background was white and I was unable to remove it at runtime too (I don't know whether this was because of the multi-threading issue you told me about or not because it does have similarities in behaviour to the form problem), if it was, is there any non-simple way to have my first solution (using a TabPage) work? I eventually ended up just showing a new form (which works now) because I couldnt get the TabPage solution to work.

I'll create a new post in the C# TA, if you could post any message there I'll assign the points to you for the additional help you have provided. Thanks.
Hi graye; a thing which seems to be causing a problem using the ShowDialog method to open the form is that I am unable to close the form when the removal event arrives, my code below may explain this better:

          private Form2 f2;
         
           private void m_MediaConnectWatcher_EventArrived(object sender, EventArrivedEventArgs e)
           {
               ManagementBaseObject mbo, obj;

               mbo = (ManagementBaseObject) e.NewEvent;
               obj = (ManagementBaseObject) mbo["TargetInstance"];

               switch (mbo.ClassPath.ClassName)
               {
                    case "__InstanceCreationEvent":
                         
                         if (obj["InterfaceType"].ToString() == "USB")
                         {
                             f2 = new Form2();
                             f2.ShowDialog();                          
                         }
                         break;

                    case "__InstanceDeletionEvent":

                         if (obj["InterfaceType"].ToString() == "USB")
                         {
                              System.Windows.Forms.MessageBox.Show("Device removed");
                              f2.Close();
                         }
                         break;
               }
          }
 
When Form2 is opened using ShowDialog and I take the USB device out, the deletion event never fires until i manually close Form2, once Form2 is closed the event then fires (giving the message box) and f2.Close(); is then executed... (too late as the form is closed already).

Any ideas?
I know that nobody likes reading the manual (that's why web sites like this are so popular), but....

Let's start off with some background information on the subject of threading:
http://msdn2.microsoft.com/en-us/library/ms173178.aspx

Then a "how to" article on threading:
http://support.microsoft.com/default.aspx?scid=kb;en-us;815804
Hi graye; I understand threading to a good level, my understanding of this problem is that the form gets shown on a different thread (created by WMI), and when that event method finishes execution, the thread which the form was created on gets killed too... what I'm having difficulty in implementing is that I don't know how to execute that form creation code (creating a form object and calling .Show) on the current application thread. Can you point me in the right direction on where I need to look?

Thanks.
Hi again graye, did a bit of reading (should do this more often) and came across the Control.Invoke method, used that and its working fine.

Thanks for the additional help!