1

I usually detect USB device insertion and removal event by monitor WM_DEVICECHANGE message. In Windows XP, when USB insertion event happened, message is below:

WM_DEVICECHANGE
WParam = DBT_DEVICEARRIVAL, devType = DBT_DEVTYP_DEVICEINTERFACE

WM_DEVICECHANGE
WParam = DBT_DEVICEARRIVAL, devType = DBT_DEVTYP_VOLUME

But now I am using Windows 8.1, for the same event, message is below:

WM_DEVICECHANGE
WParam = **DBT_DEVICEARRIVAL**, devType = DBT_DEVTYP_DEVICEINTERFACE

WM_DEVICECHANGE
WParam = **DBT_DEVICEREMOVECOMPLETE**, devType = DBT_DEVTYP_DEVICEINTERFACE

WM_DEVICECHANGE
WParam = DBT_DEVICEARRIVAL, devType = DBT_DEVTYP_DEVICEINTERFACE

WM_DEVICECHANGE
WParam = DBT_DEVICEARRIVAL, devType = DBT_DEVTYP_VOLUME

My question is:

  1. Why the first 2 messages come here?
  2. How can I detect USB device insertion event in Windows 8?
MSalters
  • 159,923
  • 8
  • 140
  • 320
ET Worker
  • 275
  • 1
  • 3
  • 11
  • Were you able to get any information on the first arrival/removal? This could be a difference in the OS or device enumeration. Since the first two (actually three) have nothing to do with a volume your code should still work to find the arrival message for the volume, and anything outside of that you should be able to ignore. – Preston Aug 23 '14 at 14:09
  • I am afraid no more information can get from the first arrival/removal event, seems both are similar to the normal event. maybe I can queue them and handle with a little bit delay to ensure detecting both of them because of the small delta time. My code should handle arrival/removal event in different function, and this strange event sequence really bother me :( – ET Worker Aug 24 '14 at 17:37

1 Answers1

0

The first two messages, DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE are generated by the system when a USB device is attached to and removed from it. By listening for these messages, your code can have 'plug-and-play' functionality: when a new devices is attached, do something; when it's removed do something else... I don't know that Win 8 is any different from previous versions, but there are several example of how to go about detecting USB 'device change' events given here, here and here.

My solution is as follows, but essentially you cannot get away from listening for WM_DEVICECHANGE messages and for that you need a WinForms component wherein you override WndProc(). The MSDN article on "Device Management" is here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363224%28v=vs.85%29.aspx As you'll see, its all unmanaged code.

#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>
        bool RequestNotifications(IntPtr handle) {
            DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;

            ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
            NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
            NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
            NotificationFilter.dbcc_reserved = 0;
            NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
            return RegisterDeviceNotificationUM((HANDLE)handle, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE) != NULL;
        }

        /// <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.
}
Community
  • 1
  • 1
  • thanks for your answer, but the solution is only worked on normal case. I have tested some machines running win8 and not only my machine has such duplicate message. maybe this is a bug? – ET Worker Jan 03 '15 at 12:00