在vs2015中如何获取U盘的pid或vid
//保存HID设备的接口类GUID
GUID HidGuid;
//保存获取到的设备信息集合句柄
HDEVINFO hDevInfoSet;
//表示当前搜索到第几个设备,0表示第一个设备
DWORD MemberIndex;
//用来保存设备的驱动接口信息
SP_DEVICE_INTERFACE_DATA DevInterfaceData;
//保存函数调用是否返回成功
BOOL result;
//用来接收需要保存详细信息的缓冲长度
DWORD requiredSize;
//指向设备详细信息的结构体指针
PSP_DEVICE_INTERFACE_DETAIL_DATA pDevDetailData;
//保存打开设备的句柄
HANDLE hDevHandle;
//保存设备属性
HIDD_ATTRIBUTES devAttributes;
myDevFound = FALSE;
//获取文本框中设置的Pid,Vid,Pvn
GetMyIDs();
//初始化读写句柄为无效句柄
hReadHandle = INVALID_HANDLE_VALUE;
hWritedHandle = INVALID_HANDLE_VALUE;
DevInterfaceData.cbSize = sizeof(DevInterfaceData);
devAttributes.Size = sizeof(devAttributes);
//获取HID设备的GUID,保存在HidGuid中
HidD_GetHidGuid(&HidGuid);
//根据HidGuid来获取设备信息集合,其中flags参数设置为DIGCF_DEVICEINTERFACE|DIGCF_PRESENT
//前者表示使用的GUID为接口类的GUID,后者表示只列举正在使用的设备,因为我们这里只查找连接上的设备
//返回 的句柄保存在hDevInfo,注意设备信息集合在使用了之后,要使用setupDIDestroyDeviveInfoList销毁,
//不然可能造成内存泄漏
hDevInfoSet = SetupDiGetClassDevs(&HidGuid,
NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
AddToInfOut("开始查找设备",TRUE,TRUE);
//然后对设备集合中的每个设备进行枚举,检查是否是我们要查找的设备
//当找到我们指定的设备,或者设备已经查找完毕时,就退出查找
//首先指向第一个设备,即将memberIndex置为零
MemberIndex = 0;
while (1)
{
//调用SetupDiEnumInterfaces函数在设备信息集合中获取参数为memberIndex的设备信息
result = SetupDiEnumDeviceInterfaces(
hDevInfoSet,
NULL,
&HidGuid,
MemberIndex,
&DevInterfaceData
);
if (result == FALSE)
break;
MemberIndex++;
//如果获取信息成功,则继续获取该设备的详细信息,在获取设备详细信息时,
//需要先知道保存详细信息需要多大的缓冲区,这通过第一次调用函数SetupDiGetDeviceInterfacesDetail
//来获取,这时 提供缓冲区和长度都为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(*pDevDetailData);
SP_DEVINFO_DATA deviceInfoData;
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
//然后再次调用SetupDiGetDeviceInterfaceDetail函数来获取设备的详细信息,
//这次调用设置使用的缓冲区以及缓冲区的大小
result = SetupDiGetDeviceInterfaceDetail(
hDevInfoSet,
&DevInterfaceData,
pDevDetailData,
requiredSize,
NULL,
NULL
);
/* INT a= GetLastError();*/
//将设备路径取出来,然后销毁刚刚申请的内存
myDevPathName = pDevDetailData->DevicePath;
free(pDevDetailData);
//如果查找设备失败,则继续查找下一个设备
if (result == FALSE)
continue;
//如果调用成功,则使用不带读写访问的createfile函数来获取设备的属性,
//包括vid,pid,pvn,等,对于一些独占设备(例如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;
//如果获取成功,则将属性中的pid,vid,pvn与我们输入的比较
//如果一致则说明找到了我们要的设备
if (devAttributes.VendorID == myVid)
if (devAttributes.ProductID == myPid)
/*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("读访问打开设备成功",TRUE,TRUE);
else AddToInfOut("读访问打开设备失败",TRUE,TRUE);
//写访问的方式打开设备
hWritedHandle = CreateFile(myDevPathName,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (hWritedHandle != INVALID_HANDLE_VALUE)AddToInfOut("写访问打开设备成功",TRUE,TRUE);
else("写访问打开设备失败",TRUE,TRUE);
//可以发送数据
DataInsending = FALSE;
//手动触发事件,让读报告线程恢复运行,因为在这之前并没有调用读数据的函数,
//也就不会引起读事件的产生,所以需要先手动触发一次事件,让读报告线程恢复运行
SetEvent(readOverLapped.hEvent);
//显示设备的状态
SetDlgItemText(IDC_EDIT4, "设备已打开");
//找到设备,退出循环,本程序只检测一个目标设备,找到后就退出查找
//如果你需要把所以的目标设备的都列出来的话,可以设置一个数组,
//找到后就设置在数组中,直达所有设备都查找完毕才退出查找
break;
}
}
else
continue;
}
//调用setupDiDestroyDeviceInfoList销毁设备信息集合
SetupDiDestroyDeviceInfoList(hDevInfoSet);
//如果设备已经找到,应该使能各个功能按钮,同时使打开设备按钮失效
if (myDevFound)
{
GetDlgItem(IDC_BUTTON3)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON4)->EnableWindow(TRUE);
}
else
{
AddToInfOut("设备未找到",TRUE,TRUE);
}
相关推荐
VS2010开发的USB HID调试工具包:含源码、可执行程序与HID系统库
一款开箱即用的USB HID设备调试工具,基于Visual Studio 2010开发,支持Windows平台下HID设备自动枚举、数据读写、描述符解析和基础稳定性测试。压缩包内提供完整VS2010工程(.sln/.vcxproj)、C++核心源文件(HID_Tool.cpp、HID_ToolDlg.cpp等)、已编译的HID_Tool.exe可执行程序,以及必需的Windows HID相关头文件(hidsdi.h、hidusage.h、hidpi.h)和链接库(hid.lib、setupapi.lib)。
2026年NVMe SSD选购核心逻辑:协议兼容、散热设计与FTL固件深度解析
NVMe固态硬盘并非即插即用的黑盒设备,其性能本质由NVMe协议支持、FTL(闪存转换层)固件调度、主控与NAND协同及散热设计共同决定。理解PCIe通道来源可避免H81/B85等老平台因协议不兼容导致设备无法识别;掌握FTL固件版本差异与量产工具(如MPTools)调优能力,能显著改善持续写入稳定性与温度控制;而散热设计已成实际性能守门员——温度每升高10℃,NAND寿命衰减约1.8倍。本文聚焦真实负载下的稳定性权衡,揭示PCIe 4.0与5.0在长时工作场景中的临界点迁移规律,并提供可落地的平台兼容方案
C#工业级USB-CAN上位机开发实战:低延迟、零拷贝、高可靠
CAN总线是工业现场设备通信的核心协议,其稳定解析与实时控制依赖于底层硬件握手、高效帧解析和确定性时序调度。理解USB-CAN适配器的物理层交互机制、掌握零拷贝内存解析技术、构建WPF界面与CAN指令分离的双通道架构,是实现毫秒级响应的关键。这类上位机不仅需兼容Win7/Win10工控环境,更要规避驱动抖动、内存泄漏与UI线程阻塞等典型工程陷阱。P4方案以C# + WPF + LibUsbDotNet为技术栈,聚焦CAN垂直优化,为PLC调试、BMS监控、伺服控制等场景提供可测、可控、可追溯的数据枢纽能力。
宝峰UV9R-Plus写频全指南:驱动、CHIRP配置与硬件避坑
对讲机写频是构建可靠无线通信链路的基础操作,本质是为设备注入符合射频规范的参数契约。其原理涉及串口通信协议转换、电台固件内存映射及亚音/功率等物理层参数协同。技术价值在于确保通联稳定性、抗干扰能力与电池续航平衡。典型应用场景包括工地调度、户外应急、车队管理及业余无线电实操。过程中需重点关注USB转TTL芯片识别(如CH340/FT232R)、CHIRP型号模板匹配(UV-9R Plus Analog/DMR)两大核心环节——选错芯片驱动导致电脑无法识别,模板误配则引发频率偏移或静噪失效,二者共同构成90%写
Arduino IDE安装指南:三个平台的串口驱动与权限配置全攻略
嵌入式开发入门的第一步往往是搭建编程环境,而Arduino IDE正是连接硬件与代码的桥梁。对于初学者来说,Windows、macOS、Linux三大平台在安装过程中常会遇到串口驱动缺失、系统权限拦截、开发板核心未安装等问题,背后的核心在于理解USB转串口芯片、设备权限管理和开发板管理器的工作机制。掌握这些基础原理,不仅能顺利解决IDE端口识别、上传失败等高频故障,还能为后续ESP32、RP2040等主流开发板的扩展配置打下坚实基础。本指南从环境差异出发,系统梳理版本选择、驱动安装、权限配置、开发板核心安装


