locked
Way to get USB hard drives via unmanaged code RRS feed

  • Question

  • Hello,
    I was using HwndSource to get notification when usb drives change, and WMI to get current USB devices, on a background thread.  However, I get this exception:
    The application called an interface that was marshalled for a different thread.  ---> System.Runtime.InteropServices.COMException (0x8001010E): The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

    I can't use System.IO.DriveInfo.GetDrives() because USB hard drives types are FIXED and not removable, only usb key/thumbdrives are removabble.

    Are there unmanaged functions I can use via p/invoke to get all the usb drives?

    Here is most of my existing code:

    var handle = new WindowInteropHelper(window).Handle;
    
                this.hwndSource = HwndSource.FromHwnd(handle);
                this.wndProc = new HwndSourceHook(this.HwndMessage);
                this.hwndSource.AddHook(this.wndProc);
    
    
            private IntPtr HwndMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
            {
                // Handle whatever Win32 message it is we feel like handling
                if (msg == NativeMethods.WM_DEVICECHANGE)
                {
                    if (wParam.ToInt32() == NativeMethods.DBT_DEVICEARRIVAL || wParam.ToInt32() == NativeMethods.DBT_DEVICEREMOVECOMPLETE)
                    {
                        OnUsbDeviceChange();
                    }
                }
                return IntPtr.Zero;
            }
    
            private void OnUsbDeviceChange()
            {
                IList<ManagementObject> usbDrives = new List<ManagementObject>();
    
                try
                {
                    ManagementObjectCollection drives = new ManagementObjectSearcher("SELECT Caption, DeviceID FROM Win32_DiskDrive WHERE InterfaceType='USB'").Get();
    
                    if (drives.Count > 0)
                    {
                        // browse all USB WMI physical disks
                        foreach (ManagementObject drive in drives)
                        {
                            // browse all USB WMI physical disks
                            foreach (ManagementObject partition in new ManagementObjectSearcher( "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + drive["DeviceID"]
                                        + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition").Get())
                            {
                                // browse all USB WMI physical disks
                                foreach (ManagementObject disk in new ManagementObjectSearcher("ASSOCIATORS OF {Win32_DiskPartition.DeviceID='"
                                        + partition["DeviceID"] + "'} WHERE AssocClass = Win32_LogicalDiskToPartition").Get())
                                {
                                    usbDrives.Add(disk);
                                }
                            }
                        }
                }
                }
                catch (Exception)
                {
                    throw;
                }
    
    }
    

    thanks,

    dan

    Friday, August 21, 2009 4:08 AM

Answers

  • Hmm, this is perhaps not relevant, but maybe worth a try; Is the window you are capturing the messages for a form ? If so, when you receive a message what does the .InvokeRequired property of the form return ? It seems unlikely, but if it is returning true, then you might want to try running your OnUsbDeviceChange method via a delegate and the form.Invoke method to see if it avoids the exception about being on the wrong thread.

    Alternatively, could you run that code on a background thread anyway.. i.e if you change the window hook to call OnUsbDeviceChange via the thread pool or a new thread, are those threads then able to access the WMI stuff without the thread error ?

    • Proposed as answer by Yort Sunday, August 23, 2009 9:26 PM
    • Marked as answer by dan blanchard Sunday, August 23, 2009 10:52 PM
    Friday, August 21, 2009 4:46 AM
  • thanks, I just dispatched getting the updated list on the window object, and it seems to work OK....

                    _window.Dispatcher.BeginInvoke(DispatcherPriority.Background, 
                        (ThreadStart) ( () =>
                        {
                            IList<IUsbDevice> devices = GetUsbPeripheralDrives();
                            tempHandler(this, new GenericEventArgs<IList<IUsbDevice>>(devices));
                        })
                        );


    • Marked as answer by dan blanchard Friday, August 21, 2009 6:20 PM
    Friday, August 21, 2009 5:33 PM

All replies

  • Hmm, this is perhaps not relevant, but maybe worth a try; Is the window you are capturing the messages for a form ? If so, when you receive a message what does the .InvokeRequired property of the form return ? It seems unlikely, but if it is returning true, then you might want to try running your OnUsbDeviceChange method via a delegate and the form.Invoke method to see if it avoids the exception about being on the wrong thread.

    Alternatively, could you run that code on a background thread anyway.. i.e if you change the window hook to call OnUsbDeviceChange via the thread pool or a new thread, are those threads then able to access the WMI stuff without the thread error ?

    • Proposed as answer by Yort Sunday, August 23, 2009 9:26 PM
    • Marked as answer by dan blanchard Sunday, August 23, 2009 10:52 PM
    Friday, August 21, 2009 4:46 AM
  • COM is single threaded. Therefore, make sure that you are handling the code in the thread that created the COM object.

    To answer your question:

    "Are there unmanaged functions I can use via p/invoke to get all the usb drives?"

    No, the point of Platform Invoke is to call unmanaged functions.
    Geert van Horrik - CatenaLogic
    Visit my blog: http://blog.catenalogic.com

    Looking for a way to deploy your updates to all your clients? Try Updater!
    • Proposed as answer by Yort Sunday, August 23, 2009 9:27 PM
    Friday, August 21, 2009 7:58 AM
  • thanks, I just dispatched getting the updated list on the window object, and it seems to work OK....

                    _window.Dispatcher.BeginInvoke(DispatcherPriority.Background, 
                        (ThreadStart) ( () =>
                        {
                            IList<IUsbDevice> devices = GetUsbPeripheralDrives();
                            tempHandler(this, new GenericEventArgs<IList<IUsbDevice>>(devices));
                        })
                        );


    • Marked as answer by dan blanchard Friday, August 21, 2009 6:20 PM
    Friday, August 21, 2009 5:33 PM
  • yeah i know the point of platform invoke calls, i was asking if there are unmanaged functions I can use to get usb drives instead of using dealing with COM and WMI.
    Friday, August 21, 2009 6:20 PM
  • So effectively you used invoke like I suggested (just via a different pattern) ?
    Sunday, August 23, 2009 9:26 PM
  • I had the same problem while trying to get USB thumb drive information when one is inserted.  First I got the ManagementException, rewrote the software, and then got a COMException.  In my Program.cs, on the Main method, I changed the [STAThread] to [MTAThread], reran the app and it worked.  Perhaps this will work for you too.
    Friday, November 6, 2009 5:38 PM