本人初学VC++,尝试使用MFC来制作USB枚举,与大家分享经验。
本文利用根据微软的usbview源码,在Visual Stdio2015利用MFC写了USB枚举。首先利用SetupDiGetClassDevs()枚举GUID类型,这里的GUID选用的是GUID_DEVINTERFACE_USB_DEVICE,在usbiodef.h头文件中还有很多GUID。
添加头文件。
#include <hidsdi.h>
#include <usbiodef.h>
#include <initguid.h>
#include <winioctl.h>
#pragma comment(lib,"hid.lib")
#include <setupapi.h>
#pragma comment(lib,"setupapi.h")
#include <initguid.h>
下面代码是GUID的类型。
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, \
0xC0, 0x4F, 0xB9, 0x51, 0xED);
/* 3ABF6F2D-71C4-462a-8A92-1E6861E6AF27 */
DEFINE_GUID(GUID_DEVINTERFACE_USB_HOST_CONTROLLER, 0x3abf6f2d, 0x71c4, 0x462a, 0x8a, 0x92, 0x1e, \
0x68, 0x61, 0xe6, 0xaf, 0x27);
/* 4E623B20-CB14-11D1-B331-00A0C959BBD2 */
DEFINE_GUID(GUID_USB_WMI_STD_DATA, 0x4E623B20L, 0xCB14, 0x11D1, 0xB3, 0x31, 0x00,\
0xA0, 0xC9, 0x59, 0xBB, 0xD2);
/* 4E623B20-CB14-11D1-B331-00A0C959BBD2 */
DEFINE_GUID(GUID_USB_WMI_STD_NOTIFICATION, 0x4E623B20L, 0xCB14, 0x11D1, 0xB3, 0x31, 0x00,\
0xA0, 0xC9, 0x59, 0xBB, 0xD2);
接下来需要循环遍历所有的接口设备信息,使用函数SetupDiEnumDeviceInfo(handle, Count, &DeviceInfoData);
GetDeviceProperty()函数中的第三个参数选择可以得到设备的信息。这里获得了USB的名称、端口号等信息,该函数的内部结构如下。
BOOL CUSB_ENUMDlg::GetDeviceProperty(_In_ HDEVINFO DeviceInfoSet, _In_ PSP_DEVINFO_DATA DeviceInfoData, _In_ DWORD Property, _Outptr_ LPTSTR *ppBuffer)
{
BOOL bResult;
DWORD requiredLength = 0;
DWORD lastError;
if (ppBuffer == NULL)
{
return FALSE;
}
*ppBuffer = NULL;
bResult = SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
DeviceInfoData,
Property,
NULL,
NULL,
0,
&requiredLength);
lastError = GetLastError();
if ((requiredLength == 0) || (bResult != FALSE && lastError != ERROR_INSUFFICIENT_BUFFER))
{
return FALSE;
}
*ppBuffer = (LPTSTR)ALLOC(requiredLength);
if (*ppBuffer == NULL)
{
return FALSE;
}
bResult = SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
DeviceInfoData,
Property,
NULL,
(PBYTE)*ppBuffer,
requiredLength,
&requiredLength);
if (bResult == FALSE)
{
FREE(*ppBuffer);
*ppBuffer = NULL;
return FALSE;
}
return TRUE;
}
制作MFC界面
这是一个简单的按钮控件+列表控件,列表控件属性View改为Report模式。为列表控件添加成员变量m_listCtrl
CListCtrl m_listCtrl;
在初始化成员函数中初始化列表控件。
BOOL CUSB_ENUMDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// 将“关于...”菜单项添加到系统菜单中。
// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标
// TODO: 在此添加额外的初始化代码
DWORD dwstyle = m_listCtrl.GetExtendedStyle();
dwstyle |= LVS_EX_GRIDLINES;
dwstyle |= LVS_EX_FULLROWSELECT;
m_listCtrl.SetExtendedStyle(dwstyle);
//m_listCtrl.GetHeaderCtrl()->EnableWindow(FALSE);//不允许拖动列表头改变列宽
CRect rcList;
m_listCtrl.GetClientRect(rcList);//获得控件坐标属性
m_listCtrl.InsertColumn(0, _T("Device Description"), LVCFMT_CENTER, 150);
m_listCtrl.InsertColumn(1, _T("PORT"), LVCFMT_CENTER, 150);
m_listCtrl.InsertColumn(2, _T("Path"), LVCFMT_CENTER, rcList.Width()-300);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}
添加枚举成员函数
BOOL CUSB_ENUMDlg::EnumerateAllDevicesWithGuid()
{
LPTSTR DeviceDescName;
SP_DEVINFO_DATA DeviceInfoData;
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
GUID deviceId;
DWORD strSize = 0, requiredSize = 0;
ULONG DeviceInterfaceDetailSize;
LPTSTR DeviceDriverName;
TCHAR did[1024];
LPTSTR locid;
LPTSTR locdesc;
//HidD_GetHidGuid(&deviceId);
HDEVINFO handle;
handle = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE,
NULL,
NULL,
DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
bool result1 = FALSE;
bool result2 = FALSE;
UINT Count = 0;
DWORD error;
do {
BOOL success;
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
success = SetupDiEnumDeviceInfo(handle, Count, &DeviceInfoData);
if (success == FALSE)
{
break;
}
result1 = GetDeviceProperty(handle,
&DeviceInfoData,
SPDRP_DEVICEDESC, //(0x00000000)
&DeviceDescName);
result1 = GetDeviceProperty(handle,
&DeviceInfoData,
SPDRP_DRIVER, //Registry Trees and Keys
&DeviceDriverName);
DWORD buffersize = 1024;
DWORD req_bufsize = 0;
DWORD DataT;
if (!SetupDiGetDeviceInstanceId(handle,
&DeviceInfoData,
NULL,
NULL,
&req_bufsize)) {
error = GetLastError();
}
SetupDiGetDeviceInstanceId(handle,
&DeviceInfoData,
did,
req_bufsize,
&requiredSize); //获得设备VID和PID
if (!GetDeviceProperty(handle,
&DeviceInfoData,
SPDRP_LOCATION_PATHS,
&locid))
continue;
if (!GetDeviceProperty(handle,
&DeviceInfoData,
SPDRP_LOCATION_INFORMATION,
&locdesc
))
continue;
DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
result1 = SetupDiEnumDeviceInterfaces(handle,
NULL,
&GUID_DEVINTERFACE_USB_DEVICE,
Count,
&DeviceInterfaceData
);
SetupDiGetDeviceInterfaceDetail(handle,
&DeviceInterfaceData,
NULL,
0,
&strSize,
NULL);
requiredSize = strSize;
char s[245];
PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = (SP_DEVICE_INTERFACE_DETAIL_DATA *)s;
DeviceInterfaceDetailData->cbSize= sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
result2 = SetupDiGetDeviceInterfaceDetail(handle,
&DeviceInterfaceData,
DeviceInterfaceDetailData, //DeviceInterfaceDetailData->DevicePath为设备路径。
strSize,
&requiredSize,
NULL
);
AddListCtrl(DeviceDescName, locdesc, DeviceInterfaceDetailData->DevicePath,Count);
Count++;
} while (result1);
return TRUE;
}
列表添加信息功能的函数AddListCtrl内容如下。
BOOL CUSB_ENUMDlg::AddListCtrl(CString Name, CString Port, CString Path,UINT nCount)
{
m_listCtrl.InsertItem(nCount, Port);
m_listCtrl.SetItemText(nCount, 1, Name);
m_listCtrl.SetItemText(nCount, 2, Path);
return TRUE;
}
最后实现效果如下图,单击按钮列表出现枚举设备信息。