giovedì 21 luglio 2011

Detecting USB microphone insertion/removal in C# .Net 4.0 WPF

I'm working on a C# .Net 4.0 WPF project that must detect the insertion or removal of an USB microphone.
The code should be implemented in a C# class library.
Since .Net 4.0 doesn't have classes able to detect the insertion or removal of an USB device, the solution is hooking the message WM_DEVICECHANGE, then find out which device changed in order to be sure that the device is the one desired and not some other device.
The first issue to solve is that we don't have any hWnd handle in a class library, so we need to create a fake window in order to hook his window procedure:


HwndSource hwndSource = new HwndSource(0, 0, 0, 0, 0, "fake", IntPtr.Zero);
if (hwndSource != null)  //Attaching to window procedure
    hwndSource.AddHook(new HwndSourceHook(this.hwndSourceHook));

//the window procedure
IntPtr hwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) 
{
    if (msg == WM_DEVICECHANGE)   
   {
      if ((int)wParam == DBT_DEVICEARRIVAL)
     {   
        DEV_BROADCAST_DEVICEINTERFACE info = (DEV_BROADCAST_DEVICEINTERFACE)   
Marshal.PtrToStructure(lParam, typeof(DEV_BROADCAST_DEVICEINTERFACE));   
      }   
      else if ((int)wParam == DBT_DEVICEREMOVECOMPLETE)
     {
      }
      else if ((int)wParam == DBT_DEVNODES_CHANGED)
     {
     }
  }  
  return IntPtr.Zero;
}


The second problem is to identify the device by the GUID class.
Several microphones by Philips, Olympus or Dictaphone are multicomposite devices.
It means that when you plugin the microphone you get 5 or 6 WM_DEVICECHANGE messages.
So it is preferable to filter the device class using the RegisterDeviceNotification API.

DEV_BROADCAST_DEVICEINTERFACE notificationFilter = new DEV_BROADCAST_DEVICEINTERFACE();
int size = Marshal.SizeOf(notificationFilter);
notificationFilter.dbcc_size = size;
notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
notificationFilter.dbcc_reserved = 0;
notificationFilter.dbcc_classguid = new Guid(MediaClassID).ToByteArray();
IntPtr buffer = IntPtr.Zero;
buffer = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(notificationFilter, buffer, true);
IntPtr result = RegisterDeviceNotification(this._hwndSource.Handle, buffer, (Int32)(DEVICE_NOTIFY.DEVICE_NOTIFY_WINDOW_HANDLE));

The class guid are stored in the registry:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceClasses
If you want find out the class guid of your device, use the parameter DEVICE_NOTIFY_ALL_INTERFACE_CLASSES in RegisterDeviceNotification API, plug in the device into the USB port and read the guid in the DEV_BROADCAST_DEVICEINTERFACE structure.

I found out the following classes used by USB microphones:
KSCATEGORY_CAPTURE = "{65E8773D-8F56-11D0-A3B9-00A0C9223196}"
KSCATEGORY_AUDIO = "{6994AD04-93EF-11D0-A3CC-00A0C9223196}"

Here attached a MVVM project using the USBDetector class

3 commenti:

  1. Hi,
    great example MVVM project!
    One question: have you ever tried do get the device name from dbcc_name and to pass it to the DeviceAttached event handler?
    regards, rainer

    RispondiElimina
  2. I make sense of that one gains some new useful knowledge ordinary. Educational site incidentally.
    yeti usb microphone

    RispondiElimina
  3. The microphone stand stand threading and jolt mount to me makes it a lot more complete package than the Abominable. Not looking like a equine suppository helps too. I actually moved on from the Yeti along with use XLR mics today, but if I could go back in time Would take this over the Yeti. hyperx quadcast usb condenser gaming microphone

    RispondiElimina