监听USB协议捕捉点击事件

#有偿求购功能代码,用于USB协议监听抓包数据
#用于监听到PC上载程序到PLC的事件动作。

#捕捉按钮点击事件 “将程序代码配置下载到PLC” 获取这个点击事件。

目前我们提供了 Logman 将事件捕获到事件跟踪日志文件中的方法,在命令提示符窗口中,输入以下命令以启动捕获会话
官方链接:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/usbcon/how-to-capture-a-usb-event-trace

logman create trace -n usbtrace -o %SystemRoot%\Tracing\usbtrace.etl -nb 128 640 -bs 128
logman update trace -n usbtrace -p Microsoft-Windows-USB-USBXHCI (Default,PartialDataBusTrace)
logman update trace -n usbtrace -p Microsoft-Windows-USB-UCX (Default,PartialDataBusTrace)
logman update trace -n usbtrace -p Microsoft-Windows-USB-USBHUB3 (Default,PartialDataBusTrace)
logman update trace -n usbtrace -p Microsoft-Windows-USB-USBPORT
logman update trace -n usbtrace -p Microsoft-Windows-USB-USBHUB
logman update trace -n usbtrace -p Microsoft-Windows-Kernel-IoTrace 0 2
logman start -n usbtrace

显示The command completed successfully.就成功捕获到了,抓补完会有个etl文件自己分析下就可以。

Logman 是 Windows 中内置的跟踪工具。 可以使用 Logman 将事件捕获到事件跟踪日志文件中。
官方USB技术支持:https://learn.microsoft.com/zh-cn/windows-hardware/drivers/usbcon/how-to-capture-a-usb-event-trace

csdn提供了 USB协议简介
https://blog.csdn.net/songze_lee/article/details/77658094?utm_source=csdn_ai_ada_ask_robot
希望对你有帮助

做USB加密狗时需要监测插拔事件,VC提供了WM_DEVICECHANGE消息,但还需要RegisterDeviceNotification()注册要检测的“Device Interface Class GUID”才能获取到有用信息。

设备相关的GUID有 Device Class GUID、Device Interface Class GUID、 Interface GUID。

Device Class GUID 是INF文件中Verison Section中指名的设备类GUID,它标志着此设备图标,此设备出现在哪一栏,电压电流等各种信息。
Interface GUID是指驱动程序中的GUID,用于使用API打开此设备进行一系列读写操作。
Device Interface Class GUID是Microsoft定义用来调用RegisterDeviceNotification()时所使用的一类GUID。
本次要使用的GUID_DEVINTERFACE_USB_DEVICE在MSDN里可以查到:

https://msdn.microsoft.com/en-us/library/windows/hardware/ff545972(v=vs.85).aspx

【关键代码】

在窗口初始化函数里:

/* 设置USB设备插拔监测 */
    DEV_BROADCAST_DEVICEINTERFACE stDev;
    memset(&stDev, 0, sizeof(DEV_BROADCAST_DEVICEINTERFACE));
    stDev.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    stDev.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    stDev.dbcc_classguid = { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
    RegisterDeviceNotification(GetSafeHwnd(), &stDev, DEVICE_NOTIFY_WINDOW_HANDLE);

BOOL OnDeviceChange(UINT nEventType, DWORD dwData)
{
    DEV_BROADCAST_DEVICEINTERFACE* dbd = (DEV_BROADCAST_DEVICEINTERFACE*)dwData;
 
    switch (nEventType)
    {
    case DBT_DEVICEREMOVECOMPLETE: // 移除USB设备
    {
        
    }
        break;
    case DBT_DEVICEARRIVAL:    // 发现USB设备
    {
        /* 新设备需的信息可能无法立即获取到,可以延时几秒再检测 */
    }
        break;
    default:
        break;
    }
 
    return TRUE;
}

  • 建议你看下这篇博客👉 :1.USB协议简介
  • 除此之外, 这篇博客: 知识点记忆中的 USB 总线的特性(常识) 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • USB 总线(通用串行总线)的特点有:① 即插即用 ;②热插拨;③有很强的连接能力,采用菊花链 形式将所有外设连接起来,且不损失带宽;④ 有很好的可扩充性,一个 USB 控制器可扩充高达 127 个外 部周边 USB 设备;⑤ 高速传输,速度可达 480Mbps 。USB 是串行总线, 不能 同时传输两位数据 。usb3.1早都超过480了

  • 您还可以看一下 任革老师的USB基础入门课程中的 USB基础小节, 巩固相关知识点
  • 以下回答由chatgpt基于相关博客总结生成:

    回答:

    很抱歉,参考资料提供的是如何使用串口连接树莓派,而不是如何监听USB协议并捕获点击事件。对于这个问题,我目前无法提供具体解决方案。但是,我可以给你一些提示和建议:

    1. 先确定你要监听的USB设备类型,例如鼠标、键盘、摄像头等,因为每种设备可能需要不同的监听方法。

    2. 搜索相关的USB监听工具或者库,例如USBPcap、libusb等,看看是否有符合你需求的。

    3. 如果没有现成的工具或库可用,你需要自己编写程序监听USB数据,具体实现可以采用以下步骤:

    a. 打开USB设备并设置监听规则,例如捕捉特定报文或特定类型的报文。

    b. 不断监听USB数据并将数据处理成合适的格式。

    c. 根据处理后的数据判断是否有点击事件触发,并执行相应的操作。

    1. 编写程序的语言和工具可以根据你的实际情况和偏好选择,例如C/C++、Python、Java等。
  1. USB数据监听
    使用libusb开源库监听指定USB设备的数据流,捕捉USB原始数据包,并保存到PC。主要代码如下:
python
import libusb  

dev = libusb.libusb_get_device_list() 
dev_handle = libusb.libusb_open_device_with_vid_pid(dev, 0x0403, 0x6001)

ep_in = libusb.libusb_get_endpoint(dev_handle, 0x81) 
buf = bytearray(64)  

while True: 
    len = libusb.libusb_interrupt_transfer(dev_handle, ep_in, buf, 64, 1000)
    if len > 0: 
        with open('usb_data.bin', 'ab') as f:
            f.write(buf[:len])

  1. USB数据包解析
    基于USB规范,解析每个USB数据包,获取包类型(中断、控制等)、传输方向、长度等信息。判断包是否包含目标数据,如果包含则进一步解析。
  2. 目标数据解析
    分析包含目标数据的USB包,提取事件时间、设备信息和数据详情等,实现指定事件的监听和信息捕捉。

可以参考下

 
#define UNICODE
 
#pragma comment(lib,"user32")
#pragma comment(lib,"gdi32")
#pragma comment(lib,"shell32")
 
// RegisterDeviceNotification.cpp
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
#include <dbt.h>
 
 
#define GWL_HINSTANCE -6
 
// This GUID is for all USB serial host PnP drivers, but you can replace it 
// with any valid device class guid.
GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
                      0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
 
// For informational messages and window titles.
PWSTR g_pszAppName;
 
void OutputMessage(
    HWND hOutWnd,
    WPARAM wParam,
    LPARAM lParam
)
// Routine Description:
//     Support routine.
//     Send text to the output window, scrolling if necessary.
 
// Parameters:
//     hOutWnd - Handle to the output window.
//     wParam  - Standard windows message code, not used.
//     lParam  - String message to send to the window.
 
// Return Value:
//     None
 
// Note:
//     This routine assumes the output window is an edit control
//     with vertical scrolling enabled.
 
//     This routine has no error-checking.
{
    LRESULT   lResult;
    LONG      bufferLen;
    LONG      numLines;
    LONG      firstVis;
 
    // Make writable and turn off redraw.
    lResult = SendMessage(hOutWnd, EM_SETREADONLY, FALSE, 0L);
    lResult = SendMessage(hOutWnd, WM_SETREDRAW, FALSE, 0L);
 
    // Obtain current text length in the window.
    bufferLen = SendMessage(hOutWnd, WM_GETTEXTLENGTH, 0, 0L);
    numLines = SendMessage(hOutWnd, EM_GETLINECOUNT, 0, 0L);
    firstVis = SendMessage(hOutWnd, EM_GETFIRSTVISIBLELINE, 0, 0L);
    lResult = SendMessage(hOutWnd, EM_SETSEL, bufferLen, bufferLen);
 
    // Write the new text.
    lResult = SendMessage(hOutWnd, EM_REPLACESEL, 0, lParam);
 
    // See whether scrolling is necessary.
    if (numLines > (firstVis + 1))
    {
        int        lineLen = 0;
        int        lineCount = 0;
        int        charPos;
 
        // Find the last nonblank line.
        numLines--;
        while (!lineLen)
        {
            charPos = SendMessage(
                hOutWnd, EM_LINEINDEX, (WPARAM)numLines, 0L);
            lineLen = SendMessage(
                hOutWnd, EM_LINELENGTH, charPos, 0L);
            if (!lineLen)
                numLines--;
        }
        // Prevent negative value finding min.
        lineCount = numLines - firstVis;
        lineCount = (lineCount >= 0) ? lineCount : 0;
 
        // Scroll the window.
        lResult = SendMessage(
            hOutWnd, EM_LINESCROLL, 0, (LPARAM)lineCount);
    }
 
    // Done, make read-only and allow redraw.
    lResult = SendMessage(hOutWnd, WM_SETREDRAW, TRUE, 0L);
    lResult = SendMessage(hOutWnd, EM_SETREADONLY, TRUE, 0L);
}
 
void ErrorHandler(
    LPCTSTR lpszFunction
)
// Routine Description:
//     Support routine.
//     Retrieve the system error message for the last-error code
//     and pop a modal alert box with usable info.
 
// Parameters:
//     lpszFunction - String containing the function name where 
//     the error occurred plus any other relevant data you'd 
//     like to appear in the output. 
 
// Return Value:
//     None
 
// Note:
//     This routine is independent of the other windowing routines
//     in this application and can be used in a regular console
//     application without modification.
{
 
    LPVOID lpMsgBuf;
    LPVOID lpDisplayBuf;
    DWORD dw = GetLastError();
 
    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_FROM_SYSTEM |
        FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL,
        dw,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf,
        0, NULL);
 
    // Display the error message and exit the process.
 
    lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
        (lstrlen((LPCTSTR)lpMsgBuf)
            + lstrlen((LPCTSTR)lpszFunction) + 40)
        * sizeof(TCHAR));
    if (!lpDisplayBuf) return;
    StringCchPrintf((LPTSTR)lpDisplayBuf,
        LocalSize(lpDisplayBuf) / sizeof(TCHAR),
        TEXT("%s failed with error %d: %s"),
        lpszFunction, dw, (LPCTSTR)lpMsgBuf);
    MessageBox(NULL, (LPCTSTR)lpDisplayBuf, g_pszAppName, MB_OK);
 
    LocalFree(lpMsgBuf);
    LocalFree(lpDisplayBuf);
}
 
BOOL DoRegisterDeviceInterfaceToHwnd(
    IN GUID InterfaceClassGuid,
    IN HWND hWnd,
    OUT HDEVNOTIFY* hDeviceNotify
)
// Routine Description:
//     Registers an HWND for notification of changes in the device interfaces
//     for the specified interface class GUID. 
 
// Parameters:
//     InterfaceClassGuid - The interface class GUID for the device 
//         interfaces. 
 
//     hWnd - Window handle to receive notifications.
 
//     hDeviceNotify - Receives the device notification handle. On failure, 
//         this value is NULL.
 
// Return Value:
//     If the function succeeds, the return value is TRUE.
//     If the function fails, the return value is FALSE.
 
// Note:
//     RegisterDeviceNotification also allows a service handle be used,
//     so a similar wrapper function to this one supporting that scenario
//     could be made from this template.
{
    DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
 
    ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
    NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    NotificationFilter.dbcc_classguid = InterfaceClassGuid;
 
    *hDeviceNotify = RegisterDeviceNotification(
        hWnd,                       // events recipient
        &NotificationFilter,        // type of device
        DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle
    );
 
    if (NULL == *hDeviceNotify)
    {
        ErrorHandler(L"RegisterDeviceNotification");
        return FALSE;
    }
 
    return TRUE;
}
 
void MessagePump(
    HWND hWnd
)
// Routine Description:
//     Simple main thread message pump.
//
 
// Parameters:
//     hWnd - handle to the window whose messages are being dispatched
 
// Return Value:
//     None.
{
    MSG msg;
    int retVal;
 
    // Get all messages for any window that belongs to this thread,
    // without any filtering. Potential optimization could be
    // obtained via use of filter values if desired.
 
    while ((retVal = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (retVal == -1)
        {
            ErrorHandler(L"GetMessage");
            break;
        }
        else
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
}
 
INT_PTR WINAPI WinProcCallback(
    HWND hWnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam
)
// Routine Description:
//     Simple Windows callback for handling messages.
//     This is where all the work is done because the example
//     is using a window to process messages. This logic would be handled 
//     differently if registering a service instead of a window.
 
// Parameters:
//     hWnd - the window handle being registered for events.
 
//     message - the message being interpreted.
 
//     wParam and lParam - extended information provided to this
//          callback by the message sender.
 
//     For more information regarding these parameters and return value,
//     see the documentation for WNDCLASSEX and CreateWindowEx.
{
    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(L"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.
            ErrorHandler(L"CreateWindow: Edit Control");
            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.
        //
        PDEV_BROADCAST_DEVICEINTERFACE b = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
        TCHAR strBuff[256];
 
        // Output some messages to the window.
        switch (wParam)
        {
        case DBT_DEVICEARRIVAL:
            msgCount++;
            StringCchPrintf(
                strBuff, 256,
                TEXT("Message %d: DBT_DEVICEARRIVAL\n"), (int)msgCount);
            break;
        case DBT_DEVICEREMOVECOMPLETE:
            msgCount++;
            StringCchPrintf(
                strBuff, 256,
                TEXT("Message %d: DBT_DEVICEREMOVECOMPLETE\n"), (int)msgCount);
            break;
        case DBT_DEVNODES_CHANGED:
            msgCount++;
            StringCchPrintf(
                strBuff, 256,
                TEXT("Message %d: DBT_DEVNODES_CHANGED\n"), (int)msgCount);
            break;
        default:
            msgCount++;
            StringCchPrintf(
                strBuff, 256,
                TEXT("Message %d: WM_DEVICECHANGE message received, value %d unhandled.\n"),
                (int)msgCount, wParam);
            break;
        }
        OutputMessage(hEditWnd, wParam, (LPARAM)strBuff);
    }
    break;
    case WM_CLOSE:
        if (!UnregisterDeviceNotification(hDeviceNotify))
        {
            ErrorHandler(L"UnregisterDeviceNotification");
        }
        DestroyWindow(hWnd);
        break;
 
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
 
    default:
        // Send all other messages on to the default windows handler.
        lRet = DefWindowProc(hWnd, message, wParam, lParam);
        break;
    }
 
    return lRet;
}
 
#define WND_CLASS_NAME TEXT("SampleAppWindowClass")
 
BOOL InitWindowClass()
// Routine Description:
//      Simple wrapper to initialize and register a window class.
 
// Parameters:
//     None
 
// Return Value:
//     TRUE on success, FALSE on failure.
 
// Note: 
//     wndClass.lpfnWndProc and wndClass.lpszClassName are the
//     important unique values used with CreateWindowEx and the
//     Windows message pump.
{
    WNDCLASSEX wndClass;
 
    wndClass.cbSize = sizeof(WNDCLASSEX);
    wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    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 = CreateSolidBrush(RGB(192, 192, 192));
    wndClass.hCursor = LoadCursor(0, IDC_ARROW);
    wndClass.lpszClassName = WND_CLASS_NAME;
    wndClass.lpszMenuName = NULL;
    wndClass.hIconSm = wndClass.hIcon;
 
 
    if (!RegisterClassEx(&wndClass))
    {
        ErrorHandler(L"RegisterClassEx");
        return FALSE;
    }
    return TRUE;
}
 
int __stdcall wWinMain(
    _In_ HINSTANCE hInstanceExe,
    _In_opt_ HINSTANCE, // should not reference this parameter
    _In_ PTSTR lpstrCmdLine,
    _In_ int nCmdShow)
{
    //
    // To enable a console project to compile this code, set
    // Project->Properties->Linker->System->Subsystem: Windows.
    //
 
    int nArgC = 0;
    PWSTR* ppArgV = CommandLineToArgvW(lpstrCmdLine, &nArgC);
    g_pszAppName = ppArgV[0];
 
    if (!InitWindowClass())
    {
        // InitWindowClass displays any errors
        return -1;
    }
 
    // Main app window
 
    HWND hWnd = CreateWindowEx(
        WS_EX_CLIENTEDGE | WS_EX_APPWINDOW,
        WND_CLASS_NAME,
        g_pszAppName,
        WS_OVERLAPPEDWINDOW, // style
        CW_USEDEFAULT, 0,
        640, 480,
        NULL, NULL,
        hInstanceExe,
        NULL);
 
    if (hWnd == NULL)
    {
        ErrorHandler(L"CreateWindowEx: main appwindow hWnd");
        return -1;
    }
 
    // Actually draw the window.
 
    ShowWindow(hWnd, SW_SHOWNORMAL);
    UpdateWindow(hWnd);
 
    // The message pump loops until the window is destroyed.
 
    MessagePump(hWnd);
 
    return 1;
}

可以参考下

#include <Windows.h>
#include <tchar.h>
#include <Dbt.h>
#include <setupapi.h>
#include <iostream>
#include <atlstr.h> // CString
using namespace std;
 
#pragma comment (lib, "Kernel32.lib")
#pragma comment (lib, "User32.lib")
 
#define THRD_MESSAGE_EXIT WM_USER + 1
const _TCHAR CLASS_NAME[] = _T("Sample Window Class");
 
HWND hWnd;
 
static const GUID GUID_DEVINTERFACE_LIST[] =
{
    // GUID_DEVINTERFACE_USB_DEVICE
    { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } },
    // GUID_DEVINTERFACE_DISK
    { 0x53f56307, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } },
    // GUID_DEVINTERFACE_HID, 
    { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } },
    // GUID_NDIS_LAN_CLASS
    { 0xad498944, 0x762f, 0x11d0, { 0x8d, 0xcb, 0x00, 0xc0, 0x4f, 0xc3, 0x35, 0x8c } }
     GUID_DEVINTERFACE_COMPORT
    //{ 0x86e0d1e0, 0x8089, 0x11d0, { 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73 } },
     GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR
    //{ 0x4D36E978, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } },
     GUID_DEVINTERFACE_PARALLEL
    //{ 0x97F76EF0, 0xF883, 0x11D0, { 0xAF, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x84, 0x5C } },
     GUID_DEVINTERFACE_PARCLASS
    //{ 0x811FC6A5, 0xF728, 0x11D0, { 0xA5, 0x37, 0x00, 0x00, 0xF8, 0x75, 0x3E, 0xD1 } }
};
 
void UpdateDevice(PDEV_BROADCAST_DEVICEINTERFACE pDevInf, WPARAM wParam)
{
    CString szDevId = pDevInf->dbcc_name + 4;
    int idx = szDevId.ReverseFind(_T('#'));
    szDevId.Truncate(idx);
    szDevId.Replace(_T('#'), _T('\\'));
    szDevId.MakeUpper();
 
    CString szClass;
    idx = szDevId.Find(_T('\\'));
    szClass = szDevId.Left(idx);
 
    CString szTmp;
    if (DBT_DEVICEARRIVAL == wParam) \
        szTmp.Format(_T("Adding %s\r\n"), szDevId.GetBuffer());
    else
        szTmp.Format(_T("Removing %s\r\n"), szDevId.GetBuffer());
 
    _tprintf(szTmp);
}
 
LRESULT DeviceChange(UINT message, WPARAM wParam, LPARAM lParam)
{
    if (DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam)
    {
        PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam;
        PDEV_BROADCAST_DEVICEINTERFACE pDevInf;
        PDEV_BROADCAST_HANDLE pDevHnd;
        PDEV_BROADCAST_OEM pDevOem;
        PDEV_BROADCAST_PORT pDevPort;
        PDEV_BROADCAST_VOLUME pDevVolume;
        switch (pHdr->dbch_devicetype)
        {
        case DBT_DEVTYP_DEVICEINTERFACE:
            pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
            UpdateDevice(pDevInf, wParam);
            break;
 
        case DBT_DEVTYP_HANDLE:
            pDevHnd = (PDEV_BROADCAST_HANDLE)pHdr;
            break;
 
        case DBT_DEVTYP_OEM:
            pDevOem = (PDEV_BROADCAST_OEM)pHdr;
            break;
 
        case DBT_DEVTYP_PORT:
            pDevPort = (PDEV_BROADCAST_PORT)pHdr;
            break;
 
        case DBT_DEVTYP_VOLUME:
            pDevVolume = (PDEV_BROADCAST_VOLUME)pHdr;
            break;
        }
    }
    return 0;
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_PAINT:
        break;
    case WM_SIZE:
        break;
    case WM_DEVICECHANGE:
        return DeviceChange(message, wParam, lParam);
    }
 
    return DefWindowProc(hWnd, message, wParam, lParam);
}
 
ATOM MyRegisterClass()
{
    WNDCLASS wc = { 0 };
    wc.lpfnWndProc = WndProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = CLASS_NAME;
    return RegisterClass(&wc);
}
 
bool CreateMessageOnlyWindow()
{
    hWnd = CreateWindowEx(0, CLASS_NAME, _T(""), WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL,       // Parent window    
        NULL,       // Menu
        GetModuleHandle(NULL),  // Instance handle
        NULL        // Additional application data
        );
 
    return hWnd != NULL;
}
 
void RegisterDeviceNotify()
{
    HDEVNOTIFY hDevNotify;
    for (int i = 0; i < sizeof(GUID_DEVINTERFACE_LIST) / sizeof(GUID); i++)
    {
        DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
        ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
        NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
        NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
        NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_LIST[i];
        hDevNotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
    }
}
 
DWORD WINAPI ThrdFunc(LPVOID lpParam)
{
    if (0 == MyRegisterClass())
        return -1;
 
    if (!CreateMessageOnlyWindow())
        return -1;
 
    RegisterDeviceNotify();
 
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (msg.message == THRD_MESSAGE_EXIT)
        {
            cout << "worker receive the exiting Message..." << endl;
            return 0;
        }
 
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
 
    return 0;
}
 
int main(int argc, char** argv)
{
    DWORD iThread;
    HANDLE hThread = CreateThread(NULL, 0, ThrdFunc, NULL, 0, &iThread);
    if (hThread == NULL) {
        cout << "error" << endl;
        return -1;
    }
 
    char chQtNum;
    do
    {
        cout << "enter Q/q for quit: " << endl;
        cin >> chQtNum;
 
    } while (chQtNum != 'Q' && chQtNum != 'q');
 
    PostThreadMessage(iThread, THRD_MESSAGE_EXIT, 0, 0);
    WaitForSingleObject(hThread, INFINITE);
    CloseHandle(hThread);
    return 0;
}