Обнаружение событий вставки / удаления USB в Windows с использованием C ++

Я пишу расширение для существующего приложения, которое должно обрабатывать события вставки / удаления USB. Я знаю VID / PID интересующего устройства. Однако у меня нет доступа к дескриптору окна, поэтому я не знаю, будет ли RegisterDeviceNotification очень полезным, если нет способа получить дескриптор через WINAPI. Каким будет лучший способ обнаружения событий вставки / удаления USB с помощью C ++?

Этот образец кода на веб-сайте Microsoft показывает, как получать уведомления о событиях через 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);
}

ПК попадает в WndProc, но не когда я извлекаю / вставляю USB-устройство. Кажется, что ПК никогда не попадает в 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. Все компилируется и работает без сбоев и блокировок, но обработчик событий никогда не запускается. Интересующее устройство устанавливается в Windows как виртуальный COM-порт.

Мое основное приложение создает экземпляр класса, описанного ниже, затем ожидает ввода символа из опроса клавиатуры, используя цикл 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 );
}

Будем очень признательны за любые мысли или предложения. Если какие-то детали отсутствуют, дайте мне знать, и я буду рад их добавить. Спасибо.

Нужно ли запускать окно только для сообщений в новом потоке, или создание нового окна автоматически порождает новый поток?


person Jim Fell    schedule 02.11.2010    source источник
comment
Почему у вас нет доступа к оконной ручке? Вы используете это как услугу?   -  person Scott Chamberlain    schedule 02.11.2010
comment
@ Скотт Чемберлен: Это приложение, которое было исправлено на протяжении многих лет. Графический интерфейс - это Java, под которой находится слой C #, а под ним - набор библиотек DLL C ++. Вот где я работаю, модифицирую некоторые из C ++ и пишу новые библиотеки DLL. Я думаю, что дескриптор окна может принадлежать java.exe, но я не уверен. Это, по крайней мере, процесс, к которому я присоединяюсь, чтобы проверить свою DLL в работающем приложении (MSVS2008- ›Инструменты-› Присоединить к процессу).   -  person Jim Fell    schedule 02.11.2010


Ответы (4)


Создайте фиктивное окно, которое ничего не делает, кроме ожидания WM_DEVICECHANGE, и зарегистрируйте это окно с помощью RegisterDeviceNotification. WMI здесь излишество, ИМХО.

person kichik    schedule 02.11.2010
comment
Спасибо. Я добавил дополнительную информацию. Были бы полезны любые другие идеи, которые могут у вас возникнуть. - person Jim Fell; 03.11.2010
comment
Вы уверены, что у вас правильный GUID интерфейса? Тот, который вы использовали, предназначен для микросхемы USB2UART. Вы также пропустили dbcc_name. Я бы выбрал DEV_BROADCAST_VOLUME для начала, чтобы убедиться, что все работает. Вы можете легко протестировать вставку тома с диском под ключ. - person kichik; 04.11.2010
comment
@kichik: Да, я почти уверен, что GUID правильный. Интерфейсный чип - CP2103 от Silicon Labs. База знаний на их веб-сайте особо подчеркивает это. Я использую их отладочную плату, поэтому для этого устройства нет логического тома; это просто отключение порта USB. - person Jim Fell; 04.11.2010
comment
В ПОРЯДКЕ. Вы по-прежнему можете использовать DEV_BROADCAST_VOLUME с диском под ключ / флэш-накопителем, чтобы убедиться, что код обмена сообщениями работает правильно. - person kichik; 04.11.2010
comment
Скорее всего, вы будете использовать DBT_DEVTYP_DEVICEINTERFACE. DBT_DEVTYP_PORT может не отображаться в зависимости от вашего устройства. - person Brad; 05.11.2010
comment
Изменение типа дескриптора получателя, переданного RegisterDeviceNotification на DEVICE_NOTIFY_ALL_INTERFACE_CLASSES, заставило его начать работать. Вы знаете, почему это будет иметь значение, или возникнут ли проблемы, если я оставлю это так? - person Jim Fell; 05.11.2010
comment
Нет проблем оставить это так, если вы сами фильтруете сообщения. Но если это изменение сработает, то у вас, вероятно, возникнут проблемы с исходной структурой. Кстати, что касается нового кода, который вы опубликовали, у вас есть две проблемы: вы не должны отправлять указатель на атом, а на сам атом. Я бы написал все это с помощью чистого WinAPI и избегал беспорядка MFC, пока вы не заставили его работать. - person kichik; 05.11.2010
comment
@kichik: К сожалению, когда я попытался передать сам atom, я получил BSOD. MSDN указывает LPTR на атом. msdn.microsoft.com/en-us/library/aa930455.aspx В любом случае, это был старый подход, в котором я пытался использовать Win32 API. Это стало действительно неуправляемым. Найдя эту статью в The Code Project (codeproject.com/KB/dialog/messageonly.aspx), я решил попробовать MFC. Что вы имеете в виду под самой фильтрацией сообщений? ПК сейчас переходит в OnDeviceChange. Кроме того, в чем заключалась другая проблема? - person Jim Fell; 05.11.2010
comment
Вы сказали, что теперь получаете OnDeviceChange вызовы, когда начали использовать DEVICE_NOTIFY_ALL_INTERFACE_CLASSES, поэтому я предлагал вам фильтровать эти сообщения по идентификатору GUID в вашей собственной функции вместо того, чтобы позволять Windows фильтровать их за вас и находить ваш класс. Я бы начал с печати всех полученных вами GUID. Может быть, по какой-то причине он отличается от того, что вы предполагаете. - person kichik; 06.11.2010

Специально для вашего случая существует образец MSDN, в собственном коде.

Регистрация для уведомления устройства

Лучше сделать так, чем через WMI.

person Steve Townsend    schedule 02.11.2010
comment
Спасибо. Я добавил дополнительную информацию. Были бы полезны любые другие идеи, которые могут у вас возникнуть. - person Jim Fell; 03.11.2010
comment
@Jim Fell - опубликованный вами код никогда не вызывает OnDeviceChange. Вы хотели вызвать это в WndProc, когда увидели WM_DEVICECHANGE? обратите внимание, что пример кода не вызывает безусловно DefWindowProc, он делает это в случае default: - person Steve Townsend; 03.11.2010
comment
@ Стив Таунсенд: Вы хотите сказать, что вызов DefWindowProc должен происходить из случая default оператора switch? Я добавлю OnDeviceChange, как вы предложили, и посмотрю, как получится. Спасибо. - person Jim Fell; 03.11.2010
comment
Я не разбираюсь в обработке сообщений Windows, я просто продолжаю то, что делал пример кода. Но если это так, тогда «да». Вы обрабатываете сообщения, которые вам небезразличны, а затем передаете остальное DefWindowProc. - person Steve Townsend; 04.11.2010
comment
@ Стив Таунсенд: Понятно. Спасибо. Я думаю, что для целей этого приложения я не обязательно хочу переопределять DefWindowProc; Я хочу обрабатывать определенные сообщения ради моего приложения. Я реализовал ваше предложение относительно OnDeviceChange; это имеет смысл. - person Jim Fell; 04.11.2010
comment
Продолжай, Джим, это сложная логика, но я верю, что ты на правильном пути. - person Steve Townsend; 04.11.2010
comment
Этот метод предназначен только для оконных приложений, верно? А как насчет консольного приложения? Единственный вариант - создание невидимого окна? - person kakyo; 16.09.2019

Я последовал вашему «новому подходу» и также обнаружил, что 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 );
    } 
}
person John W    schedule 09.08.2011

Это еще один способ обнаружения ВСТАВКИ И УДАЛЕНИЯ запоминающих устройств USB.

Этот код c ++ обнаруживает ВСТАВКУ и УДАЛЕНИЕ обоих USB-накопителей.

Это также обнаруживает одновременное подключение и отключение нескольких USB-устройств.

Код c ++: протестировано в VISUAL STUDIO 2015

Вы также можете проверить наличие других типов устройств для удаления и установки. Просто заполните переданный массив символов другим типам устройств в 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;
    }

ПРИМЕЧАНИЕ. При этом не создаются окна. Это консольное приложение.

person jayprakash    schedule 25.02.2020