枚举总线
主机控制器的驱动程序(HCD(Host Control Driver))。它位于USB主机控制器与USB系统软件之间。
可以用CreateFile打开名字为"\\\\.\\HCD1",\\\\.\\HCD2的文件来检测HCD总线。
打开句柄之后可以通过DeviceIoControl 传递IOCTL_GET_HCD_DRIVERKEY_NAME 参数来得到DriveName。
并可以用CM_系列函数遍历各节点来得到相应的驱动描述。
可以通过调用IOCTL_USB_GET_ROOT_HUB_NAME 为参数的DeviceIoControl 来得到此主机驱动器上的根HUB。
通过CreateFile打开名字为"\\\\.\\HUBNAME"形式的文件,来得到根HUB的句柄,然后通过将IOCTL_USB_GET_NODE_CONNECTION_INFORMATION 传递给函数DeviceIoControl来得到此HUB的连接信息,通过这个信息可以知道此HUB上有几个端口,以及每个端口的设备连接情况。还能得到连接端口设备的VID,PID,如果有的话。
如果HUB端口已经有设备连接,可以通过DeviceIoControl传递参数IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME 来得到连接设备的情况(传递参数为Hub句柄和当前的索引值,索引值要从1开始),如果当前端口连接的设备为另外一个HUB,那么可以通过像DevioceIoControl传递参数IOCTL_USB_GET_NODE_CONNECTION_NAME 来得到此HUB的名字,然后就可以枚举得到此子HUB的信息了。
查找设备
通过PID或者VID或者ClassGUID或者InterfaceGUID,可以得到符合条件的当前设备。
typedef struct _SSDevHandles
{
DWORD dwDevsCount;
HDEVINFO hDevInfoSet;
SP_DEVINFO_DATA *pDevDatas;
} SSDevHandles;
HANDLE STDCALL SSDevGetDevices(IN LPCTSTR lpVid /* = NULL */, IN LPCTSTR lpPID /* = NULL */,
IN const GUID* pSetupClassGuid /* = NULL */, IN const GUID* pInterfaceClassGuid /* = NULL */)
{
SSDevHandles *phDevs = NULL;
HDEVINFO hDevInfoSet = INVALID_HANDLE_VALUE;
check_int_begin
{
if(lpVid != NULL && _tcslen(lpVid) == 0)
lpVid = NULL;
if(lpPID != NULL && _tcslen(lpPID) == 0)
lpPID = NULL;
hDevInfoSet = SetupDiCreateDeviceInfoList(pSetupClassGuid, NULL);
check_int_bool(hDevInfoSet != INVALID_HANDLE_VALUE, SS_RC_NOT_FOUND);
if(pInterfaceClassGuid == NULL)
hDevInfoSet = SetupDiGetClassDevsEx(NULL, NULL, NULL,
DIGCF_ALLCLASSES|DIGCF_DEVICEINTERFACE|DIGCF_PRESENT,
hDevInfoSet, NULL, NULL);
else
hDevInfoSet = SetupDiGetClassDevsEx(pInterfaceClassGuid, NULL, NULL,
DIGCF_DEVICEINTERFACE|DIGCF_PRESENT,
hDevInfoSet, NULL, NULL);
check_int_bool(hDevInfoSet != INVALID_HANDLE_VALUE, SS_RC_NOT_FOUND);
SP_DEVINFO_DATA dtDevInfo = { sizeof(SP_DEVINFO_DATA) };
for(DWORD dwMemIdx = 0; ;dwMemIdx++)
{
if(!SetupDiEnumDeviceInfo(hDevInfoSet, dwMemIdx, &dtDevInfo))
{
if(::GetLastError() == ERROR_NO_MORE_ITEMS)
{
break;
}
continue;;
}
if(lpVid != NULL || lpPID != NULL)
{
TCHAR szInstanceId[MAX_INSTANCE_ID] = {0};
SetupDiGetDeviceInstanceId(hDevInfoSet, &dtDevInfo, szInstanceId, SS_DIMOF(szInstanceId), NULL);
if(_tcslen(szInstanceId) <= 0)
continue;
TCHAR szVID[5] = {0}, szPID[5] = {0};
if(!SSDevUtilGetVIDPID(szInstanceId, szVID, SS_DIMOF(szVID), szPID, SS_DIMOF(szPID)))
continue;
if(lpVid != NULL && _tcsicmp(lpVid, szVID) != 0)
continue;
if(lpPID != NULL && _tcsicmp(lpPID, szPID) != 0)
continue;
}
DWORD dwCapbilities = 0;
if(!SetupDiGetDeviceRegistryProperty(hDevInfoSet, &dtDevInfo, SPDRP_CAPABILITIES, NULL,\
(BYTE*)&dwCapbilities, sizeof(DWORD), NULL))
continue;
if(!SS_FLAG_ISSET(dwCapbilities, CM_DEVCAP_REMOVABLE))
continue;
if(phDevs == NULL)
{
phDevs = (SSDevHandles *)SS_MALLOC(sizeof(SSDevHandles));
memset(phDevs, 0, sizeof(SSDevHandles));
phDevs->hDevInfoSet = INVALID_HANDLE_VALUE;
}
phDevs->pDevDatas = (SP_DEVINFO_DATA*)SS_REALLOC(phDevs->pDevDatas, (phDevs->dwDevsCount + 1) *sizeof(SP_DEVINFO_DATA));
MP_ASSERT(phDevs->pDevDatas != NULL);
phDevs->pDevDatas[phDevs->dwDevsCount] = dtDevInfo;
phDevs->dwDevsCount++;
}
}
check_int_finally
{
if(phDevs != NULL && phDevs->dwDevsCount > 0)
{
phDevs->hDevInfoSet = hDevInfoSet;
hDevInfoSet = INVALID_HANDLE_VALUE;
}
if(hDevInfoSet != INVALID_HANDLE_VALUE)
{
SetupDiDestroyDeviceInfoList(hDevInfoSet);
hDevInfoSet = INVALID_HANDLE_VALUE;
}
}
return phDevs;
}
并通过setupDi函数SetupDiGetDeviceInstanceId ,SetupDiGetDeviceRegistryProperty 来得到设备信息。
监控USB设备插拔
注册设备通知事件
HANDLE STDCALL SSPNPRegisterDeviceNotifyToHwnd(IN HWND hWnd)
{
if(!::IsWindow(hWnd))
return NULL;
SSPNPDeviceNotifyHandle *pDeviceHandle = (SSPNPDeviceNotifyHandle *)SS_MALLOC(sizeof(SSPNPDeviceNotifyHandle));
memset(pDeviceHandle, 0, sizeof(SSPNPDeviceNotifyHandle));
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = {0};
NotificationFilter.dbcc_size = sizeof(NotificationFilter);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
for(int nIdx=0; nIdx<SS_DIMOF(GUID_DEVINTERFACE_LIST); nIdx++)
{
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_LIST[nIdx];
pDeviceHandle->hDevNotifies[pDeviceHandle->nCount++] = ::RegisterDeviceNotification(hWnd,
&NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
}
if(pDeviceHandle->nCount <= 0)
{
SS_FREE(pDeviceHandle);
pDeviceHandle = NULL;
}
return (HANDLE)pDeviceHandle;
}
BOOL STDCALL SSPNPUnregisterDeviceNotify(IN HANDLE hDevNotify)
{
SSPNPDeviceNotifyHandle *pDeviceHandle = (SSPNPDeviceNotifyHandle *)hDevNotify;
if(pDeviceHandle == NULL)
return FALSE;
for(int nIdx=0; nIdx<SS_DIMOF(GUID_DEVINTERFACE_LIST); nIdx++)
{
if(pDeviceHandle->hDevNotifies[nIdx] != NULL)
UnregisterDeviceNotification(pDeviceHandle->hDevNotifies[nIdx]);
}
SS_FREE(pDeviceHandle);
pDeviceHandle = NULL;
return TRUE;
}
反注册设备通知事件
BOOL STDCALL SSPNPUnregisterDeviceNotify(IN HANDLE hDevNotify)
{
SSPNPDeviceNotifyHandle *pDeviceHandle = (SSPNPDeviceNotifyHandle *)hDevNotify;
if(pDeviceHandle == NULL)
return FALSE;
for(int nIdx=0; nIdx<SS_DIMOF(GUID_DEVINTERFACE_LIST); nIdx++)
{
if(pDeviceHandle->hDevNotifies[nIdx] != NULL)
UnregisterDeviceNotification(pDeviceHandle->hDevNotifies[nIdx]);
}
SS_FREE(pDeviceHandle);
pDeviceHandle = NULL;
return TRUE;
}
事件发生后会像窗口发送WM_DEVICECHANGE 消息,可以通过检测wParam是否为DBT_DEVICEARRIVAL 或者DBT_DEVICEREMOVECOMPLETE 来判断插拔。如果确实是插拔事件,且lParam不为空,则lParam指向一个结构DEV_BROADCAST_HDR ,通过判断它的域dbch_devicetype 来判断事件类型,例如
LPCTSTR STDCALL SSDevGetNameOnDeviceNotifyCB(WPARAM wParam, LPARAM lParam)
{
if(wParam != DBT_DEVICEARRIVAL && wParam != DBT_DEVICEREMOVECOMPLETE)
return NULL;
LPCTSTR lpReturnString = NULL;
DEV_BROADCAST_HDR *pDevBCHdr = (DEV_BROADCAST_HDR *)lParam;
MP_ASSERT(pDevBCHdr != NULL);
switch (pDevBCHdr->dbch_devicetype)
{
case DBT_DEVTYP_DEVICEINTERFACE:
{
DEV_BROADCAST_DEVICEINTERFACE *pDevBCInterface = (DEV_BROADCAST_DEVICEINTERFACE *)lParam;
lpReturnString = pDevBCInterface->dbcc_name;
}
break;
/*
case DBT_DEVTYP_VOLUME:
{
DEV_BROADCAST_VOLUME *pDevBCVolume = (DEV_BROADCAST_VOLUME *)lParam;
for(int i=0; i<32; i++)
{
if(SS_FLAG_ISSET(pDevBCVolume->dbcv_unitmask, 1<<i))
{
//_T('A') + i;
}
}
}
break;
*/
default:
break;
}
return lpReturnString;
}
DBT_DEVTYP_VOLUME消息不用注册窗口事件,顶层窗口会自动获得。
常用USB设备GUID
// Copy from HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceClasses
static const GUID GUID_DEVINTERFACE_LIST[] =
{
// GUID_DEVINTERFACE_USB_DEVICE
{ 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } },
// GUID_DEVINTERFACE_DISK
{ 0x53f56307, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } },
// GUID_DEVINTERFACE_HID,
{ 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } },
// GUID_NDIS_LAN_CLASS
{ 0xad498944, 0x762f, 0x11d0, { 0x8d, 0xcb, 0x00, 0xc0, 0x4f, 0xc3, 0x35, 0x8c } },
// GUID_DEVINTERFACE_COMPORT
{ 0x86e0d1e0, 0x8089, 0x11d0, { 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73 } },
// GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR
{ 0x4D36E978, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } },
// GUID_DEVINTERFACE_PARALLEL
{ 0x97F76EF0, 0xF883, 0x11D0, { 0xAF, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x84, 0x5C } },
// GUID_DEVINTERFACE_PARCLASS
{ 0x811FC6A5, 0xF728, 0x11D0, { 0xA5, 0x37, 0x00, 0x00, 0xF8, 0x75, 0x3E, 0xD1 } },
};