Обнаружение событий вставки / удаления 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 отображается как виртуальный COM-порт для Windows. Благодарю.
Дополнительная информация: Звонок 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 );
}
Любые мысли или предложения будут высоко оценены. Если какие-либо детали отсутствуют, дайте мне знать, и я буду рад их добавить. Благодарю.
Нужно ли открывать окно только для сообщений в новом потоке, или создание нового окна автоматически приводит к появлению нового потока?
3 ответа
Создать фиктивное окно, которое ничего не делает, кроме ожидания WM_DEVICECHANGE
и зарегистрируйте это окно, используя RegisterDeviceNotification
, WMI - это излишество, ИМХО.
Существует специальный пример MSDN для вашего случая в нативном коде.
Регистрация для уведомления устройства
Лучше сделать это так, чем через WMI.
Я следовал вашему "новому подходу" и также обнаружил, что 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 );
}
}
Это еще один способ обнаружения ВСТАВКИ И УДАЛЕНИЯ запоминающих устройств 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;
}
ЗАМЕЧАНИЕ: Это не создает никаких окон. Это консольное приложение.