1

I created a generic USB HID comms driver capable of monitoring USB events and sending/receiving data packets to and from devices. I've been using it successfully since my WinForms days as much of the code was culled from MSDN and implemented very similarly to the way suggested here. My question is: The examples I see continue to rely on what seem to be the old "Windows Messaging" architecture, i.e. System.Runtime.InteropServices, WM_xxxxxxx type messages, registering/handling these in a purely C# WPF app with HwndHandler, overriding WndProc, etc. I was wondering is there not a way to handle USB events using a more modern, event-driven approach, say with delegates, subscribing to EventHandlers, etc? Even this SO posts suggest refraining from using such archaic methods and I imagine there must be a solution for USB events as well. Thanx.

Community
  • 1
  • 1
  • It is a bit like complaining that your brand new car has a combustion engine that was invented a hundred years ago. The idea is old and solid and has a *lot* of infrastructure that support it, there just hasn't been anything compelling enough to replace it. Anything can be wrapped to fit the paradigm *du jour*, its what most of the .NET Framework does. Just not *that* detail. Just don't do it yourself, take [somebody else's](http://stackoverflow.com/a/2061741/17034). With the obvious risk that you don't like that flavor either :) – Hans Passant Oct 01 '14 at 19:08
  • Not really complaining, just thought I'd missed something and was still doing it the hundred-year-old-way when there was a today-way. Thanx for the link, I'll take a look. – Michael Brodsky Oct 01 '14 at 19:45
  • Hans, I did look at "somebody else's" post and I already have something like that. I was just trying to get rid of the Windoze messaging stuff. Mine's a little different, doesn't rely on extra threading and dispenses with having to put the `OnSourceInitialized` and `WndProc` stuff in your "receiver" `Window`. I'll post the working parts here and link to "somebody else's". BTW, I'm not averse to using legacy code. I use the `WinForms PropertyGrid` in my stuff all the time and just write custom `TypeConverters` and `UIEditors`. The `PropertyGrid` is robust, proven and, I've found no substitute. – Michael Brodsky Oct 02 '14 at 15:31
  • Well, I did as I said, posted an answer and a linked to "somebody elses" answer. Unfortunately, "somebody else" must've had a bad hair day. First I got a bunch of auto-generated comments (the wording was identical) telling me not to "link" answers and post the whole answer there. When I asked for clarification and to research my answer to see if it was even relevant, I received none (I don't think it was even read). Then I did as suggested and posted this answer on "somebody else's" question. I got down-voted and blocked. All this on my first attempt at contributing. What kind site is SO? – Michael Brodsky Oct 03 '14 at 19:18

1 Answers1

0

After doing a bit of research, it seems that Windows messaging is a necessary part of handling USB Device Change notifications. I have a working usb comms driver class which includes a simple implementation. This solution consists of two parts, 1) the EventNotifier class, which generates the events and 2) the 'receiver', which subscribes to the events (i.e. a client that gets notified of USB events). The sample code is C++/CLI and although I don't subscribe to the practice of putting executable code in header files, for the sake of brevity, I do so here.

#pragma once

#include <Windows.h>    // Declares required datatypes.
#include <Dbt.h>        // Required for WM_DEVICECHANGE messages.
#include <initguid.h>   // Required for DEFINE_GUID definition (see below).

namespace USBComms 
{
    using namespace System;
    using namespace System::Runtime::InteropServices;
    using namespace System::Windows;
    using namespace System::Windows::Forms;

    // This function is required for receieving WM_DEVICECHANGE messages.
    // Note: name is remapped "RegisterDeviceNotificationUM"
    [DllImport("user32.dll" , CharSet = CharSet::Unicode, EntryPoint="RegisterDeviceNotification")]                 
    extern "C" HDEVNOTIFY WINAPI RegisterDeviceNotificationUM(
        HANDLE hRecipient,
        LPVOID NotificationFilter,
        DWORD Flags);

    // Generic guid for usb devices (see e.g. http://msdn.microsoft.com/en-us/library/windows/hardware/ff545972%28v=vs.85%29.aspx).
    // Note: GUIDs are device and OS specific and may require modification. Using the wrong guid will cause notification to fail.
    // You may have to tinker with your device to find the appropriate GUID. "hid.dll" has a function `HidD_GetHidGuid' that returns
    // "the device interfaceGUID for HIDClass devices" (see http://msdn.microsoft.com/en-us/library/windows/hardware/ff538924%28v=vs.85%29.aspx).
    // However, testing revealed it does not always return a useful value. The GUID_DEVINTERFACE_USB_DEVICE value, defined as
    // {A5DCBF10-6530-11D2-901F-00C04FB951ED}, has worked with cell phones, thumb drives, etc. For more info, see e.g.
    // http://msdn.microsoft.com/en-us/library/windows/hardware/ff553426%28v=vs.85%29.aspx. 
    DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);

    /// <summary>
    /// Declare a delegate for the notification event handler.
    /// </summary>
    /// <param name="sender">The object where the event handler is attached.</param>
    /// <param name="e">The event data.</param>
    public delegate void NotificationEventHandler(Object^ sender, EventArgs^ e);

    /// <summary>
    /// Class that generetaes USB Device Change notification events.
    /// </summary>
    /// <remarks>
    /// A Form is not necessary. Any type wherein you can override WndProc() can be used.
    /// </remarks>
    public ref class EventNotifier : public Control
    {
    private:
        /// <summary>
        /// Raises the NotificationEvent.
        /// </summary>
        /// <param name="e">The event data.</param>
        void RaiseNotificationEvent(EventArgs^ e) {
            NotificationEvent(this, e);
        }

    protected:
        /// <summary>
        /// Overrides the base class WndProc method.
        /// </summary>
        /// <param name="message">The Windows Message to process. </param>
        /// <remarks>
        /// This method receives Windows Messages (WM_xxxxxxxxxx) and
        /// raises our NotificationEvent as appropriate. Here you should
        /// add any message filtering (e.g. for the WM_DEVICECHANGE) and
        /// preprocessing before raising the event (or not).
        /// </remarks>
        virtual void WndProc(Message% message) override {
            if(message.Msg == WM_DEVICECHANGE)
            {
                RaiseNotificationEvent(EventArgs::Empty);
            }
            __super::WndProc(message);
        }

    public:
        /// <summary>
        /// Creates a new instance of the EventNotifier class.
        /// </summary>
        EventNotifier(void) {
            RequestNotifications(this->Handle); // Register ourselves as the Windows Message processor.
        }

        /// <summary>
        /// Registers an object, identified by the handle, for
        /// Windows WM_DEVICECHANGE messages.
        /// </summary>
        /// <param name="handle">The object's handle.</param>
        void RequestNotifications(IntPtr handle) {
            GUID InterfaceClassGuid = GUID_DEVINTERFACE_USB_DEVICE;
            DEV_BROADCAST_DEVICEINTERFACE MyDeviceBroadcastHeader;

            MyDeviceBroadcastHeader.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
            MyDeviceBroadcastHeader.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
            MyDeviceBroadcastHeader.dbcc_reserved = 0;
            MyDeviceBroadcastHeader.dbcc_classguid = InterfaceClassGuid;
            RegisterDeviceNotificationUM((HANDLE)handle, &MyDeviceBroadcastHeader, DEVICE_NOTIFY_WINDOW_HANDLE);
        }

        /// <summary>
        /// Defines the notification event.
        /// </summary>
        virtual event NotificationEventHandler^ NotificationEvent;
    };
}

Then, in the 'receiver' (the object that subscribes to and consumes our NotificationEvent, all you have to do is:

void Receiver::SomeFunction(void)
{
    USBComms::EventNotifier usb = gcnew USBComms::EventNotifier();

    usb->NotificationEvent += gcnew USBComms::NotificationEventHandler(this, &Receiver::USBEvent);
}

void Receiver::USBEvent(Object^ sender, EventArgs^ e)
{
    // Handle the event notification as appropriate.
}