简体   繁体   English

C ++ Win32在WM_DEVICECHANGE上没有收到DBT_DEVICEARRIVAL或DBT_DEVICEREMOVECOMPLETE

[英]C++ Win32 Not receiving DBT_DEVICEARRIVAL or DBT_DEVICEREMOVECOMPLETE on WM_DEVICECHANGE

I have been working on detecting USB insertion/removal. 我一直致力于检测USB插入/移除。 I have implemented code using CreateWindowEx(), passing a WNCLASSEX that with my window process callback. 我已经使用CreateWindowEx()实现了代码,并通过我的窗口进程回调传递了WNCLASSEX。 On inserting and removing my usb, I successfully receive the WM_DEVICECHANGE message, but the wParam is always set to DBT_DEVNODES_CHANGED. 在插入和删除我的usb时,我成功收到WM_DEVICECHANGE消息,但wParam始终设置为DBT_DEVNODES_CHANGED。

I never get either DBT_DEVICEARRIVAL or DBT_DEVICEREMOVECOMPLETE. 我从来没有得过DBT_DEVICEARRIVAL或DBT_DEVICEREMOVECOMPLETE。 I have been using what I am getting, but I really need to be able to tell the difference between device arrival and removal, so that I can take different actions depending on which I receive. 我一直在使用我得到的东西,但我真的需要能够分辨出设备到达和移除之间的区别,以便我可以采取不同的操作,具体取决于我收到的内容。

Right now, I'm having to put a timer after receiving DBT_DEVNODES_CHANGED, and then test to see if there are any new removables on the system, or if any in my list are no longer there. 现在,我必须在收到DBT_DEVNODES_CHANGED之后放一个计时器,然后测试系统上是否有任何新的可移动设备,或者我的列表中是否有任何新的可移动设备。 I'm sure this isn't right, so I thought I'd ask. 我确定这不对,所以我想我会问。 I would much rather to get rid of the timer and just receive these two messages. 我宁愿摆脱计时器,只是收到这两条消息。 That would help a lot in what I have to do. 这对我必须做的事情有很大帮助。 Any suggestions? 有什么建议?

Here is the code that I have for registering the callback, as well as the callback itself: 这是我用于注册回调的代码,以及回调本身:

NOTE: 3/12/2015: Code updated to show actual GUID and the definition of the DoRegisterDeviceInterfaceToHwnd() function.): 注意:2015年3月12日:更新代码以显示实际GUID和DoRegisterDeviceInterfaceToHwnd()函数的定义。):

GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72, 0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
//GUID WusbrawGUID = {0xa5dcbf10, 0x6530, 0x11d2, 0x90, 0x1f, 0x00, 0xc0, 0x4f, 0xb9, 0x51, 0xed };
//GUID WusbGUID = {0x88BAE032, 0x5A81, 0x49f0, 0xBC, 0x3D, 0xA4, 0xFF, 0x13, 0x82, 0x16, 0xD6 };

INT_PTR WINAPI WinProcCallback(HWND __hWnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL DoRegisterDeviceInterfaceToHwnd(GUID InterfaceClassGuid, HWND __hWnd, HDEVNOTIFY *hDeviceNotify);

bool UsbController::startNotifyUsbAddedRemoved(QString &errmsg)
{
    WNDCLASSEX wndClass;

    wndClass.cbSize = sizeof(wndClass);
    wndClass.style = 0;
    wndClass.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
    wndClass.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProcCallback);
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hIcon = LoadIcon(0, IDI_APPLICATION);
    wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wndClass.hCursor = LoadCursor(0, IDC_ARROW);
    wndClass.lpszClassName = WND_CLASS_NAME;
    wndClass.lpszMenuName = NULL;
    wndClass.hIconSm = LoadIcon(0, IDI_APPLICATION);

    if (!RegisterClassEx(&wndClass))
    {
        FormatErrorMsg("RegisterClassEx: ", errmsg);
        return false;
    }

    HINSTANCE hInstance = (HINSTANCE)::GetModuleHandle(NULL);
    __hWnd = CreateWindowEx(
                    WS_EX_CLIENTEDGE | WS_EX_APPWINDOW,
                    WND_CLASS_NAME,
                    WND_APP_NAME,
                    WS_OVERLAPPEDWINDOW, // style
                    CW_USEDEFAULT, 0,
                    0, 0,
                    NULL, NULL,
                    hInstance,
                    NULL);

    if ( __hWnd == NULL )
    {
        FormatErrorMsg("CreateWindowEx: ", errmsg);
        return false;
    }

    return true;
}

INT_PTR WINAPI WinProcCallback(HWND __hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    LRESULT lRet = 1;
    static HDEVNOTIFY hDeviceNotify;
    static HWND hEditWnd;
    static ULONGLONG msgCount = 0;

    switch (message)
    {
    case WM_CREATE:
        //
        // This is the actual registration., In this example, registration
        // should happen only once, at application startup when the window
        // is created.
        //
        // If you were using a service, you would put this in your main code
        // path as part of your service initialization.
        //
        if ( ! DoRegisterDeviceInterfaceToHwnd( WceusbshGUID, __hWnd, &hDeviceNotify) )
        {
            // Terminate on failure.
            //ErrorHandler(TEXT("DoRegisterDeviceInterfaceToHwnd"));
            ExitProcess(1);
        }

        //
        // Make the child window for output.
        //
        hEditWnd = CreateWindow(TEXT("EDIT"),// predefined class
                                NULL,        // no window title
                                WS_CHILD | WS_VISIBLE | WS_VSCROLL |
                                ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
                                0, 0, 0, 0,  // set size in WM_SIZE message
                                __hWnd,        // parent window
                                (HMENU)1,    // edit control ID
                                (HINSTANCE) GetWindowLong(__hWnd, GWL_HINSTANCE),
                                NULL);       // pointer not needed

        if ( hEditWnd == NULL )
        {
            // Terminate on failure.
            ExitProcess(1);
        }
        // Add text to the window.
        SendMessage(hEditWnd, WM_SETTEXT, 0,
            (LPARAM)TEXT("Registered for USB device notification...\n"));

        break;

    case WM_SETFOCUS:
        SetFocus(hEditWnd);

        break;

    case WM_SIZE:
        // Make the edit control the size of the window's client area.
        MoveWindow(hEditWnd,
                   0, 0,                  // starting x- and y-coordinates
                   LOWORD(lParam),        // width of client area
                   HIWORD(lParam),        // height of client area
                   TRUE);                 // repaint window

        break;

    case WM_DEVICECHANGE:
        {
            //
            // This is the actual message from the interface via Windows messaging.
            // This code includes some additional decoding for this particular device type
            // and some common validation checks.
            //
            // Note that not all devices utilize these optional parameters in the same
            // way. Refer to the extended information for your particular device type
            // specified by your GUID.
            //

            // Output some messages to the window.
            UsbController *pusbctl;
            switch (wParam)
            {
            case DBT_DEVICEARRIVAL:
                msgCount++;
                pusbctl = UsbController::instance();
                pusbctl->signalDeviceArrival();
                break;
            case DBT_DEVICEREMOVECOMPLETE:
                msgCount++;
                pusbctl = UsbController::instance();
                pusbctl->signalDeviceRemoval();
                break;
            case DBT_DEVNODES_CHANGED:
                msgCount++;
                pusbctl = UsbController::instance();
                pusbctl->signalDeviceAddedRemoved();
                break;
            default:
                msgCount++;
                break;
            }
        }
        break;

    default:
        // Send all other messages on to the default windows handler.
        lRet = DefWindowProc(__hWnd, message, wParam, lParam);
        break;
    }

    return lRet;
}

BOOL DoRegisterDeviceInterfaceToHwnd(GUID InterfaceClassGuid, HWND __hWnd, HDEVNOTIFY *hDeviceNotify)
{
    DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;

    ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
    NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    //NotificationFilter.dbcc_devicetype = DEVICE_NOTIFY_ALL_INTERFACE_CLASSES;
    NotificationFilter.dbcc_classguid = InterfaceClassGuid;

    *hDeviceNotify = RegisterDeviceNotification(
        __hWnd,                       // events recipient
        &NotificationFilter,        // type of device
        DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
        );

    if ( NULL == *hDeviceNotify )
    {
        return FALSE;
    }

    return TRUE;
}

If you read MSDN's documentation, it says: 如果您阅读MSDN的文档,它会说:

Detecting Media Insertion or Removal 检测媒体插入或删除

Windows sends all top-level windows a set of default WM_DEVICECHANGE messages when new devices or media (such as a CD or DVD) are added and become available, and when existing devices or media are removed. 当新设备或介质(如CD或DVD)添加并可用时,以及删除现有设备或介质时,Windows会向所有顶级窗口发送一组默认 WM_DEVICECHANGE消息。 You do not need to register to receive these default messages. 您无需注册即可接收这些默认消息。 See the Remarks section in RegisterDeviceNotification for details on which messages are sent by default . 有关默认情况下发送哪些消息的详细信息,请参阅RegisterDeviceNotification中的“备注”部分

RegisterDeviceNotification function RegisterDeviceNotification函数

Any application with a top-level window can receive basic notifications by processing the WM_DEVICECHANGE message. 具有顶级窗口的任何应用程序都可以通过处理WM_DEVICECHANGE消息来接收基本通知 Applications can use the RegisterDeviceNotification function to register to receive device notifications . 应用程序可以使用RegisterDeviceNotification函数进行注册以接收设备通知
... ...
The DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE events are automatically broadcast to all top-level windows for port devices . DBT_DEVICEARRIVAL和DBT_DEVICEREMOVECOMPLETE事件会自动广播到端口设备的所有顶级窗口 Therefore, it is not necessary to call RegisterDeviceNotification for ports , and the function fails if the dbch_devicetype member is DBT_DEVTYP_PORT . 因此,没有必要为端口调用RegisterDeviceNotification,如果dbch_devicetype成员是DBT_DEVTYP_PORT ,则函数将失败。

DEV_BROADCAST_HDR structure DEV_BROADCAST_HDR结构

DBT_DEVTYP_PORT DBT_DEVTYP_PORT
0x00000003 0x00000003

Port device (serial or parallel) . 端口设备(串行或并行) This structure is a DEV_BROADCAST_PORT structure. 此结构是DEV_BROADCAST_PORT结构。

A USB device is not a serial/parallel port. USB设备不是串行/并行端口。 It is a Device Interface ( DBT_DEVTYP_DEVICEINTERFACE ) instead. 它是一个设备接口( DBT_DEVTYP_DEVICEINTERFACE )。 And DBT_DEVICEARRIVAL / DBT_DEVICEREMOVECOMPLETE are not sent for DBT_DEVTYP_DEVICEINTERFACE devices by default . 默认情况下,不会为DBT_DEVTYP_DEVICEINTERFACE设备发送DBT_DEVICEARRIVAL / DBT_DEVICEREMOVECOMPLETE If you want them, you have to use RegisterDeviceNotification() to request them. 如果需要它们,则必须使用RegisterDeviceNotification()来请求它们。

It looks like your code is based on this MSDN example: 看起来您的代码基于此MSDN示例:

Registering for Device Notification 注册设备通知

In that code, WceusbshGUID is defined as {25dbce51-6c8f-4a72-8a6d-b54c2b4fc835} , which is commented as being the class guid for USB serial host PnP drivers . 在该代码中, WceusbshGUID定义为{25dbce51-6c8f-4a72-8a6d-b54c2b4fc835} ,它被评为USB串行主机PnP驱动程序的类guid。 According to this MSDN page: 根据这个MSDN页面:

System-Defined Device Setup Classes Available to Vendors 供应商可用的系统定义的设备设置类

That guid is the class guid for Windows CE USB ActiveSync Devices (which is more consistent with the Wceusb... prefix used in the code). 该guid是Windows CE USB ActiveSync设备的类guid(与代码中使用的Wceusb...前缀更加一致)。 Also on that same page is {88BAE032-5A81-49f0-BC3D-A4FF138216D6} for USB Device ( all USB devices that do not belong to another class ). 对于USB设备 ,同一页面上也是{88BAE032-5A81-49f0-BC3D-A4FF138216D6}所有不属于其他类别的 USB设备 )。

The following CodeProject article: 以下CodeProject文章:

Detecting Hardware Insertion and/or Removal 检测硬件插入和/或移除

Mentions {a5dcbf10-6530-11d2-901f-00c04fb951ed} for USB Raw Device . 提及USB原始设备的 {a5dcbf10-6530-11d2-901f-00c04fb951ed} That same guid is documented on MSDN as GUID_DEVINTERFACE_USB_DEVICE (the naming of which probably stems back to pre-XP days when the naming of class guids and interface guids was not well separated ). 同样的guid在MSDN上被记录为GUID_DEVINTERFACE_USB_DEVICE (其命名可能源于XP之前的日期,当时类guid和接口GUID_DEVINTERFACE_USB_DEVICE的命名没有很好地分开 )。

So when you call RegisterDeviceNotification() with a specific class guid, make sure it is the correct class guid, as you are going to get device events for only that specific type of device. 因此,当您使用特定类guid调用RegisterDeviceNotification()时,请确保它是正确的类guid,因为您将仅为该特定类型的设备获取设备事件。 It is likely that your USB device is using a different class guid than the one you are registering, and that is why you are not getting the device events you are expecting. 您的USB设备可能正在使用与您注册的类别不同的​​类别guid,这就是您没有获得预期的设备事件的原因。

If you want to detect any USB device regardless of its class guid (and there are several USB class guids defined), you can use the DEVICE_NOTIFY_ALL_INTERFACE_CLASSES flag when calling RegisterDeviceNotification() , then the class guid will be ignored. 如果要检测任何 USB设备而不管其类guid(并且定义了多个USB类guid),则可以在调用RegisterDeviceNotification()时使用DEVICE_NOTIFY_ALL_INTERFACE_CLASSES标志,然后将忽略类guid。 In the DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE messages (assuming you are able to get them now), the reported dbcc_classguid will tell you the actual class guid, and the reported dbcc_name will begin with the \\\\?\\USB: prefix. DBT_DEVICEARRIVALDBT_DEVICEREMOVECOMPLETE消息中(假设您现在可以获取它们),报告的dbcc_classguid将告诉您实际的类guid,并且报告的dbcc_name将以\\\\?\\USB:前缀开头。

One last thing - you are only going to get DBT_DEVICE... messages if a USB device is inserted/removed while your app is already running. 最后一件事 - 如果在您的应用程序已经运行时插入/移除USB设备,您将只获得DBT_DEVICE...消息。 To detect if a USB device is already plugged in when your app starts, you have to use SetupAPI functions ( SetupDiGetClassDevs() , SetupDiEnumDeviceInterfaces() , SetupDiGetDeviceInterfaceDetail() , etc) to enumerate the available devices. 要在应用程序启动时检测USB设备是否已插入,您必须使用SetupAPI函数( SetupDiGetClassDevs()SetupDiEnumDeviceInterfaces()SetupDiGetDeviceInterfaceDetail()等)来枚举可用的设备。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在我的Qt应用程序中收到了WM_DEVICECHANGE但没有收到DBT_DEVICEARRIVAL - WM_DEVICECHANGE received but not DBT_DEVICEARRIVAL in my Qt app Win32 设备事件:未在 DBT_DEVICEARRIVAL 事件上接收 DBT_DEVTYP_VOLUME - Win32 Device Events : Not receiving DBT_DEVTYP_VOLUME on DBT_DEVICEARRIVAL event 修改时间,直到发送DBT_DEVICEREMOVECOMPLETE - Modify time until DBT_DEVICEREMOVECOMPLETE is sent DBT_DEVICEARRIVAL消息正在发送无法识别的类GUID - DBT_DEVICEARRIVAL Message is sending unrecognisable Class GUIDs WM_DEVICECHANGE消息未发送到WndProc - C ++ - WM_DEVICECHANGE Messages Are Not Sent to WndProc - C++ 调用WM_SETTEXT后,Win32 C ++子类化标签未收到WM_PAINT - Win32 C++ Subclassed label not receiving WM_PAINT after calling WM_SETTEXT MFC C++ 通过 WM_DEVICECHANGE 通知区分两个设备 - MFC C++ differentiate between two devices from WM_DEVICECHANGE notifications WM_DEVICECHANGE lParam为空 - WM_DEVICECHANGE lParam is null 在Win32 C / C ++ API中,Windows消息(例如WM_LBUTTONDOWN,WM_COMMAND和WM_CLOSE)在哪里定义? - Where are Windows Messages such as WM_LBUTTONDOWN, WM_COMMAND, and WM_CLOSE defined in the win32 C/C++ API? 在 WM_COMMAND win32 GUI C++ 中处理 WM_LBUTTONDOWN 和 WM_LBUTTONUP - Handling WM_LBUTTONDOWN and WM_LBUTTONUP inside WM_COMMAND win32 GUI C++
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM