Windows上USB设备检测

枚举总线

主机控制器的驱动程序(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函数SetupDiGetDeviceInstanceIdSetupDiGetDeviceRegistryProperty 来得到设备信息。
 

监控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
    {
        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
    {
        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
    {
        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<
                {
                    //_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 } },
};

你可能感兴趣的:(Windows,C++)