📄 myusbhidtestappdlg.cpp
字号:
//将MemberIndex指向下一个设备
MemberIndex++;
//如果获取信息成功,则继续获取该设备的详细信息。在获取设备
//详细信息时,需要先知道保存详细信息需要多大的缓冲区,这通过
//第一次调用函数SetupDiGetDeviceInterfaceDetail来获取。这时
//提供缓冲区和长度都为NULL的参数,并提供一个用来保存需要多大
//缓冲区的变量RequiredSize。
Result=SetupDiGetDeviceInterfaceDetail(hDevInfoSet,
&DevInterfaceData,
NULL,
NULL,
&RequiredSize,
NULL);
//然后,分配一个大小为RequiredSize缓冲区,用来保存设备详细信息。
pDevDetailData=(PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(RequiredSize);
if(pDevDetailData==NULL) //如果内存不足,则直接返回。
{
MessageBox("内存不足!");
SetupDiDestroyDeviceInfoList(hDevInfoSet);
return;
}
//并设置pDevDetailData的cbSize为结构体的大小(注意只是结构体大小,
//不包括后面缓冲区)。
pDevDetailData->cbSize=sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
//然后再次调用SetupDiGetDeviceInterfaceDetail函数来获取设备的
//详细信息。这次调用设置使用的缓冲区以及缓冲区大小。
Result=SetupDiGetDeviceInterfaceDetail(hDevInfoSet,
&DevInterfaceData,
pDevDetailData,
RequiredSize,
NULL,
NULL);
//将设备路径复制出来,然后销毁刚刚申请的内存。
MyDevPathName=pDevDetailData->DevicePath;
free(pDevDetailData);
//如果调用失败,则查找下一个设备。
if(Result==FALSE) continue;
//如果调用成功,则使用不带读写访问的CreateFile函数
//来获取设备的属性,包括VID、PID、版本号等。
//对于一些独占设备(例如USB键盘),使用读访问方式是无法打开的,
//而使用不带读写访问的格式才可以打开这些设备,从而获取设备的属性。
hDevHandle=CreateFile(MyDevPathName,
NULL,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
//如果打开成功,则获取设备属性。
if(hDevHandle!=INVALID_HANDLE_VALUE)
{
//获取设备的属性并保存在DevAttributes结构体中
Result=HidD_GetAttributes(hDevHandle,
&DevAttributes);
//关闭刚刚打开的设备
CloseHandle(hDevHandle);
//获取失败,查找下一个
if(Result==FALSE) continue;
//如果获取成功,则将属性中的VID、PID以及设备版本号与我们需要的
//进行比较,如果都一致的话,则说明它就是我们要找的设备。
if(DevAttributes.VendorID==MyVid) //如果VID相等
if(DevAttributes.ProductID==MyPid) //并且PID相等
// if(DevAttributes.VersionNumber==MyPvn) //并且设备版本号相等
{
MyDevFound=TRUE; //设置设备已经找到
AddToInfOut("设备已经找到");
//那么就是我们要找的设备,分别使用读写方式打开之,并保存其句柄
//并且选择为异步访问方式。
//读方式打开设备
hReadHandle=CreateFile(MyDevPathName,
GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
NULL);
if(hReadHandle!=INVALID_HANDLE_VALUE)AddToInfOut("读访问打开设备成功");
else AddToInfOut("读访问打开设备失败");
//写方式打开设备
hWriteHandle=CreateFile(MyDevPathName,
GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
NULL);
if(hWriteHandle!=INVALID_HANDLE_VALUE)AddToInfOut("写访问打开设备成功");
else AddToInfOut("写访问打开设备失败");
DataInSending=FALSE; //可以发送数据
//手动触发事件,让读报告线程恢复运行。因为在这之前并没有调用
//读数据的函数,也就不会引起事件的产生,所以需要先手动触发一
//次事件,让读报告线程恢复运行。
SetEvent(ReadOverlapped.hEvent);
//显示设备的状态。
SetDlgItemText(IDC_DS,"设备已打开");
//找到设备,退出循环。本程序只检测一个目标设备,查找到后就退出
//查找了。如果你需要将所有的目标设备都列出来的话,可以设置一个
//数组,找到后就保存在数组中,直到所有设备都查找完毕才退出查找
break;
}
}
//如果打开失败,则查找下一个设备
else continue;
}
//调用SetupDiDestroyDeviceInfoList函数销毁设备信息集合
SetupDiDestroyDeviceInfoList(hDevInfoSet);
//如果设备已经找到,那么应该使能各操作按钮,并同时禁止打开设备按钮
if(MyDevFound)
{
//禁止打开设备按键,使能关闭设备、清计数器按键
GetDlgItem(IDC_OPEN_DEVICE)->EnableWindow(FALSE);
GetDlgItem(IDC_CLOSE_DEVICE)->EnableWindow(TRUE);
GetDlgItem(IDC_CLEAR_COUNTER)->EnableWindow(TRUE);
//使能LED控制按键。
GetDlgItem(IDC_LED1)->EnableWindow(TRUE);
GetDlgItem(IDC_LED2)->EnableWindow(TRUE);
GetDlgItem(IDC_LED3)->EnableWindow(TRUE);
GetDlgItem(IDC_LED4)->EnableWindow(TRUE);
GetDlgItem(IDC_LED5)->EnableWindow(TRUE);
GetDlgItem(IDC_LED6)->EnableWindow(TRUE);
GetDlgItem(IDC_LED7)->EnableWindow(TRUE);
GetDlgItem(IDC_LED8)->EnableWindow(TRUE);
}
else
{
AddToInfOut("设备未找到");
}
}
//////////////////////////////End of function//////////////////////
//将整型转化为CString
CString CMyUsbHidTestAppDlg::itos(INT value, INT radix)
{
static CString Str;
UCHAR strBuffer[20];
Str=itoa(value,(char *)strBuffer,radix);
if(radix==16)
{
if(Str.GetLength()==1) //如果只有1位数据,则在前面加3个0
{
Str="000"+Str;
}
if(Str.GetLength()==2) //如果只有2位数据,则在前面加2个0
{
Str="00"+Str;
}
if(Str.GetLength()==3) //如果只有3位数据,则在前面加1个0
{
Str="0"+Str;
}
}
Str.MakeUpper();
return Str;
}
//////////////////////////////End of function//////////////////////
//获取文本框中的内容并转换为数值
BOOL CMyUsbHidTestAppDlg::GetInputData(INT nID, CString ErrorMsg, DWORD &Value)
{
CString InText;
GetDlgItemText(nID,InText); //获取输入的文本
if((InText.GetLength()<=4)&&(sscanf(InText,"%x",&Value))) //判断是否输入正确
{
SetDlgItemText(nID,itos(Value,16)); //设置文本
return TRUE;
}
else //输入错误,提示重新输入,并聚焦到错误的输入框
{
MessageBox(ErrorMsg,NULL,MB_OK | MB_ICONEXCLAMATION);
GetDlgItem(nID)->SetFocus();
((CEdit*)GetDlgItem(nID))->SetSel(0,-1);
return FALSE;
}
}
//////////////////////////////End of function//////////////////////
//从文本框中获取输入的VID、PID、PVN
void CMyUsbHidTestAppDlg::GetMyIDs()
{
//从文本框中获取VID号,保存在MyVid中。
GetInputData(IDC_VID_EDIT,"厂商ID(VID)输入格式错误。请重新输入。",MyVid);
//从文本框中获取PID号,保存在MyPid中。
GetInputData(IDC_PID_EDIT,"产品ID(PID)输入格式错误。请重新输入。",MyPid);
//从文本框中获取PVN号,保存在MyPvn中。
GetInputData(IDC_PVN_EDIT,"产品版本号(PVN)输入格式错误。请重新输入。",MyPvn);
}
//////////////////////////////End of function//////////////////////
//单击关闭设备按钮的响应函数
void CMyUsbHidTestAppDlg::OnCloseDevice()
{
// TODO: Add your control notification handler code here
HICON hIcon;
LedStatus=0; //所有LED关闭
KeyStatus=0; //所有按键抬起
//如果读数据的句柄不是无效句柄,则关闭之
if(hReadHandle!=INVALID_HANDLE_VALUE)
{
CloseHandle(hReadHandle);
hReadHandle=INVALID_HANDLE_VALUE;
}
//如果写数据的句柄不是无效句柄,先设置所有LED为关闭状态,
//然后关闭句柄。
if(hWriteHandle!=INVALID_HANDLE_VALUE)
{
SendLedStatus();
CloseHandle(hWriteHandle);
hWriteHandle=INVALID_HANDLE_VALUE;
}
//设置设备状态为未找到
MyDevFound=FALSE;
//修改按键使能情况
GetDlgItem(IDC_OPEN_DEVICE)->EnableWindow(TRUE);
GetDlgItem(IDC_CLOSE_DEVICE)->EnableWindow(FALSE);
GetDlgItem(IDC_CLEAR_COUNTER)->EnableWindow(FALSE);
//设置LED为关闭状态
hIcon=AfxGetApp()->LoadIcon(IDI_LED_OFF);
GetDlgItem(IDC_LED1)->SendMessage(BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);
GetDlgItem(IDC_LED2)->SendMessage(BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);
GetDlgItem(IDC_LED3)->SendMessage(BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);
GetDlgItem(IDC_LED4)->SendMessage(BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);
GetDlgItem(IDC_LED5)->SendMessage(BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);
GetDlgItem(IDC_LED6)->SendMessage(BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);
GetDlgItem(IDC_LED7)->SendMessage(BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);
GetDlgItem(IDC_LED8)->SendMessage(BM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon);
//禁止LED
GetDlgItem(IDC_LED1)->EnableWindow(FALSE);
GetDlgItem(IDC_LED2)->EnableWindow(FALSE);
GetDlgItem(IDC_LED3)->EnableWindow(FALSE);
GetDlgItem(IDC_LED4)->EnableWindow(FALSE);
GetDlgItem(IDC_LED5)->EnableWindow(FALSE);
GetDlgItem(IDC_LED6)->EnableWindow(FALSE);
GetDlgItem(IDC_LED7)->EnableWindow(FALSE);
GetDlgItem(IDC_LED8)->EnableWindow(FALSE);
//设置按键及显示的状态
KeyStatus=0x00;
SetKeyStatus();
//显示关闭信息
AddToInfOut("关闭设备");
//显示设备的状态
SetDlgItemText(IDC_DS,"设备已关闭");
}
//////////////////////////////End of function//////////////////////
//单击退出程序的按钮响应
void CMyUsbHidTestAppDlg::OnQuit()
{
// TODO: Add your control notification handler code here
OnCloseDevice(); //退出程序前先关闭设备
DestroyWindow(); //销毁窗口
}
//////////////////////////////End of function//////////////////////
//窗口关闭时的处理
void CMyUsbHidTestAppDlg::OnClose()
{
// TODO: Add your message handler code here and/or call default
OnCloseDevice(); //关闭窗口前先关闭设备
CDialog::OnClose();
}
//////////////////////////////End of function//////////////////////
//显示计数器的值
void CMyUsbHidTestAppDlg::SetCounterNumber(DWORD Counter)
{
SetDlgItemText(IDC_COUNTER, "计数器值:" + itos(Counter));
}
//////////////////////////////End of function//////////////////////
//点击清除计数器按钮时的响应函数
void CMyUsbHidTestAppDlg::OnClearCounter()
{
//当输出报告的第二字节为非0值时,清除计数器值。
WriteReportBuffer[2]=0xFF;
if(SendLedStatus()==TRUE)
{
SetCounterNumber(0);
}
}
//////////////////////////////End of function//////////////////////
//定时器事件处理
void CMyUsbHidTestAppDlg::OnTimer(UINT nIDEvent)
{
if(nIDEvent==1) //刷新图标时间到,用来产生动作的小图标
{
HICON hIcon;
static UINT i=0;
switch(i)
{
case 0:
hIcon=AfxGetApp()->LoadIcon(IDI_ICON7);
i++;
break;
case 1:
hIcon=AfxGetApp()->LoadIcon(IDI_ICON1);
SetIcon(hIcon,TRUE);
i++;
break;
case 2:
hIcon=AfxGetApp()->LoadIcon(IDI_ICON2);
SetIcon(hIcon,TRUE);
i++;
break;
case 3:
hIcon=AfxGetApp()->LoadIcon(IDI_ICON3);
SetIcon(hIcon,TRUE);
i++;
break;
case 4:
hIcon=AfxGetApp()->LoadIcon(IDI_ICON4);
SetIcon(hIcon,TRUE);
i++;
break;
case 5:
hIcon=AfxGetApp()->LoadIcon(IDI_ICON5);
SetIcon(hIcon,TRUE);
i++;
break;
case 6:
hIcon=AfxGetApp()->LoadIcon(IDI_ICON6);
SetIcon(hIcon,TRUE);
i++;
break;
case 7:
hIcon=AfxGetApp()->LoadIcon(IDI_ICON7);
SetIcon(hIcon,TRUE);
i=0;
break;
default:
i=0;
break;
}
}
CDialog::OnTimer(nIDEvent);
}
//////////////////////////////End of function//////////////////////
//设备状态改变时的处理函数
BOOL CMyUsbHidTestAppDlg::OnDeviceChange(UINT nEventType, DWORD dwData)
{
PDEV_BROADCAST_DEVICEINTERFACE pdbi;
CString DevPathName;
//dwData是一个指向DEV_BROADCAST_DEVICEINTERFACE结构体的指针,
//在该结构体中保存了设备的类型、路径名等参数。通过跟我们指定设备
//的路径名比较,即可以判断是否是我们指定的设备拔下或者插入了。
pdbi=(PDEV_BROADCAST_DEVICEINTERFACE)dwData;
switch(nEventType) //参数nEventType中保存着事件的类型
{
//设备连接事件
case DBT_DEVICEARRIVAL:
if(pdbi->dbcc_devicetype==DBT_DEVTYP_DEVICEINTERFACE)
{
DevPathName=pdbi->dbcc_name; //保存发生状态改变的设备的路径名
//比较是否是我们指定的设备
if(MyDevPathName.CompareNoCase(DevPathName)==0)
{
AddToInfOut("设备已连接");
}
}
return TRUE;
//设备拔出事件
case DBT_DEVICEREMOVECOMPLETE:
if(pdbi->dbcc_devicetype==DBT_DEVTYP_DEVICEINTERFACE)
{
DevPathName=pdbi->dbcc_name; //保存发生状态改变的设备的路径名
//比较是否是我们指定的设备
if(MyDevPathName.CompareNoCase(DevPathName)==0)
{
AddToInfOut("设备被拔出");
//设备被拔出,应该关闭设备(如果处于打开状态的话),停止操作
if(MyDevFound==TRUE)
{
MyDevFound=FALSE;
OnCloseDevice();
}
}
}
return TRUE;
default:
return TRUE;
}
}
//////////////////////////////End of function//////////////////////
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -