Обработка USB-событий Windows

Я создал общий драйвер связи USB HID, способный отслеживать события USB и отправлять/принимать пакеты данных на устройства и с них. Я успешно использую его с WinForms дня, так как большая часть кода была взята из MSDN и реализована очень похоже на предложенный способ здесь. Мой вопрос: примеры, которые я вижу, продолжают полагаться на то, что кажется старой архитектурой «Windows Messaging», то есть сообщения типа System.Runtime.InteropServices, WM_xxxxxxxx, регистрация/обработка их в чисто C# WPF приложении с HwndHandler, переопределение WndProc и т. д. Я был интересно, нет ли способа обрабатывать события USB, используя более современный подход, основанный на событиях, скажем, с delegates, подпиской на EventHandlers и т. д.? Даже эти сообщения SO предлагают воздержаться от использования таких архаичных методов, и я думаю должно быть решение и для событий USB. Спасибо.


person Michael Brodsky    schedule 01.10.2014    source источник
comment
Это все равно, что жаловаться на то, что в вашем новом автомобиле стоит двигатель внутреннего сгорания, изобретенный сто лет назад. Идея старая и надежная, и для ее поддержки существует много инфраструктуры, просто не было ничего достаточно убедительного, чтобы заменить ее. Что угодно можно обернуть в соответствии с парадигмой du jour, что и делает большинство .NET Framework. Только не эта деталь. Только не делайте этого сами, возьмите чужой. С очевидным риском того, что вам не понравится и этот вкус :)   -  person Hans Passant    schedule 01.10.2014
comment
На самом деле не жаловался, просто подумал, что я что-то упустил и все еще делаю это столетним способом, когда был сегодняшний способ. Спс за ссылку, посмотрю.   -  person Michael Brodsky    schedule 01.10.2014
comment
Ганс, я посмотрел чужой пост и у меня уже есть что-то подобное. Я просто пытался избавиться от обмена сообщениями Windoze. Мой немного отличается, не требует дополнительных потоков и избавляет от необходимости помещать OnSourceInitialized и WndProc в приемник Window. Я буду размещать здесь рабочие части и ссылаться на чужие. Кстати, я не против использования устаревшего кода. Я постоянно использую WinForms PropertyGrid в своих материалах и просто пишу собственные TypeConverters и UIEditors. PropertyGrid надежный, проверенный, и я не нашел замены.   -  person Michael Brodsky    schedule 02.10.2014
comment
Что ж, я сделал, как сказал, опубликовал ответ и ссылку на чей-то ответ. К сожалению, у кого-то еще, должно быть, был плохой день с волосами. Сначала я получил кучу автоматически сгенерированных комментариев (формулировка была идентичной), говорящих мне не связывать ответы и размещать там весь ответ. Когда я попросил разъяснений и исследования моего ответа, чтобы увидеть, был ли он вообще уместным, я не получил ничего (я не думаю, что его даже читали). Затем я сделал, как было предложено, и опубликовал этот ответ на чужой вопрос. Меня заминусовали и заблокировали. Все это при моей первой попытке внести свой вклад. Какой сайт ТАК?   -  person Michael Brodsky    schedule 03.10.2014


Ответы (1)


После небольшого исследования кажется, что обмен сообщениями Windows является необходимой частью обработки уведомлений об изменении USB-устройства. У меня есть рабочий класс драйвера USB-коммуникаций, который включает простую реализацию. Это решение состоит из двух частей: 1) класса EventNotifier, который генерирует события, и 2) «приемника», который подписывается на события (т. е. клиент, который получает уведомления о событиях USB). Пример кода — C++/CLI, и хотя я не поддерживаю практику помещения исполняемого кода в заголовочные файлы, для краткости я делаю это здесь.

#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;
    };
}

Затем в «приемнике» (объект, который подписывается и потребляет наш NotificationEvent, все, что вам нужно сделать, это:

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.
}
person Michael Brodsky    schedule 02.10.2014