[英]Detecting USB Insertion / Removal Events in Windows using C++
我正在為需要處理 USB 插入/移除事件的現有應用程序編寫擴展。 我知道感興趣設備的 VID/PID。 但是,我無權訪問窗口句柄,所以我不知道RegisterDeviceNotification
是否會有很多用處,除非有辦法通過WINAPI
獲取句柄。 使用 C++ 檢測 USB 插入/移除事件的最佳方法是什么?
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);
}
PC 進入WndProc
,但在我移除/插入 USB 設備時不會。 PC 似乎永遠不會進入OnDeviceChange
。 任何提示將不勝感激。 我需要處理 USB 設備的意外插入/移除。 如果有所不同,USB 設備將作為 Windows 的虛擬 COM 端口出現。 謝謝。
附加信息:使用RegisterClassEx
返回的類atom
調用CreateWindowEx
失敗並顯示錯誤消息“找不到窗口類”。
_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 );
}
任何想法或建議將不勝感激。 如果缺少任何細節,請告訴我,我會很樂意添加它們。 謝謝。
是否需要在新線程中啟動僅消息窗口,還是創建新窗口會自動脫離新線程?
創建一個只等待WM_DEVICECHANGE
並使用RegisterDeviceNotification
注冊該窗口的虛擬窗口。 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 中測試
您還可以檢查是否有其他類型的設備進行移除和插入。 只需在函數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;
}
備注:這不會創建任何窗口。 這是控制台應用程序。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.