最近写资料用到按照USB顺序显示所有的USB设备(设备不用按照插入顺序,只需要插入对应USB插口即可),搜了很多资料没有找到想要的实现方式,便自己动手写了一些库文件(结合USB View以及WindowsAPI函数),并且成功移植到MFC工程(我使用的是VS2010工具)。
首先看下USB View软件显示效果:
我们看到USB View软件是以树结构显示了USB所有的插口(包括以及连接设备的插口和还未连接设备的插口),但是连接的设备信息却不齐全(比如如何判断是安卓设备以及设备的容器ID和名称等)。
插入设备的数据采用WindowsAPI函数(WindowsAPI函数的缺点是只能查询到已经连接的设备信息,却不能确定设备信息对应的插口),对于这块我也纠结不少时间…
后来进行猜想,采用USB View作为USB插口的遍历,然后结合算法把遍历到的数据进行配对,可以组成完成的USB数据信息。
下面是我的实验计划:
1.使用USB View函数库进行遍历并且保存所有的USB插口信息(保存已经插入和未插入的),其中树结构分为三级保存,第一级做为主要设备信息(USBHostControllers 主控制器),第二级做为hub设备信息(可以清楚知道hub下有几个USB插口),第三级做为当前hub下的USB插口信息。当然这只是USB View实现的功能;
2.使用WindowsAPI遍历到所有的USB信息(其中包括Hub信息);
3.根据USB View插口内的idVendor和idProduct信息进行匹配WindowAPI内所有相关的数据,配对成功后得到容器ID进行遍历剩下的设备信息确保设备信息的完整性以及不重复性,在匹配的过程中我们就可以到插入设备的名称,以及设备类型比如“Android”等,匹配成功后按照USB插口的顺序进行保存顺序(这里只按照计数的形式向后增加USB插口,不再使用树结构);
4.由于USB View使用C语言写的库文件,需要把一些不兼容MFC的部分进行修改,比如隐藏USB View自带的树结构视图等。
下面说下使用这个MFC工程的方式:
首先下载WinDDK 7600.16385.1工具包,这个网上很多下载后,安装到默认的路径:C:\WinDDK; 安装完成后需要把使用的VS2010文件夹路径下的 VC\ include内的所有文件 拷贝覆盖到C:\WinDDK\7600.16385.1\inc\api 文件夹内;
MFC工程下载地址:!
这个工程可以直接使用,也可以移植内部的文件用来使用,由于修改的文件比较多,只贴出具体实现的文件:
.h文件:
/*
*李坤昱
*QQ:326087275@qq.com
*/
#include <stdio.h>
#include <vector>
#include "usbioctl.h "
//枚举出所有的设备信息,然后与USB view信息合并
struct List_all_USB_devices
{
//设备类型 比如USB
CString szDeviceInstanceID;
//
CString Device_Description;
//设备描述
CString szDesc;
//
CString Hardware_IDs;
//
CString pszId;
//
CString Bus_Reported_Device_Description;
//
CString Device_Manufacturer;
//
CString Device_Friendly_Name;
//
CString Device_Location_Info;
//
CString Device_Security_Descriptor_String;
//容器ID
CString ContainerId;
//
CString Device_Display_Category;
//
CString pszToken;
//vid
CString szVid;
//pid
CString szPid;
//mi
CString szMi;
//如果当前这条数据已经写入设备结构,标记为1 不再进行匹配
bool bIs;
List_all_USB_devices():bIs(0)
{
}
};
struct Usb_List_Data
{
Usb_List_Data();
//获取所有的USB设备信息
bool GetAllUsbInfo(std::vector<List_all_USB_devices> & usb_Info);
private:
//保存所有的USB信息
std::vector<List_all_USB_devices> Usb_Devices;
private:
//遍历USB设备
void ListDevices (CONST GUID *pClassGuid, LPCTSTR pszEnumerator);
};
typedef struct _STRING_DESCRIPTOR_NODE_
{
struct _STRING_DESCRIPTOR_NODE *Next;
UCHAR DescriptorIndex;
USHORT LanguageID;
USB_STRING_DESCRIPTOR StringDescriptor[0];
} STRING_DESCRIPTOR_NODE_, *PSTRING_DESCRIPTOR_NODE_;
//
struct Usb_Info
{
int Count;
//
int deviceNameSize;
//
BOOLEAN LowSpeed;
//
BOOLEAN DeviceIsHub;
//
USHORT DeviceAddress;
//
USB_CONNECTION_STATUS ConnectionStatus;
//
USHORT NumberOfOpenPipes;
//
PSTRING_DESCRIPTOR_NODE_ stringDescs;
//
PUSB_NODE_CONNECTION_INFORMATION_EX connectionInfoEx;
//
PUSB_DESCRIPTOR_REQUEST configDesc;
//
CString leafName;
//
CString driverKeyName;
//
CString deviceDesc;
};
//Hub
struct RootHub
{
int Count;
//
int DeviceInfoType;
//
CString HubName;
//
int deviceNameSize;
//
CString deviceName;
//
CString leafName;
//
PUSB_NODE_INFORMATION hubInfo;
//
PUSB_NODE_CONNECTION_INFORMATION_EX ConnectionInfo;
//
PUSB_DESCRIPTOR_REQUEST ConfigDesc;
//
PSTRING_DESCRIPTOR_NODE_ StringDescs;
std::vector<Usb_Info> Usb;
};
//主机控制器
struct HostControllers
{
//标记当前是第几个主机控制器
int Count;
//
CString DeviceInfoType;
//
CString DriverKeyName;
//
CString DeviceId;
//
ULONG VendorID;
//
ULONG DeviceID;
//
ULONG SubSysID;
//
ULONG Revision;
//
CString rootHubName;
//
CString deviceDesc;
std::vector<RootHub> Hub;
};
//获取计算机内树形结构形式 所有的USB相关信息,以及先后顺序(用来记录USB设备的位置)
struct Usb_Id
{
bool GetAllUsbPlace(std::vector<HostControllers> & usb_host);
public:
//设备连接数
ULONG devicesConnected;
private:
void GetAllUsbPlace_();
};
struct Usb_Port
{
//标记是第几个USB插口
int nCount;
//
ULONG VendorID;
//
ULONG ProductID;
//
CString Device_Description;
//用来保存安卓设备名称
CString Device_Description1;
//
CString Bus_Reported_Device_Description;
//
CString Device_Manufacturer;
//
CString Device_Location_Info;
//
CString ContainerId;
//vid
CString szVid;
//pid
CString szPid;
//mi
CString szMi;
//
USB_CONNECTION_STATUS ConnectionStatus;
};
//按循序整理出从Usb插口1开始,一直到最后的设备
struct Usb_SequenceInfo
{
Usb_List_Data usb_list;
Usb_Id Usb_id;
bool GetUsbAllPorts(std::vector<Usb_Port> & port);
private:
std::vector<Usb_Port> m_port;
private:
void GetUsbPortsInfo();
void WriteUsbPortsInfo(std::vector<List_all_USB_devices> usb_data,std::vector<HostControllers> usb_host);
};
.cpp文件:
/*
*李坤昱
*QQ:326087275@qq.com
*/
#include "StdAfx.h"
#include "Usb_.h"
#include "inc/usbview.h"
#include <windowsx.h>
#include <windows.h>
#include <devguid.h> // for GUID_DEVCLASS_CDROM etc
#include <setupapi.h>
#include <cfgmgr32.h> // for MAX_DEVICE_ID_LEN, CM_Get_Parent and CM_Get_Device_ID
#define INITGUID
#include <tchar.h>
#include "C:\WinDDK\7600.16385.1\inc\api\devpkey.h"
#pragma comment (lib, "setupapi.lib")
typedef BOOL (WINAPI *FN_SetupDiGetDevicePropertyW)(
__in HDEVINFO DeviceInfoSet,
__in PSP_DEVINFO_DATA DeviceInfoData,
__in const DEVPROPKEY *PropertyKey,
__out DEVPROPTYPE *PropertyType,
__out_opt PBYTE PropertyBuffer,
__in DWORD PropertyBufferSize,
__out_opt PDWORD RequiredSize,
__in DWORD Flags
);
char *strupr(char *str);
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
Usb_List_Data::Usb_List_Data()
{
}
// 获取所有的USB信息
void Usb_List_Data::ListDevices (CONST GUID *pClassGuid, LPCTSTR pszEnumerator)
{
unsigned i, j;
DWORD dwSize, dwPropertyRegDataType;
DEVPROPTYPE ulPropertyType;
CONFIGRET status;
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
const static LPCTSTR arPrefix[3] = {TEXT("VID_"), TEXT("PID_"), TEXT("MI_")};
TCHAR szDeviceInstanceID [MAX_DEVICE_ID_LEN];
TCHAR szDesc[1024], szHardwareIDs[4096];
WCHAR szBuffer[4096];
LPTSTR pszToken, pszNextToken;
TCHAR szVid[MAX_DEVICE_ID_LEN], szPid[MAX_DEVICE_ID_LEN], szMi[MAX_DEVICE_ID_LEN];
FN_SetupDiGetDevicePropertyW fn_SetupDiGetDevicePropertyW = (FN_SetupDiGetDevicePropertyW)
GetProcAddress (GetModuleHandle (TEXT("Setupapi.dll")), "SetupDiGetDevicePropertyW");
// List all connected USB devices
hDevInfo = SetupDiGetClassDevs (pClassGuid, pszEnumerator, NULL,
pClassGuid != NULL ? DIGCF_PRESENT: DIGCF_ALLCLASSES | DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
return;
// Find the ones that are driverless
for (i = 0; ; i++) {
List_all_USB_devices usb_dev;
DeviceInfoData.cbSize = sizeof (DeviceInfoData);
if (!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData))
break;
status = CM_Get_Device_ID(DeviceInfoData.DevInst, szDeviceInstanceID , MAX_PATH, 0);
if (status != CR_SUCCESS)
continue;
// Display device instance ID
_tprintf (TEXT("%s\n"), szDeviceInstanceID );
if (SetupDiGetDeviceRegistryProperty (hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC,
&dwPropertyRegDataType, (BYTE*)szDesc,
sizeof(szDesc), // The size, in bytes
&dwSize))
//设备名称
_tprintf (TEXT(" Device Description: \"%s\"\n"), szDesc);
usb_dev.Device_Description.Format(_T("%s"),szDesc);
if (SetupDiGetDeviceRegistryProperty (hDevInfo, &DeviceInfoData, SPDRP_HARDWAREID,
&dwPropertyRegDataType, (BYTE*)szHardwareIDs,
sizeof(szHardwareIDs), // The size, in bytes
&dwSize)) {
LPCTSTR pszId;
_tprintf (TEXT(" Hardware IDs:\n"));
for (pszId=szHardwareIDs;
*pszId != TEXT('\0') && pszId + dwSize/sizeof(TCHAR) <= szHardwareIDs + ARRAYSIZE(szHardwareIDs);
pszId += lstrlen(pszId)+1) {
_tprintf (TEXT(" \"%s\"\n"), pszId);
usb_dev.pszId.Format(_T("%s"),pszId);
}
}
// Retreive the device description as reported by the device itself
// On Vista and earlier, we can use only SPDRP_DEVICEDESC
// On Windows 7, the information we want ("Bus reported device description") is
// accessed through DEVPKEY_Device_BusReportedDeviceDesc
if (fn_SetupDiGetDevicePropertyW && fn_SetupDiGetDevicePropertyW (hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc,
&ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0)) {
//设备类型
if (fn_SetupDiGetDevicePropertyW (hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc,
&ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0))
_tprintf (TEXT(" Bus Reported Device Description: \"%ls\"\n"), szBuffer);
usb_dev.Bus_Reported_Device_Description = szBuffer;
if (fn_SetupDiGetDevicePropertyW (hDevInfo, &DeviceInfoData, &DEVPKEY_Device_Manufacturer,
&ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0)) {
_tprintf (TEXT(" Device Manufacturer: \"%ls\"\n"), szBuffer);
usb_dev.Device_Manufacturer = szBuffer;
}
if (fn_SetupDiGetDevicePropertyW (hDevInfo, &DeviceInfoData, &DEVPKEY_Device_FriendlyName,
&ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0)) {
_tprintf (TEXT(" Device Friendly Name: \"%ls\"\n"), szBuffer);
usb_dev.Device_Friendly_Name = szBuffer;
}
if (fn_SetupDiGetDevicePropertyW (hDevInfo, &DeviceInfoData, &DEVPKEY_Device_LocationInfo,
&ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0)) {
_tprintf (TEXT(" Device Location Info: \"%ls\"\n"), szBuffer);
usb_dev.Device_Location_Info = szBuffer;
}
if (fn_SetupDiGetDevicePropertyW (hDevInfo, &DeviceInfoData, &DEVPKEY_Device_SecuritySDS,
&ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0)) {
// See Security Descriptor Definition Language on MSDN
// (http://msdn.microsoft.com/en-us/library/windows/desktop/aa379567(v=vs.85).aspx)
_tprintf (TEXT(" Device Security Descriptor String: \"%ls\"\n"), szBuffer);
usb_dev.Device_Security_Descriptor_String = szBuffer;
}
//容器ID
if (fn_SetupDiGetDevicePropertyW (hDevInfo, &DeviceInfoData, &DEVPKEY_Device_ContainerId,
&ulPropertyType, (BYTE*)szDesc, sizeof(szDesc), &dwSize, 0)) {
StringFromGUID2((REFGUID)szDesc, szBuffer, ARRAY_SIZE(szBuffer));
_tprintf (TEXT(" ContainerId: \"%ls\"\n"), szBuffer);
usb_dev.ContainerId = szBuffer;
}
if (fn_SetupDiGetDevicePropertyW (hDevInfo, &DeviceInfoData, &DEVPKEY_DeviceDisplay_Category,
&ulPropertyType, (BYTE*)szBuffer, sizeof(szBuffer), &dwSize, 0))
_tprintf (TEXT(" Device Display Category: \"%ls\"\n"), szBuffer);
usb_dev.Device_Display_Category = szBuffer;
}
pszToken = _tcstok_s (szDeviceInstanceID , TEXT("\\#&"), &pszNextToken);
while(pszToken != NULL) {
szVid[0] = TEXT('\0');
szPid[0] = TEXT('\0');
szMi[0] = TEXT('\0');
for (j = 0; j < 3; j++) {
if (_tcsncmp(pszToken, arPrefix[j], lstrlen(arPrefix[j])) == 0) {
switch(j) {
case 0:
_tcscpy_s(szVid, ARRAY_SIZE(szVid), pszToken);
break;
case 1:
_tcscpy_s(szPid, ARRAY_SIZE(szPid), pszToken);
break;
case 2:
_tcscpy_s(szMi, ARRAY_SIZE(szMi), pszToken);
break;
default:
break;
}
}
}
if (szVid[0] != TEXT('\0'))
{
_tprintf (TEXT(" vid: \"%s\"\n"), szVid);
usb_dev.szVid.Format(_T("%s"),szVid);
}
if (szPid[0] != TEXT('\0'))
{
_tprintf (TEXT(" pid: \"%s\"\n"), szPid);
usb_dev.szPid.Format(_T("%s"),szPid);
}
if (szMi[0] != TEXT('\0'))
{
_tprintf (TEXT(" mi: \"%s\"\n"), szMi);
usb_dev.szMi.Format(_T("%s"),szMi);
}
pszToken = _tcstok_s (NULL, TEXT("\\#&"), &pszNextToken);
}
Usb_Devices.push_back(usb_dev);
}
return;
}
bool Usb_List_Data::GetAllUsbInfo(std::vector<List_all_USB_devices> & usb_Info)
{
ListDevices(NULL, _T("USB"));
usb_Info = Usb_Devices;
return (usb_Info.size() > 0 ? true : false);
}
std::vector<HostControllers> host;
bool Usb_Id::GetAllUsbPlace(std::vector<HostControllers> & usb_host)
{
GetAllUsbPlace_();
usb_host = host;
return (usb_host.size() > 0 ? true : false);
}
void Usb_Id::GetAllUsbPlace_()
{
host.clear();
EnumerateHostControllers(0,&devicesConnected);
}
void Usb_SequenceInfo::GetUsbPortsInfo()
{
std::vector<List_all_USB_devices> usb_data;
usb_list.GetAllUsbInfo(usb_data);
std::vector<HostControllers> usb_host;
Usb_id.GetAllUsbPlace(usb_host);
WriteUsbPortsInfo(usb_data,usb_host);
}
void Usb_SequenceInfo::WriteUsbPortsInfo(std::vector<List_all_USB_devices> usb_data,std::vector<HostControllers> usb_host)
{
if (0 >= usb_data.size() && 0 >= usb_host.size())
{
AfxMessageBox(_T("无法获取到USB的接口位置!"));
}
//合并信息组成一条完整usb设备信息
m_port.clear();
int count = 0;
for (auto it = usb_host.begin();it != usb_host.end();it++)
{
for (auto Usb = it->Hub.begin();Usb != it->Hub.end();Usb++)
{
for (auto UsbInfo = Usb->Usb.begin();UsbInfo != Usb->Usb.end();UsbInfo++)
{
Usb_Port port;
port.ConnectionStatus = UsbInfo->ConnectionStatus;
if (DeviceConnected == port.ConnectionStatus)
{
if ((UsbInfo->connectionInfoEx) && (NULL != UsbInfo->connectionInfoEx) /*&& (0 != *UsbInfo->connectionInfoEx->DeviceDescriptor)*/)
{
port.VendorID = UsbInfo->connectionInfoEx->DeviceDescriptor.idVendor;
port.ProductID = UsbInfo->connectionInfoEx->DeviceDescriptor.idProduct;
//设备的id数字有的是16进制 有的是10进制
CString StrVendor,StrProduct;
StrVendor.Format(_T("VID_%04X"),port.VendorID);
StrProduct.Format(_T("PID_%04X"),port.ProductID);
CString StrVendor_,StrProduct_;
StrVendor_.Format(_T("VID_%d"),port.VendorID);
StrProduct_.Format(_T("PID_%d"),port.ProductID);
//在所有设备信息里进行匹配
for (auto data = usb_data.begin();data != usb_data.end();data++)
{
if (data->bIs == true)
continue;
//进行记录数据,然后继续遍历相同容器ID的
if ((data->szVid == StrVendor && data->szPid == StrProduct)
|| (data->szVid == StrVendor_ && data->szPid == StrProduct_))
{
port.szVid = data->szVid;
port.szPid = data->szPid;
data->bIs = true;
port.Device_Description = data->Device_Description;
port.Bus_Reported_Device_Description = data->Bus_Reported_Device_Description;
port.Device_Manufacturer = data->Device_Manufacturer;
port.Device_Location_Info = data->Device_Location_Info;
port.ContainerId = data->ContainerId;
port.szMi = data->szMi;
if (!port.ContainerId.IsEmpty())
{
//遍历一边相同容器ID的信息,找出设备名称
for (auto data1 = usb_data.begin();data1 != usb_data.end();data1++)
{
if (port.ContainerId == data1->ContainerId)
{
if (!data1->bIs)
{
//如果设备描述已经是Android了,那么修改为设备名称
/*
这么做的原因是Device_Description或Bus_Reported_Device_Description其中有一个表示设备名称,
遍历的时候可能会被Android字节覆盖掉,这样可以保证保留设备名称
*/
if (port.Device_Description1.IsEmpty() || _T("Android") != data->Bus_Reported_Device_Description)
{
port.Device_Description1 = data1->Device_Description;
}
data1->bIs = true;
}
}
}
}
}
}
}
}
port.nCount = ++count;
m_port.push_back(port);
}
}
}
}
bool Usb_SequenceInfo::GetUsbAllPorts(std::vector<Usb_Port> & port)
{
GetUsbPortsInfo();
port = m_port;
return (port.size() > 0 ? true : false);
}
MFC工程下载地址:!这个项目可以顺利执行,如果有报错的情况,请参考前面配置的时候是否没有配置正确!