Answered by:
Way to get USB hard drives via unmanaged code

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