web-dev-qa-db-ja.com

C ++を使用したWindowsでのUSB挿入/削除イベントの検出

USB挿入/取り外しイベントを処理する必要がある既存のアプリケーションの拡張機能を書いています。対象のデバイスのVID/PIDを知っています。ただし、ウィンドウハンドルにアクセスできないため、RegisterDeviceNotificationを介してハンドルを取得する方法がない限り、WINAPIが大いに役立つかどうかはわかりません。 C++でUSB挿入/取り外しイベントを検出する最良の方法は何ですか?

MicrosoftのWebサイトにあるこのサンプルコード は、WMIを介してイベント通知を受信する方法を示しています。

USB挿入/取り外しイベントを受信するように変更するにはどうすればよいですか?または、これについて私がすべき別の方法はありますか? Visual Studio 2008を使用しています。ありがとうございます。

追加情報

これは私がこれまで持ってきたものです(エラー処理を除いて):

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);
}

PCがWndProcに入りますが、USBデバイスを取り外したり挿入したりしても入りません。 PCがOnDeviceChangeに侵入することはありません。任意のヒントをいただければ幸いです。 USBデバイスの予期しない挿入/取り外しを処理する必要があります。違いがある場合、USBデバイスはWindowsへの仮想COMポートとして表示されます。ありがとう。

追加情報:CreateWindowExから返されたクラスatomを使用してRegisterClassExを呼び出すと、エラーメッセージが表示されて失敗します、「ウィンドウクラスが見つかりません。」

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

新しいアプローチ

私もこの新しいアプローチを試みています。 USBデバイスのデバイス変更通知メッセージを受信するためのメッセージ専用ウィンドウを作成しようとしています。私はMFC、C++、およびVisual Studio 2008を使用しています。すべてがコンパイルされ、クラッシュまたはロックアップせずに実行されますが、イベントハンドラーはトリガーされません。対象のデバイスは、仮想COMポートとしてWindowsにインストールされます。

私のメインアプリケーションは、以下で説明するクラスをインスタンス化し、whileループを使用して、キーボードからの文字入力ポーリングを待ちます。イベントが発生することを期待して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.
};

簡単にするために、すべてのクリーンアップとエラー処理を削除しました。

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 );
}

ご意見やご提案をいただければ幸いです。詳細が不足している場合はお知らせください。追加させていただきます。ありがとう。

メッセージ専用ウィンドウを新しいスレッドで開始する必要がありますか、または新しいウィンドウを作成すると新しいスレッドが自動的にスピンオフしますか?

21
Jim Fell

WM_DEVICECHANGEを待つだけのダミーウィンドウを作成し、RegisterDeviceNotificationを使用してそのウィンドウを登録します。ここではWMIはやりすぎです。

15
kichik

MSDNサンプル は、ネイティブコード内に特別にあります。

デバイス通知の登録

WMI経由よりもこの方法で行う方が良いです。

8
Steve Townsend

私はあなたの「新しいアプローチ」に従いましたが、OnDeviceChangeが呼び出されていませんでした。問題は、コンソールアプリであるためメッセージループが発生しないことでした。次の関数を定期的に呼び出すことで修正されました。

void check_for_device_change()
{
    MSG msg; 

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

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

これは、USBストレージデバイスの挿入と削除を検出するもう1つの方法です。

このc ++コードは、両方のUSBストレージデバイスの挿入と削除を検出します。

これにより、USBデバイスの複数の挿入と取り外しが同時に検出されます

c ++コード:VISUAL STUDIO 2015でテスト済み

また、他の種類のデバイスの取り外しと挿入を確認することもできます。渡されたchar配列を他のタイプのデバイスに関数のコードのif elseに入力するだけですgetUSBStorageDeviceList()

    #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:これはウィンドウを作成しません。これはコンソールアプリケーションです。

0
jayprakash