简体   繁体   English

使用 C++ 在 Windows 中检测 USB 插入/移除事件

[英]Detecting USB Insertion / Removal Events in Windows using C++

I am writing an extension for an existing application that needs to handle USB insertion/removal events.我正在为需要处理 USB 插入/移除事件的现有应用程序编写扩展。 I know the VID/PID of the device of interest.我知道感兴趣设备的 VID/PID。 However, I don't have access to the window handle, so I don't know if RegisterDeviceNotification will be of much use, unless there is a way to obtain the handle via the WINAPI .但是,我无权访问窗口句柄,所以我不知道RegisterDeviceNotification是否会有很多用处,除非有办法通过WINAPI获取句柄。 What would be the best way to detect USB insertion/removal events with C++?使用 C++ 检测 USB 插入/移除事件的最佳方法是什么?

This sample code on the Microsoft website shows how to receive event notifications via WMI: Microsoft 网站上的此示例代码显示了如何通过 WMI 接收事件通知:

How could it be modified to receive USB insertion/removal events?如何修改它以接收 USB 插入/移除事件? Or, is there another way I should be going about this?或者,我还有其他方法可以解决这个问题吗? I am using Visual Studio 2008. Thanks.我正在使用 Visual Studio 2008。谢谢。

ADDITIONAL INFO附加信息

This is what I have so far (minus error-handling):这是我到目前为止所拥有的(减去错误处理):

DEFINE_GUID(GUID_INTERFACE_CP210x, 0x993f7832, 0x6e2d, 0x4a0f, 0xb2, 0x72, 0xe2, 0xc7, 0x8e, 0x74, 0xf9, 0x3e);

MyClass::MyClass()
{
    // Generate message-only window
    _pWndClassEx = (WNDCLASSEX *)malloc( sizeof(WNDCLASSEX) );
    memset( _pWndClassEx, 0, sizeof(WNDCLASSEX) );
    _pWndClassEx->cbSize = sizeof(WNDCLASSEX);
    _pWndClassEx->lpfnWndProc = (WNDPROC)WndProc; // function which will handle messages
    _pWndClassEx->hInstance = GetCurrentModule();
    _pWndClassEx->lpszClassName = pClassName;
    atom = RegisterClassEx( _pWndClassEx );
    _hWnd = CreateWindowEx( 0, pClassName, pWindowName, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL );

    // Register the USB device for notification
    _pDevIF = (DEV_BROADCAST_DEVICEINTERFACE *)malloc( sizeof(DEV_BROADCAST_DEVICEINTERFACE) );
    memset( _pDevIF, 0, sizeof(DEV_BROADCAST_DEVICEINTERFACE) );
    _pDevIF->dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    _pDevIF->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    _pDevIF->dbcc_classguid = GUID_INTERFACE_CP210x;
    _hNotifyDevNode = RegisterDeviceNotification( _hWnd, _pDevIF, DEVICE_NOTIFY_WINDOW_HANDLE );
}

static bool OnDeviceChange(UINT nEventType, DWORD dwData)
{
    switch ( nEventType )
    {
    case DBT_DEVICEARRIVAL:
        // A device has been inserted adn is now available.
        break;

    case DBT_DEVICEREMOVECOMPLETE:
        // Device has been removed.
        break;

    default:
        break;
    }

    return true;
}

static LRESULT WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    switch ( message )
    {
    case WM_DEVICECHANGE:
        OnDeviceChange( wParam, lParam ); // Set breakpoint (never gets here)
        break;

    default:
        break;
    }

    return DefWindowProc(hwnd, message, wParam, lParam);
}

The PC gets into WndProc , but not when I remove/insert my USB device. PC 进入WndProc ,但在我移除/插入 USB 设备时不会。 The PC never seems to get into OnDeviceChange . PC 似乎永远不会进入OnDeviceChange Any tips would be appreciated.任何提示将不胜感激。 I need to handle unexpected insertions/removals of the USB device.我需要处理 USB 设备的意外插入/移除。 If it makes a difference, the USB device appears as a virtual COM port to Windows.如果有所不同,USB 设备将作为 Windows 的虚拟 COM 端口出现。 Thanks.谢谢。

Aditional info: Calling CreateWindowEx using the class atom returned by RegisterClassEx fails with the error message, "Cannot find window class."附加信息:使用RegisterClassEx返回的类atom调用CreateWindowEx失败并显示错误消息“找不到窗口类”。

_hWnd = CreateWindowEx( 0, (LPCTSTR)&atom, pWindowName, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL );

NEW APPROACH新方法

I'm also trying this new approach.我也在尝试这种新方法。 I'm trying to get write a message-only window to receive device change notification messages for a USB device.我正在尝试编写一个仅消息窗口来接收 USB 设备的设备更改通知消息。 I am using MFC, C++, and Visual Studio 2008. Everything compiles, and it runs without crashing or locking up, but the event handler is never triggered.我正在使用 MFC、C++ 和 Visual Studio 2008。一切都可以编译,并且它在运行时不会崩溃或锁定,但从未触发事件处理程序。 The device of interest is installed on Windows as a virtual COM port.感兴趣的设备作为虚拟 COM 端口安装在 Windows 上。

My main application instantiates the class described below then waits for a character input from the keyboard polling using a while loop.我的主应用程序实例化下面描述的类,然后使用 while 循环等待来自键盘轮询的字符输入。 It is during this wait time that I remove and insert my USB device expecting the event to get fired.正是在这段等待时间内,我移除并插入了我的 USB 设备,期待事件被触发。

class CMessageOnlyWindow : public CWnd
{
    DECLARE_DYNAMIC(CMessageOnlyWindow)
private:
    DEV_BROADCAST_DEVICEINTERFACE * _pDevIF; // The notification filter.
    HDEVNOTIFY _hNotifyDev;             // The device notification handle.
public:
    CMessageOnlyWindow();
    virtual ~CMessageOnlyWindow();
protected:
    afx_msg BOOL OnDeviceChange( UINT nEventType, DWORD dwData );
private:
    void RegisterNotification( void );
    void UnregisterNotification( void );
protected:
    DECLARE_MESSAGE_MAP()               // Must be last.
};

For simplicity, I've removed all the cleanup and error-handling:为简单起见,我删除了所有清理和错误处理:

DEFINE_GUID(GUID_INTERFACE_CP210x, 0x993f7832, 0x6e2d, 0x4a0f, \
    0xb2, 0x72, 0xe2, 0xc7, 0x8e, 0x74, 0xf9, 0x3e);

IMPLEMENT_DYNAMIC(CMessageOnlyWindow, CWnd)

CMessageOnlyWindow::CMessageOnlyWindow()
{
    CString cstrWndClassName = ::AfxRegisterWndClass( NULL );
    BOOL bCreated = this->CreateEx( 0, cstrWndClassName,
        L"CMessageOnlyWindow", 0, 0, 0, 0, 0, HWND_MESSAGE, 0 );
    this->RegisterNotification();
}

CMessageOnlyWindow::~CMessageOnlyWindow() {}

BEGIN_MESSAGE_MAP(CMessageOnlyWindow, CWnd)
    ON_WM_DEVICECHANGE()
END_MESSAGE_MAP()

afx_msg BOOL CMessageOnlyWindow::OnDeviceChange( UINT nEventType, DWORD dwData )
{
    switch ( nEventType ) // <-- Never gets here.
    {
    case DBT_DEVICEARRIVAL:
        break;

    case DBT_DEVICEREMOVECOMPLETE:
        break;

    default:
        break;
    }

    return TRUE;
}

void CMessageOnlyWindow::RegisterNotification(void)
{
    _pDevIF = (DEV_BROADCAST_DEVICEINTERFACE *)malloc( sizeof(DEV_BROADCAST_DEVICEINTERFACE) );
    memset( _pDevIF, 0, sizeof(DEV_BROADCAST_DEVICEINTERFACE) );
    _pDevIF->dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
    _pDevIF->dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
    _pDevIF->dbcc_classguid = GUID_INTERFACE_CP210x;
    _hNotifyDev = RegisterDeviceNotification( this->m_hWnd, _pDevIF, DEVICE_NOTIFY_WINDOW_HANDLE );
}

void CMessageOnlyWindow::UnregisterNotification(void)
{
    UnregisterDeviceNotification( _hNotifyDev );
}

Any thoughts or suggestions would be much appreciated.任何想法或建议将不胜感激。 If any details are missing, let me know, and I will be glad to add them.如果缺少任何细节,请告诉我,我会很乐意添加它们。 Thanks.谢谢。

Does the message-only window need to be started in a new thread, or does creating a new window automatically spin off a new thread?是否需要在新线程中启动仅消息窗口,还是创建新窗口会自动脱离新线程?

Create a dummy window that does nothing but wait for WM_DEVICECHANGE and register that window using RegisterDeviceNotification .创建一个只等待WM_DEVICECHANGE并使用RegisterDeviceNotification注册该窗口的虚拟窗口。 WMI is an overkill here, IMHO. WMI在这里是一种矫枉过正,恕我直言。

There is a MSDN sample specifically for your case, in native code.有一个专门针对您的案例的MSDN 示例,采用本机代码。

Registering for Device Notification注册设备通知

Better to do it this way than via WMI.这样做比通过 WMI 更好。

I followed your "new approach" and also found that OnDeviceChange wasn't being called.我遵循了您的“新方法”,还发现没有调用 OnDeviceChange。 The problem was that there was no message loop because it was a Console app.问题是没有消息循环,因为它是一个控制台应用程序。 Calling the following function at regular intervals fixed it.定期调用以下函数修复了它。

void check_for_device_change()
{
    MSG msg; 

    const int val = PeekMessage( &msg, 0, 0, 0, PM_REMOVE );

    if( val > 0 )
    { 
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    } 
}

This is another way for detecting the INSERTION & REMOVAL of USB storage devices.这是另一种检测 USB 存储设备插入和移除的方法。

This c++ code detect INSERTION and REMOVAL both of USB Storage devices.此 C++ 代码检测插入和移除两个 USB 存储设备。

This also detect Multiple insertion and removal of USB devices at same time.这也会同时检测多个 USB 设备的插入和移除。

c++ code: Tested in VISUAL STUDIO 2015 C++ 代码:在 VISUAL STUDIO 2015 中测试

You can also check for other types of devices for removal and insertion.您还可以检查是否有其他类型的设备进行移除和插入。 Just fill passed char array to other types of devices in if else of the code in function getUSBStorageDeviceList()只需在函数getUSBStorageDeviceList()中的if else代码中将传递的字符数组填充到其他类型的设备

    #include "stdafx.h"
    #include <stdio.h>
    #include <time.h>
    #include <windows.h>
    #include <string>
    #include<iostream>

    using namespace std;

    #define MAX_LETTER 26
    char PREV_DRIVE_LIST[MAX_LETTER];
    char NEW_DRIVE_LIST[MAX_LETTER];

    /* To GET DRIVE LIST in char ARRAY */
    void getUSBStorageDeviceList(char drive[]) {

        int count = 0;

        char szLogicalDrives[MAX_PATH];
        size_t size = strlen(szLogicalDrives) + 1;
        wchar_t* text = new wchar_t[size];

        size_t outSize;
        mbstowcs_s(&outSize, text, size, szLogicalDrives, size - 1);

        DWORD dwResult = GetLogicalDriveStrings(MAX_PATH, text); // text = szLogicalDrives
        WCHAR* szSingleDrive = text;

        while (*szSingleDrive)
        {
            UINT nDriveType = GetDriveType(szSingleDrive);

        //  printf("\nFUNC: getRemovableDisk, Drive Name%d= %s", ++count, szSingleDrive);

            if (nDriveType == DRIVE_UNKNOWN) {
            //  cout << "\nDrive type : Unknown: The drive type cannot be determined." << endl;
            }
            else if (nDriveType == DRIVE_NO_ROOT_DIR) {
            //  cout << "\nDrive type : Invalid Root Directory Media: The root path is invalid." << endl;
            }
            else if (nDriveType == DRIVE_REMOVABLE) {
            //  cout << "\nDrive type :  Removable Media:" << endl;
                char letter = szSingleDrive[0];
                drive[letter - 65] = letter;
            }
            else if (nDriveType == DRIVE_FIXED) {
                //cout << "\nDrive type : Fixed Media: " << endl;
            }
            else if (nDriveType == DRIVE_REMOTE) {
                //cout << "\nDrive type : Remote Media: The drive is a remote (network) drive.." << endl;
            }
            else if (nDriveType == DRIVE_CDROM) {
                //cout << "\nDrive type : CD ROM:   The drive is a CD-ROM drive." << endl;
            }
            else if (nDriveType == DRIVE_RAMDISK) {
                //cout << "\nDrive type : RAM Disk: The drive is a RAM disk." << endl;
            }

            szSingleDrive += wcslen(szSingleDrive) + 1; // next drive 
        }
    }

    int main(void) {

        int count = 0;
        for (int i = 0; i < MAX_LETTER; i++) {
            PREV_DRIVE_LIST[i] = '0';
            NEW_DRIVE_LIST[i] = '0';
        }
        // initial drive list which is already attached 
        getUSBStorageDeviceList(PREV_DRIVE_LIST);

        while (1) {

            getUSBStorageDeviceList(NEW_DRIVE_LIST);
            count = 1;

            /* Check for insertion and removabal*/

            for (int i = 0; i < MAX_LETTER; i++) {
                // check for new drive
                if ((NEW_DRIVE_LIST[i] >= 65 && NEW_DRIVE_LIST[i] <= 89) && (PREV_DRIVE_LIST[i] == '0')) {

                    printf("\nNew Device Inserted%d : %c", count++, NEW_DRIVE_LIST[i]);
                    PREV_DRIVE_LIST[i] = NEW_DRIVE_LIST[i];
                }
            }
                // fill ALl zero 
                for (int i = 0; i < MAX_LETTER; i++) {
                    NEW_DRIVE_LIST[i] = '0';
                }
                // update NEW drive list
                getUSBStorageDeviceList(NEW_DRIVE_LIST);

                for (int i = 0; i < MAX_LETTER; i++) {
                    // check for removed drive
                    if ((PREV_DRIVE_LIST[i] >= 65 && PREV_DRIVE_LIST[i] <= 89) && (NEW_DRIVE_LIST[i] == '0')) {
                        printf("\nDevice Removed%d : %c", count++, PREV_DRIVE_LIST[i]);
                        PREV_DRIVE_LIST[i] = NEW_DRIVE_LIST[i];
                    }
            }
                Sleep(500);
        }

        return 0;
    }

REMARK : This does not create Any windows.备注:这不会创建任何窗口。 This is console Application.这是控制台应用程序。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM