Dynamic Code Blocks

Microsoft Dynamics GP & .NET technical Blog by Tim Wappat

U.B. Funkey USB Detection Example VB.NET & WPF

Last time I wanted to perform detection of a USB device with .NET I overrode the WndProc method in the main form of the application to capture and route the windows messages through to the library.

FunkeyBlogWPF does not natively have the WndProc as it uses a different messaging architecture, however an interop has been provided details of which can be found in amongst "WPF and Win32 Interoperation Overview"

Just want example code?

Here is some example code to get you started. It detects the USB insertion and removal for a U.B. Funkey Game unit – it was what was on my desk when I needed something to test with… U.B. Funkeys Official Site

Find the device class ID from your device manger page for the device you want to use. In device manager, expand the node for the USB device in question and look for a GUID that is found under the properties, then details tab. The GUID uniquely identifies the USB device.

Get the message

We ultimately want to get the WM_DEVICECHANGE message from windows and register the device we are looking for so that we also get passed the WParam information about which device has changed and raise events as required. If you don’t register an interest in the specific device you merely get a DBT_DEVNODES_CHANGED = &H7 returned that is not really too helpful.

Public Sub ProcessWinMessage(ByVal msg As Integer, ByVal wParam As IntPtr, _
                             ByVal IParam As IntPtr)
    If msg = WM_DEVICECHANGE Then
        Select Case wParam
            Case WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEARRIVAL
                RaiseEvent AfterUSBInserted(Me, New EventArgs)
            Case WM_DEVICECHANGE_WPPARAMS.DBT_DEVICEREMOVECOMPLETE
                RaiseEvent AfterUSBRemoved(Me, New EventArgs)
        End Select
    End If
End Sub

There is plenty on code project website about these messages and how to use them in traditional windows with .NET. However we want to use WPF as an application type so lets move on…

WindowInteropHelper

To get around the fact that WPF has no WndProc to override we have to use another technique. Override the OnSourceInitalized and get an instance of the Interop.WindowInteropHelper class initialised with the WPF form we are using. Using this interop object we can access a windows handle and can create a hook with AddHook to get hold of the windows events. This hook simply calls a delegate method for processing those messages, in this example oUsbFunkey.HwndHandler.

Protected Overrides Sub OnSourceInitialized(ByVal e As System.EventArgs)
    MyBase.OnSourceInitialized(e)
    Dim MainFormWinInteropHelper As New Interop.WindowInteropHelper(Me)
    Interop.HwndSource.FromHwnd(MainFormWinInteropHelper.Handle). _
        AddHook(AddressOf oUsbFunkey.HwndHandler)
    oUsbFunkey.RegisterDeviceNotification(MainFormWinInteropHelper.Handle)
End Sub

HwndHandler function gets the messages from the message hooking above

Public Function HwndHandler(ByVal hwnd As IntPtr, ByVal msg As Integer, _
                            ByVal wParam As IntPtr, ByVal IParam As IntPtr, _
                            ByRef handled As Boolean) As IntPtr
    ProcessWinMessage(msg, wParam, IParam)
    handled = False
    Return IntPtr.Zero
End Function

Register interest in the USB device

The code next registers the device we are interested in, namely the Funkey and its class id of "c671678c-82c1-43f3-d700-0049433e9a4b".

Private Const USBClassID As String = _
    "c671678c-82c1-43f3-d700-0049433e9a4b"
 
Public Sub RegisterDeviceNotification(ByVal hwnd As IntPtr)
    Dim deviceInterface As New Win32.DEV_BROADCAST_DEVICEINTERFACE()
    Dim size As Integer = Marshal.SizeOf(deviceInterface)
    deviceInterface.dbcc_size = size
    deviceInterface.dbcc_devicetype = Win32.DBT_DEVTYP_DEVICEINTERFACE
    deviceInterface.dbcc_reserved = 0
    deviceInterface.dbcc_classguid = New Guid(USBClassID).ToByteArray
    Dim buffer As IntPtr = Nothing
    buffer = Marshal.AllocHGlobal(size)
    Marshal.StructureToPtr(deviceInterface, buffer, True)
    Dim r As IntPtr = Nothing
    r = Win32.RegisterDeviceNotification(hwnd, buffer, _
                            Win32.DEVICE_NOTIFY_WINDOW_HANDLE Or _
                            Win32.DEVICE_NOTIFY_ALL_INTERFACE_CLASSES)
End Sub

This registration ensures we get details about when this device is inserted or removed through the previously set up message hook.  The registration calls through to the registerdevicenotification function in user32.dll. When you have finished listening you should also call the UnregisterDeviceNotification function, something I’ve not done for this example.

<DllImport("user32.dll", SetLastError:=True)> _
Public Shared Function RegisterDeviceNotification( _
  ByVal IntPtr As IntPtr, ByVal NotificationFilter As IntPtr, _
  ByVal Flags As Int32) As IntPtr
End Function
 
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Public Shared Function UnregisterDeviceNotification( _
   ByVal hHandle As IntPtr) As UInteger
End Function

Hello Funkey

When the message is received I choose to raise an event back to the WPF form that in turn shows or hides a photo of my Funkey.

This is a get you started guide. Check out the various elements of this solution so you understand how each part works, and thus add appropriate robustness to it for your application of the code.

Code download

Download the Example Code 

 

WpfFunkeyApplication.zip (235.51 kb)