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函数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 } },

};

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享