Windows下USB HID 设备枚举读写操作

一、USB HID 设备枚举操作

本文需求是通过USB HID的pid,vid,pvn(bcdDevice:用于标识USB当前产品的固件版本号)去寻找HID设备,然后进行数据传输操作。我也忘记当时数据是不是正常的了,反正下位机是有问题的后面改回来,客户上位机通信是ok,只是这个程序后续未验证了,但实现方式是这样的,区别也就是下面writefile的数据第一个字节的问题,给自己留个bug也行
在Win下面通过SetupDi系列函数进行枚举USB设备
BOOL Enum(USHORT m_Pid, USHORT m_Vid, USHORT m_Pvn, char* str, int* len)
	DWORD DeviceNum = 0;
	GUID hidGuid;
	//获取HID设备的GUID
	::HidD_GetHidGuid((LPGUID)&hidGuid);

	HDEVINFO hDevInfoList = SetupDiGetClassDevs(&hidGuid, NULL, NULL, (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
	if (hDevInfoList != NULL)
	{
		SP_DEVICE_INTERFACE_DATA deviceInfoData;

		// Clear data structure
		ZeroMemory(&deviceInfoData, sizeof(deviceInfoData));
		deviceInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
		SetLastError(NO_ERROR);
		while (1)
		{
			int ret = GetLastError();
			if (ret == ERROR_NO_MORE_ITEMS)
			{
				break;
			}
			ZeroMemory(&deviceInfoData, sizeof(deviceInfoData));
			deviceInfoData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
			// retrieves a context structure for a device interface of a device information set.
			if (SetupDiEnumDeviceInterfaces(hDevInfoList, 0, &hidGuid, DeviceNum, &deviceInfoData))
			{
				// Must get the detailed information in two steps
				// First get the length of the detailed information and allocate the buffer
				// retrieves detailed information about a specified device interface.
				PSP_DEVICE_INTERFACE_DETAIL_DATA     functionClassDeviceData = NULL;
				ULONG  predictedLength, requiredLength;

				predictedLength = requiredLength = 0;
				SetupDiGetDeviceInterfaceDetail(hDevInfoList,
					&deviceInfoData,
					NULL,			// Not yet allocated
					0,				// Set output buffer length to zero 
					&requiredLength,// Find out memory requirement
					NULL);

				predictedLength = requiredLength;
				functionClassDeviceData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(predictedLength);
				functionClassDeviceData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

				SP_DEVINFO_DATA did = { sizeof(SP_DEVINFO_DATA) };

				// Second, get the detailed information
				if (SetupDiGetDeviceInterfaceDetail(hDevInfoList,
					&deviceInfoData,
					functionClassDeviceData,
					predictedLength,
					&requiredLength,
					&did))
				{
					TCHAR fname[256];

					// Try by friendly name first.
					if (!SetupDiGetDeviceRegistryProperty(hDevInfoList, &did, SPDRP_FRIENDLYNAME, NULL, (PBYTE)fname, sizeof(fname), NULL))
					{	// Try by device description if friendly name fails.
						if (!SetupDiGetDeviceRegistryProperty(hDevInfoList, &did, SPDRP_DEVICEDESC, NULL, (PBYTE)fname, sizeof(fname), NULL))
						{	// Use the raw path information for linkname and friendlyname
							strncpy_s(fname, 256, (char*)functionClassDeviceData->DevicePath, 256);
						}
					}


					HANDLE UdiskDevice = CreateFile(functionClassDeviceData->DevicePath,
						GENERIC_READ | GENERIC_WRITE,
						FILE_SHARE_READ | FILE_SHARE_WRITE,
						NULL,
						OPEN_EXISTING,
						0,
						NULL);



					//=============== Get Attribute ===============
					HIDD_ATTRIBUTES Attributes;
					ZeroMemory(&Attributes, sizeof(Attributes));
					Attributes.Size = sizeof(HIDD_ATTRIBUTES);
					if (!HidD_GetAttributes(UdiskDevice, &Attributes))
					{
						CloseHandle(UdiskDevice);
						DeviceNum++;
						continue;
					}
					if (Attributes.ProductID == m_Pid && Attributes.VendorID == m_Vid
						&& Attributes.VersionNumber == m_Pvn)
					{
						//Save Device Path
						CString m_linkname = functionClassDeviceData->DevicePath;
						memcpy(str, m_linkname.GetBuffer(), m_linkname.GetLength());
						*len = m_linkname.GetLength();
						free(functionClassDeviceData);
						SetupDiDestroyDeviceInfoList(hDevInfoList);
						return TRUE;
					}

					free(functionClassDeviceData);
					CloseHandle(UdiskDevice);
					UdiskDevice = INVALID_HANDLE_VALUE;
				}
				DeviceNum++;
			}
		}
	}

	// SetupDiDestroyDeviceInfoList() destroys a device information set
	// and frees all associated memory.
	SetupDiDestroyDeviceInfoList(hDevInfoList);
		return FALSE;
}

二、USB HID写操作

void CCoreUSBHIDCommToolDlg::OnBnClickedBtnSend()
{
	CString Cstr;
	int Len;
	UINT16 OutLength = 0;
	UINT8 OutData[4096] = { 0 };
	UINT8 InputData[1024] = { 0 };
	PHIDP_PREPARSED_DATA PreparsedData;
	HIDP_CAPS Capabilities;
	CString DevicePath = m_USBPath;

	UpdateData(TRUE);
	Cstr = m_SendInfo;
	Cstr.Remove(' ');

	Len = Cstr.GetLength();
	memcpy(InputData, Cstr.GetBuffer(), Len);
	if ((Len == 0) || (Len % 2 != 0))
	{
		AfxMessageBox("Input Data Error!");
		return;
	}
	if (AsciiToHex(InputData, Len, OutData, &OutLength) == FALSE)
	{
		AfxMessageBox("Input Data Error!");
		return;
	}
	unsigned long NumberOfBytesWriten = 0;
	if (hWriteCom == INVALID_HANDLE_VALUE)
	{
		AfxMessageBox("Comm Fail...");
		return;
	}
	OVERLAPPED    Overlapped;

	Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	Overlapped.Offset = 0;
	Overlapped.OffsetHigh = 0;

	SetLastError(NO_ERROR);
	int WriteTimeout = 100;
	int status = 1;
	BOOL bRet = WriteFile(hWriteCom, OutData, WriteLen, &NumberOfBytesWriten, &Overlapped);
	if (bRet)
	{
		status = 0;
	}
	if (status != 0)
	{
		DWORD CommLastErrCode = GetLastError();
		CloseHandle(hWriteCom);
		hWriteCom = INVALID_HANDLE_VALUE;
		CString strDate;
		SYSTEMTIME st;
		GetLocalTime(&st);
		strDate.Format("%02d-%02d-%02d", st.wHour, st.wMinute, st.wSecond);
		((CEdit*)GetDlgItem(IDC_EDIT_Recv))->ReplaceSel("Cannot Write Data..." + strDate + "\r\n");
		return;
	}
	AfxBeginThread(UsbRunReadInfo, this);
}

三、USB HID读操作

UINT  CCoreUSBHIDCommToolDlg::UsbRunReadInfo(void* pParam)
{
	CCoreUSBHIDCommToolDlg* pData = (CCoreUSBHIDCommToolDlg*)pParam;
	CString DevicePath = pData->m_USBPath;

	BOOL result1;
	unsigned long numBytesReturned;
	


	int Imglen = 0;
	CString str = NULL;
	clock_t start, end, final = 0;
	pData->SetDlgItemText(IDC_EDIT_Recv, "");
	if (pData->hReadCom == INVALID_HANDLE_VALUE)
	{
		AfxMessageBox("Comm Fail...");
		return 1;
	}
	CFile cf;
	FILE* fp;
	if (!(fp = fopen("Temp.bin", "wb")))
	{
		return 0;
	}
	if (!cf.Open(_T("Recvinfo.txt"), CFile::modeCreate | CFile::modeWrite))
	{
		AfxMessageBox(_T("Recvinfo.txt"));
		return 0;
	}
	OVERLAPPED    Overlapped;
	DWORD  Flag;
	Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	Overlapped.Offset = 0;
	Overlapped.OffsetHigh = 0;
	int status = 1;
	int cusumlen = 0;
	int OffsetAddr = 0;//偏移地址
	byte inbuffer[4096] = { 0 };
	clock_t	dwStart = clock();
	clock_t	dwEnd;
	UINT8* Startptr = RecvBuffer;
	//第一位数据是报ID,无效参数
	while (ReadFile(pData->hReadCom, Startptr, pData->ReadLen, &numBytesReturned, &Overlapped))
	{
		memcpy(&RecvBufferNoID[OffsetAddr], &Startptr[1], (numBytesReturned - 1));
		*Startptr = *(Startptr + (numBytesReturned - 1));
		OffsetAddr += (numBytesReturned - 1);
		cusumlen += numBytesReturned;
		if (OffsetAddr >= sumlen)
		{
			break;
		}
		dwEnd = clock();
		if (dwEnd - dwStart > 5000)
		{
			break;
		}
	}
	dwEnd = clock();
	if (OffsetAddr != sumlen)
	{
		CString strDate;
		SYSTEMTIME st;
		GetLocalTime(&st);
		strDate.Format("%02d-%02d-%02d", st.wHour, st.wMinute, st.wSecond);

		((CEdit*)pData->GetDlgItem(IDC_EDIT_Recv))->ReplaceSel("Read Data Timeout..." + strDate + "\r\n");
		CloseHandle(pData->hReadCom);
		pData->hReadCom = INVALID_HANDLE_VALUE;
		fclose(fp);
		cf.Close();
		return 0;
	}
	int length = 0;
	int i = 0;
	for (i = 0; i < sumlen; i++)
	{
		CString Tmp;
		Tmp.Format("%02X", RecvBufferNoID[i]);
		str += Tmp;
		if ((i - 1) % 53 == 0 && i != 0)
		{
			str += "\r\n";
		}
	}
	if ((i - 1) % 53 != 0)
	{
		str += "\r\n";
	}
	fwrite(RecvBufferNoID, sizeof(unsigned char), sumlen, fp);
	fclose(fp);
	//DWORD	dwEnd = GetTickCount();
	CString ss;
	ss.Format("时间为%d ms", dwEnd - dwStart);
	str += ss;
	((CEdit*)(pData->GetDlgItem(IDC_EDIT_Recv)))->SetWindowText(str);
	((CEdit*)(pData->GetDlgItem(IDC_EDIT_Recv)))->SetSel(str.GetLength(), str.GetLength());
	((CEdit*)(pData->GetDlgItem(IDC_EDIT_Recv)))->ReplaceSel("\r\n");
	int len = str.GetLength();
	cf.Write(str.GetBuffer(len), len);
	cf.Close();

	FILE* fpbmp; 
	fpbmp = fopen("Finger.bmp", "wb");
	if (fpbmp == NULL)
	{
		fclose(fpbmp);
		return FALSE;
	}
	*((int*)&bmpDummy[18]) = width;
	*((int*)&bmpDummy[22]) = High;
	fwrite(bmpDummy, 1078, sizeof(char), fpbmp);
	fwrite(RecvBufferNoID, width * High, sizeof(unsigned char), fpbmp);
	fclose(fpbmp);
	return 0;
}

四、当时封装的其他函数

/******************************************************************************************************
** 函数名称:	AsciiToHex
** 功能描述:	ASCII 到 HEX 的转换函数
** 形    参:  	UINT8  *_pInData   ,传入的ASCII数据缓冲,该缓冲在内部会被该变
**              UINT16 _nInLength  ,*_pInData缓冲区的长度
**              UINT8  *_pOutData  ,存放HEX数据的缓冲
**              UINT16 *_nOutLength,存放HEX数据的长度
** 返	 回:    true 转换成功,false 转换失败
********************************************************************************************************/
BOOL AsciiToHex(UINT8* _pInData, UINT16 _nInLength, UINT8* _pOutData, UINT16* _nOutLength)
{
	UINT16 i, j, nLen;
	UINT8 TmpData;
	for (i = 0; i < _nInLength; i++)
	{
		if ((_pInData[i] >= '0') && (_pInData[i] <= '9'))
			TmpData = _pInData[i] - '0';
		else if ((_pInData[i] >= 'A') && (_pInData[i] <= 'F')) //A....F
			TmpData = _pInData[i] - 0x37;
		else if ((_pInData[i] >= 'a') && (_pInData[i] <= 'f'))  //a....f
			TmpData = _pInData[i] - 0x57;
		else
			return FALSE;
		_pInData[i] = TmpData;
	}
	for (nLen = 0, j = 0; j < i; j += 2)
		_pOutData[nLen++] = (_pInData[j] << 4) | _pInData[j + 1];

	*_nOutLength = nLen;
	return TRUE;
}

void CCoreUSBHIDCommToolDlg::OnBnClickedBtnOpen()
{
	// TODO: 在此添加控件通知处理程序代码
	CString str, Tstr, strDate;
	USHORT m_Pid, m_Vid, m_Pvn;
	GetDlgItemText(IDC_EDIT_PID, str);
	if (str.IsEmpty())
	{
		AfxMessageBox("PID 输入为空!!!");
		return;
	}
	m_Pid = strtoull(str, NULL, 16);
	GetDlgItemText(IDC_EDIT_VID, str);
	if (str.IsEmpty())
	{
		AfxMessageBox("VID 输入为空!!!");
		return;
	}
	m_Vid = strtoull(str, NULL, 16);
	GetDlgItemText(IDC_EDIT_PVN, str);
	if (str.IsEmpty())
	{
		AfxMessageBox("PVN 输入为空!!!");
		return;
	}
	m_Pvn = strtoull(str, NULL, 16);
	//获得当前时间,年月日
	SYSTEMTIME st;
	GetLocalTime(&st);
	strDate.Format("%02d-%02d-%02d", st.wHour, st.wMinute, st.wSecond);
	((CEdit*)GetDlgItem(IDC_EDIT_Recv))->ReplaceSel("正在打开设备,请等待" + strDate + "\r\n");
	char charstr[128] = { 0 };
	int len = 0;
	BOOL ret = Enum(m_Pid, m_Vid, m_Pvn, charstr, &len);
	if (ret == FALSE)
	{
		GetLocalTime(&st);
		memset(m_USBPath, 0, sizeof(char) * 128);
		strDate.Format("%02d-%02d-%02d", st.wHour, st.wMinute, st.wSecond);
		((CEdit*)GetDlgItem(IDC_EDIT_Recv))->ReplaceSel("打开设备失败未识别到设备" + strDate + "\r\n");
		return;
	}

	memcpy(m_USBPath, charstr, len);
	PHIDP_PREPARSED_DATA PreparsedData;
	HIDP_CAPS Capabilities;

	//得到读句柄/写句柄,及读与写的长度,不然放到读写函数里去实现会导致通信超时
	hWriteCom = CreateFile(
		m_USBPath,
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING, 0,
		NULL);

	if (hWriteCom == INVALID_HANDLE_VALUE)
	{
		AfxMessageBox("Comm Fail...");
		return ;
	}
	if (!HidD_GetPreparsedData(hWriteCom, &PreparsedData))
	{
		CloseHandle(hWriteCom);
		CString strDate;
		SYSTEMTIME st;
		GetLocalTime(&st);
		strDate.Format("%02d-%02d-%02d", st.wHour, st.wMinute, st.wSecond);
		((CEdit*)GetDlgItem(IDC_EDIT_Recv))->ReplaceSel("Cannot get the Preparsed Data..." + strDate + "\r\n");
		return ;
	}

	if (!HidP_GetCaps(PreparsedData, &Capabilities))
	{
		CloseHandle(hWriteCom);
		CString strDate;
		SYSTEMTIME st;
		GetLocalTime(&st);
		strDate.Format("%02d-%02d-%02d", st.wHour, st.wMinute, st.wSecond);
		((CEdit*)GetDlgItem(IDC_EDIT_Recv))->ReplaceSel("Cannot get the Cap Data..." + strDate + "\r\n");
		return ;
	}
	WriteLen = Capabilities.OutputReportByteLength;
	hReadCom = CreateFile(
		m_USBPath,
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE,
		NULL,
		OPEN_EXISTING, 0,
		NULL);

	if (hReadCom == INVALID_HANDLE_VALUE)
	{
		AfxMessageBox("Comm Fail...");		
		CloseHandle(hWriteCom);
		hWriteCom = INVALID_HANDLE_VALUE;
		return;
	}
	if (!HidD_GetPreparsedData(hReadCom, &PreparsedData))
	{
		CloseHandle(hWriteCom);
		CloseHandle(hReadCom);
		hWriteCom = INVALID_HANDLE_VALUE;
		hReadCom = INVALID_HANDLE_VALUE;
		CString strDate;
		SYSTEMTIME st;
		GetLocalTime(&st);
		strDate.Format("%02d-%02d-%02d", st.wHour, st.wMinute, st.wSecond);
		((CEdit*)GetDlgItem(IDC_EDIT_Recv))->ReplaceSel("Cannot get the Preparsed Data..." + strDate + "\r\n");
		return;
	}

	if (!HidP_GetCaps(PreparsedData, &Capabilities))
	{
		CloseHandle(hWriteCom);
		CloseHandle(hReadCom);
		hWriteCom = INVALID_HANDLE_VALUE;
		hReadCom = INVALID_HANDLE_VALUE;
		CString strDate;
		SYSTEMTIME st;
		GetLocalTime(&st);
		strDate.Format("%02d-%02d-%02d", st.wHour, st.wMinute, st.wSecond);
		((CEdit*)GetDlgItem(IDC_EDIT_Recv))->ReplaceSel("Cannot get the Cap Data..." + strDate + "\r\n");
		return;
	}
	
	ReadLen = Capabilities.OutputReportByteLength;
	GetLocalTime(&st);
	strDate.Format("%02d-%02d-%02d", st.wHour, st.wMinute, st.wSecond);
	((CEdit*)GetDlgItem(IDC_EDIT_Recv))->ReplaceSel("打开设备成功"+ strDate + "\r\n");
	SaveCfg();
	((CButton*)GetDlgItem(IDC_BTN_Close))->EnableWindow(TRUE);
	((CButton*)GetDlgItem(IDC_BTN_Open))->EnableWindow(FALSE);
	((CButton*)GetDlgItem(IDC_BTN_Send))->EnableWindow(TRUE);
}``

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
史上最全USB HID开发资料,悉心整理一个月,亲自测试。 涉及STM32 C51 8051F例子都有源码,VC上位机例子以及源码,USB协议,HID协议,USB抓包工具,开发文档,开发过程文档。 详细内容就不都说了,看目录。 有了这个资料包,你再说开发不了USB HID,打死我都不信! 好资料当然一分都不能少! 目录: STM32_USB_HID_PC_demo:USB HID Demonstrator Release软件和文档 STM32_USB_HID_分析:STM32 USB HID 固件学习分析 STM32_USB_HID_例子:STM32 USB HID方式收发例子。 STM32_USB_HID_学习心得:基于STM32 的USB程序开发笔记、修改STM32的USB例程为自己所用、初涉USB,初学者USB入门总结——枚举。 STM32_固件库说明文档:STM32_USB_Demo例子的中文说明文档。 STM32F107 鼠标USBHID数据发送程序。 USB_HID_8051F例子。 USB_HID_C51源码。 USB_HID_PC_源码:PC端打开HID设备读写操作实例。 USB_HID_PC接收发送工具:用于调试USB HID设备,就相当于串口工具啦。自己写好了HID设备,用它接收发送调试非常方便。 USB_HID_PC通信详解:PC端HID读写操作说明。 USB_HID_VC++6.0_入门级例子:简单读写USB HID设备,很好的参考作用。 USB_HID_VC++6.0_入门级例子开发步骤(图解说明,真详细啊!):一步一步手把把手教你开发VC++6.0 USB HID程序。 USB_HID_VC++6.0读写设备源码:简单打开HID设备,读写源码,参考的好例子。 USB_HID_VC++6.0源码:出具雏形的VC++6.0 USB HID 工具源码。非常完善了,可以直接当工具使用,关键是有源码! USB_HID_开发过程详细说明:长篇论文一篇,详细说明HID开发过程,包括下位机、上位机、HID驱动的开发,牛! USB_HID协议(英文)。 USB_STM32_HID开发笔记:里面有USB设备枚举的详细过程,抓包说明的哦。当然包括开发过程啦。令牌包、握手包、数据包中的数据都看得到。 USB_URB分析:抓包工具抓到的数据包的详细解析。 USB_VC教程:短论文一篇,用VC++编写USB接口通信程序,简洁扼要说明VC++6.0开发步骤,提纲挈领,值得一看! USB2.0协议(英文)。 USB技术规范(中文):中文的USB技术规范说明,中文的! USB抓包软件:两种抓包工具,bushound和usbtrace。都是破解版,哈哈,自己偷着乐吧! 深入解析STM32_USB库:STM32 USB的库说明。
USB HID设备是一种使用USB接口进行通信的设备,它可以通过发送和接收数据来实现和计算机的交互。为了读写USB HID设备数据,我们需要编写一个程序来实现这个功能。 首先,我们需要使用合适的编程语言和相关的库来开发这样一个程序。对于Windows系统,我们可以使用C#、C++或者Python等语言来编写程序;对于Linux系统,则可以选择C语言、Python等来实现。然后,我们需要选择一个合适的USB库,比如libusb,它提供了一些用于控制USB设备的函数和结构体。 接着,我们需要编写程序来进行USB设备的初始化和连接。一般来说,我们需要找到目标设备的vendor ID和product ID,然后使用相关函数来打开设备并进行数据的读写。 在程序运行的过程中,我们可以使用相关函数来读取设备发送的数据,也可以发送命令或者数据给设备。在读取数据时,我们需要根据设备的协议和数据格式进行解析和处理,以确保能正确地获取到设备发送的信息。 最后,我们需要进行错误处理和资源释放。当程序出现错误时,我们需要进行相应的处理,比如关闭设备,释放相关资源等,以确保程序的稳定性和可靠性。 总的来说,编写USB HID设备数据读写程序是一个需要考虑设备协议、数据格式等因素的复杂任务,但通过合适的编程语言和库的选择,以及认真的编程实现,我们可以成功地实现这一功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值