tags:

views:

44

answers:

4

Hello,

I am writing a directory selector control for WPF, and I would like to add/remove a drive from the directory tree when it gets mounted or unmounted or when it becomes ready or not ready (e.g. the user inserts or removes a CD). I am looking for a system event similar to WM_DEVICECHANGE.

konstantin

+1  A: 

You could:

  • Use WMI and WMI events to detect hardware changes.
  • Use a hidden WinForms window with an override of the WinProc method to pick up WM_DEVICECHANGE.
Richard
+1  A: 

Even though you are using WPF, you can still intercept WM_DEVICECHANGE. You could either attach to an existing window procedure using WPF callback methods, or you could use a System.Windows.Forms.NativeWindow (my preferred method, more control and easier, but you do need to add a reference to System.Windows.Forms.dll)

// in your window's code behind
private static int WM_DEVICECHANGE = 0x0219;

protected override void OnSourceInitialized(EventArgs e)
{
    WindowInteropHelper helper = new WindowInteropHelper(this);
    SystemEventIntercept intercept = new SystemEventIntercept(helper.Handle);
    base.OnSourceInitialized(e);
}

class SystemEventIntercept : System.Windows.Forms.NativeWindow
{
    public SystemEventIntercept(IntPtr handle)
    {
        this.AssignHandle(handle);
    }

    protected override void WndProc(ref Winforms.Message m)
    {
        if (m.Msg == WM_DEVICECHANGE)
        {
            // do something
        }

        base.WndProc(ref m);
    }
}
Zach Johnson
thanks. i suspect this is not portable, it won't work on other operating systems. would you explain what is the purpose of creating SystemEventIntercept object inside OnSourceInitialized method and not using it anywhere?
akonsu
@akonsu: Yes, this solution is not portable, but I don't think WPF is either. `OnSourceInitialized` is called when your WPF window's underlying Win32 handle has been created, so it is a convenient place to hook in to it. The `SystemEventIntercept` is actually doing something, when it is created it starts listening for messages right away. When you intercept `WM_DEVICECHANGE`, you can do whatever you need at the `// do something` comment. My code is just an example, you will probably need to modify it to suit your needs.
Zach Johnson
+3  A: 

I used WMI to implement something like this (like Richard stated in his answer)

using System.Management;

...

private void SubscribeToCDInsertion()
{
    WqlEventQuery q;
    ManagementOperationObserver observer = new ManagementOperationObserver();

    // Bind to local machine
    ConnectionOptions opt = new ConnectionOptions();
    opt.EnablePrivileges = true; //sets required privilege
    ManagementScope scope = new ManagementScope("root\\CIMV2", opt);

    try
    {
        q = new WqlEventQuery();
        q.EventClassName = "__InstanceModificationEvent";
        q.WithinInterval = new TimeSpan(0, 0, 1);
        // DriveType - 5: CDROM
        q.Condition = @"TargetInstance ISA 'Win32_LogicalDisk' and TargetInstance.DriveType = 5";
        w = new ManagementEventWatcher(scope, q);

       // register async. event handler
       w.EventArrived += new EventArrivedEventHandler(driveInsertEvent);
       w.Start();

    }
    catch (Exception e)
    {
        w.Stop();
    }

}

void driveInsertEvent(object sender, EventArrivedEventArgs e)
{
    // Get the Event object and display it
    PropertyData pd = e.NewEvent.Properties["TargetInstance"];

    if (pd != null)
    {
        ManagementBaseObject mbo = pd.Value as ManagementBaseObject;
        // if CD removed VolumeName == null
        if (mbo.Properties["VolumeName"].Value != null)
        {
            //do something
        }
    }
}

EDIT: I didn't invent the code myself, I think I got it from here

StephaneT
A: 

the below code worked for me. it subscribes to both DriveType=2 and DriveType=5 events to detect cd-rom and usb. since i do not need to know whether the drive was mounted or unmounted or cd was removed or inserted, the code does not check for that. for usb mounts e.NewEvent.ClassPath can be used to tell whether drive was connected or disconnected.

also, i found some confusing remarks on the internet saying that subscribing to events for DriveType=5 alone would detect usb mounts as well. this did not work for me.

konstantin


using System;
using System.Management;

namespace consapp
{
    class Program
    {
        static void Main(string[] args)
        {
            const string QUERY = @"select * from __InstanceOperationEvent within 1 where TargetInstance isa 'Win32_LogicalDisk' and (TargetInstance.DriveType=2 or TargetInstance.DriveType=5)";

            Program p = new Program();

            ManagementEventWatcher w = new ManagementEventWatcher(new WqlEventQuery(QUERY));

            w.EventArrived += new EventArrivedEventHandler(p.OnWMIEvent);
            w.Start();

            Console.ReadKey();

            w.Stop();
        }

        public void OnWMIEvent(object sender, EventArrivedEventArgs e)
        {
            PropertyData p = e.NewEvent.Properties["TargetInstance"];

            if (p != null)
            {
                ManagementBaseObject mbo = p.Value as ManagementBaseObject;

                PropertyData deviceid = mbo.Properties["DeviceID"];
                PropertyData drivetype = mbo.Properties["DriveType"];

                Console.WriteLine("{0}-{1}:{2}", deviceid.Value, drivetype.Value, e.NewEvent.ClassPath);
            }
        }
    }
}

akonsu