隨著 USB 設備普及,應用程式常需枚舉、識別與存取各類 USB 裝置。本文系統整理 USB 控制器枚舉、HID 類別裝置、大容量儲存裝置 (U盤) 的程式設計方法,基於 VC++ 6.0 + XP DDK 環境實測。
標準枚舉路徑:控制器 → Hub → 連接埠 → 設備
TCHAR HCName[64];
for (int i = 0; ; ++i) {
wsprintf(HCName, L"\\\\.\\HCD%d", i);
HANDLE hHC = CreateFile(HCName, GENERIC_WRITE, FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (hHC == INVALID_HANDLE_VALUE) break; // 無更多控制器
// 取得驅動名稱與 Hub 連結
USB_HUB_NAME hubName = {0};
DWORD bytesRet;
DeviceIoControl(hHC, IOCTL_USB_GET_HUB_NAME, NULL, 0,
&hubName, sizeof(hubName), &bytesRet, NULL);
CloseHandle(hHC);
EnumHub(hubName.ucHubName); // 遞迴列舉 Hub
}
void EnumHub(WCHAR* hubPath) {
HANDLE hHub = CreateFile(hubPath, GENERIC_WRITE, FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
USB_NODE_INFORMATION hubInfo = {0};
DeviceIoControl(hHub, IOCTL_USB_GET_NODE_INFORMATION, NULL, 0,
&hubInfo, sizeof(hubInfo), &bytesRet, NULL);
for (ULONG port = 1; port <= hubInfo.u.HubInformation.HubDescriptor.bNumberOfPorts; ++port) {
USB_PORT_CONNECTOR_PROPERTIES portProp = {0};
portProp.ConnectionIndex = port;
DeviceIoControl(hHub, IOCTL_USB_GET_PORT_CONNECTOR_PROPERTIES,
&portProp, sizeof(portProp), &portProp, sizeof(portProp),
&bytesRet, NULL);
if (portProp.DeviceConnected) {
// 取得設備描述符
USB_DEVICE_DESCRIPTOR devDesc;
// ... 透過 IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX 取得詳細資訊
}
}
CloseHandle(hHub);
}
使用 Windows HID API 簡化開發:
#include
#include
GUID hidGuid;
HidD_GetHidGuid(&hidGuid);
HDEVINFO hDevInfo = SetupDiGetClassDevs(&hidGuid, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
SP_DEVICE_INTERFACE_DATA devInterface = {0};
devInterface.cbSize = sizeof(devInterface);
for (DWORD i = 0; SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &hidGuid, i, &devInterface); ++i) {
DWORD reqSize = 0;
SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInterface, NULL, 0, &reqSize, NULL);
PSP_DEVICE_INTERFACE_DETAIL_DATA detail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, reqSize);
detail->cbSize = sizeof(*detail);
if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &devInterface, detail, reqSize, NULL, NULL)) {
HANDLE hDev = CreateFile(detail->DevicePath, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
HIDD_ATTRIBUTES attrs = {0};
attrs.Size = sizeof(attrs);
HidD_GetAttributes(hDev, &attrs);
// attrs.VendorID, attrs.ProductID, attrs.VersionNumber
CloseHandle(hDev);
}
LocalFree(detail);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
注意:XP 下 `HidD_GetHidGuid` 可能無法列舉滑鼠鍵盤,需改用 `GUID_DEVINTERFACE_MOUSE`/`KEYBOARD`。
TCHAR drive[] = L"\\\\.\\E:"; // 遍歷磁碟機代號
HANDLE hDrive = CreateFile(drive, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
STORAGE_PROPERTY_QUERY query = {0};
query.PropertyId = StorageDeviceProperty;
query.QueryType = PropertyStandardQuery;
STORAGE_DEVICE_DESCRIPTOR desc = {0};
DWORD bytesRet;
DeviceIoControl(hDrive, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query),
&desc, sizeof(desc), &bytesRet, NULL);
// desc.BusType == BusTypeUsb 確認為 USB 裝置
// desc.VendorId, desc.ProductId, desc.ProductRevision 取得詳細資訊
CloseHandle(hDrive);
| 問題 | 原因 | 對策 |
|---|---|---|
| CreateFile 失敗 (Access Denied) | 權限不足 | 以系統管理員執行,或調整裝置安全性描述元 |
| IOCTL 返回 ERROR_INVALID_FUNCTION | 驅動不支援該控制碼 | 檢查驅動版本,嘗試替代 IOCTL |
| 無法列舉特定廠商裝置 | 私有協定/加密 | 需廠商提供 SDK 或逆向分析 |