USB HID学习:MFC检测USB拔插事件

MFC具备检测设备的消息,但需要手动添加。针对USB设备,需要注册对应的GUID方可。本文对此进行简单记录。
本省略对MFC机制的描述,仅描述主要的模块代码。

一、步骤

Dbt.h头文件引用

在stdafx.h(或有关的头文件)添加Dbt.h头文件的引用:

#include <Dbt.h>

注册USB设备GUID

在对话框初始化函数中注册:

BOOL CFooDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // 将“关于...”菜单项添加到系统菜单中。

    // ...

    // 注册HID事件
    DEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface;

    memset(&DevBroadcastDeviceInterface, 0, sizeof(DEV_BROADCAST_DEVICEINTERFACE));
    DevBroadcastDeviceInterface.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    DevBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    // HID设备的GUID,可在设备管理器中查询,经查结果如下:
    // {745a17a0-74d3-11d0-b6fe-00a0c90f57da}
    // 注:使用真实的HID的GUID,反正检测不出来,如果是其它的GUID,所有USB事件都能检测出
    const GUID GUID_DEVINTERFACE_LIST[] = {
        { 0xA5DCBF10, 0x6530, 0x11D2,{ 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } }, // USB设备
        { 0x53f56307, 0xb6bf, 0x11d0,{ 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } }, // 磁盘(U盘)
        { 0x4D1E55B2, 0xF16F, 0x11CF,{ 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } }, // HID
        { 0x745A17A0, 0x74D3, 0x11D0,{ 0xB6, 0xFE, 0x00, 0xA0, 0xC9, 0x0F, 0x57, 0xDA } }, // 另一个HID
        { 0xad498944, 0x762f, 0x11d0,{ 0x8d, 0xcb, 0x00, 0xc0, 0x4f, 0xc3, 0x35, 0x8c } } }; // 网卡
    // 可以循环注册所有列出的GUID,此处只使用一种
    DevBroadcastDeviceInterface.dbcc_classguid = GUID_DEVINTERFACE_LIST[2];

    RegisterDeviceNotification(this->GetSafeHwnd(), &DevBroadcastDeviceInterface, DEVICE_NOTIFY_WINDOW_HANDLE);

}

说明1:不同的USB设备使用不同的GUID表示。在注册时需要指定要检测哪一类,本文针对HID,有兴趣者可使用其它来测试。
说明2:笔者使用的键盘有多个USB设备,其一为HID设备,在设备管理器中查询其类GUID为745a17a0-74d3-11d0-b6fe-00a0c90f57da
说明3:查询到的GUID与代码GUID结构体本质一样,形式不同。具体参考定义。

消息函数声明

在对话框头文件声明消息函数:

afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD dwData);

消息声明

在对话框实现文件中添加ON_WM_DEVICECHANGE消息:

BEGIN_MESSAGE_MAP(CFooDlg, CDialogEx)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_MESSAGE(WM_SHOWTASK, OnSystemtray)
    // ...
    ON_WM_SIZE()
    ON_WM_DESTROY()
    ON_WM_DEVICECHANGE() // USB HID设备检测消息
END_MESSAGE_MAP()

消息响应函数实现

下面实现OnDeviceChange函数:

BOOL CFooDlg::OnDeviceChange(UINT nEventType, DWORD dwData)
{
    DEV_BROADCAST_DEVICEINTERFACE* dbd = (DEV_BROADCAST_DEVICEINTERFACE*)dwData;
    
    wchar_t vid[32] = { 0 };
    int offset = 4 * 3 + sizeof(GUID)+10;
    CString szInfo;
    int sendtype = 0;
    switch (nEventType)
    {
    case DBT_DEVICEARRIVAL:
    {
        memcpy(vid, (char*)dwData + offset, 32);
        wchar_t* rr = wcsstr(vid, L"VID_AA55"); // !! 可过滤特定设备ID,下同
        if (rr == NULL)
        {
            return FALSE;
        }

        szInfo.Format(L"提示信息: 设备已插入.\n");

        this->GetDlgItem(IDC_STC_DEVINFO)->SetWindowText(szInfo);

    }
    break;
    case DBT_DEVICEREMOVECOMPLETE:
    {
        // 注:dbd->dbcc_name只有1个字节,不能直接用其来做源地址拷贝,直接使用偏移,上同
        //wmemcpy(vid, (wchar_t*)dwData + offset, 32);
        memcpy(vid, (char*)dwData + offset, 32);
        wchar_t* rr = wcsstr(vid, L"VID_AA55");
        if (rr == NULL)
        {
            return FALSE;
        }

        szInfo.Format(L"提示信息: 设备已移除.\n");

        this->GetDlgItem(IDC_STC_DEVINFO)->SetWindowText(szInfo);

    }
    break;

    default:
    {
        //szInfo.Format(L"[%d]got event: %d\n", cnt, nEventType);
        //this->GetDlgItem(IDC_STC_DEVINFO)->SetWindowText(szInfo);
    }
    break;
    }

    return TRUE;
}

注1:只有注册的设备,nEventType才有DBT_DEVICEARRIVAL、DBT_DEVICEREMOVECOMPLETE(当然也有其它值,按下不提),如果不注册,nEventType的值为7。
注2:查了些资料,说nEventType值不同,dwData亦不同。但本文没有深入研究。
注3:如果要针对某一种设备,如所属为HID,但厂家不同,则可以通过查找VID关键字来过滤。文中代码使用偏移量外加字符串搜索来实现,仅作示例,有些绕,但能实现功能。

后在 Windows 7 系统上用 VS 2015 编译,发现 ON_WM_DEVICECHANGE 出错,经排查,定位到

afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD dwData);

声明不兼容。修改为:

afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD_PTR dwData);

可解决编译问题,并且在 Win10 也能正常编译。

二、测试

使用

0xA5DCBF10, 0x6530, 0x11D2,{ 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED }

可以检测出所有的USB设备事件。包括U盘、键盘等。
使用

0x53f56307, 0xb6bf, 0x11d0,{ 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b }

只能检测出U盘事件。
使用

0x745A17A0, 0x74D3, 0x11D0,{ 0xB6, 0xFE, 0x00, 0xA0, 0xC9, 0x0F, 0x57, 0xDA }

检测不出HID事件(此处原因未知)。
但是,使用

0x4D1E55B2, 0xF16F, 0x11CF,{ 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 }

可以检测出HID事件。

三、小结

本文不过多涉及检测原理,代码测试通过。
需要指出的是,在 Windows 上使用 Qt 编程检测 USB 事件,也是使用本文所提到的技术,包括注册、响应事件。毕竟,无论 MFC 还是 Qt 程序,都是在 Windows 上运行的,可谓殊途同归。当然,如 Linux 或 MacOS 系统,机制已然不同,不在此列。

发布了481 篇原创文章 · 获赞 244 · 访问量 110万+

猜你喜欢

转载自blog.csdn.net/subfate/article/details/104272090
usb