上位机学习笔记:Hid上位机软件的实现
利用了aardio这个软件,
话说这个软件应该属于小众软件了.
其实本人也没接触过多长时间,但是我还是推荐给目前只会下位机编程,上位机还从未自己实现过的人试试.
这款软件本身就只有5M,而且绿色不用安装,我一般都是放U盘,直接走哪用哪.
它生成的软件都是绿色的,就是经常看到的那种只有一个exe的软件.
好了,其他不多说,直接先上程序,之后再记录一些自己编辑过程中遇到的问题和解决的办法!
首先,我自己定义了一个用户hid库文件,名字Usbhidapi.aardio
//Usbhidapi用户库
namespace Usbhidapi;
var dll = ..raw.loadDll($"\res\hidapi.dll", , "cdecl");
//初始化函数,0成功,-1失败
Hidapi_init= dll.api("hid_init","int(void)");
//释放端口函数,0成功,-1失败
Hidapi_exit= dll.api("hid_exit","int(void)");
//枚举端口
Hidapi_enumerate= dll.api("hid_enumerate","struct(WORD vendor_id, WORD product_id)","cdecl");
//释放枚举端口
Hidapi_free_enumeration= dll.api("hid_free_enumeration","void(struct devs)","cdecl");
//vid和pid方式打开端口
Hidapi_open= dll.api("hid_open","pointer(WORD vendor_id, WORD product_id, string serial_number)","cdecl");
//路径名方式打开
Hidapi_open_path= dll.api("hid_open_path","pointer(STRING path)","cdecl");
//发送数据函数,返回发送成功的长度 或者 发送失败-1
Hidapi_write= dll.api("hid_write","int(POINTER device, pointer data, INT length)","cdecl");
//读取(超时方式)
Hidapi_read_timeout= dll.api("hid_read_timeout","int(POINTER device, pointer data, INT length, INT milliseconds)","cdecl");
//读取(普通方式)
Hidapi_read= dll.api("hid_read","int(POINTER device, pointer data, INT length)","cdecl");
//设置阻塞模式 0 使用,1 不使用
Hidapi_set_nonblocking= dll.api("hid_set_nonblocking","int(POINTER device, int nonblock)","cdecl");
//发送功能报告
Hidapi_send_feature_report= dll.api("hid_send_feature_report","int(POINTER device, pointer data, int length)","cdecl");
//获取功能报告
Hidapi_get_feature_report= dll.api("hid_get_feature_report","int(POINTER device, pointer data, int length)","cdecl");
//关闭端口函数
Hidapi_close = dll.api("hid_close","void(POINTER device)","cdecl");
//获取用户码
Hidapi_get_manufacturer_string= dll.api("hid_get_manufacturer_string","int(POINTER device, ustring& data, INT maxlen)","cdecl");
//获取产品码
Hidapi_get_product_string= dll.api("hid_get_product_string","int(POINTER device, ustring& data, int maxlen)","cdecl");
//获取序列号
Hidapi_get_serial_number_string= dll.api("hid_get_serial_number_string","int(POINTER device, ustring& data, int maxlen)","cdecl")
//获取字符(按照给定的帧首[string_index]获取 1=manufacturer_strin, 2=product_string, 3=serial_number_string),同上面的功能一样
Hidapi_get_indexed_string= dll.api("hid_get_indexed_string","int(POINTER device, int string_index, ustring& data, int maxlen)","cdecl");
//获取错误信息
Hidapi_error= dll.api("hid_error","ustring(POINTER device)","cdecl");
然后就是界面实现和使用逻辑.
名称:main.aardio
import win.ui;
import Usbhidapi;
import console;
/*DSG{{*/
mainForm = win.form(text="aardio form";right=691;bottom=408;border="dialog frame")
mainForm.add(
Rxdata={cls="listbox";left=429;top=57;right=650;bottom=128;edge=1;items={};z=20};
Txdata={cls="edit";text="123456abcd";left=136;top=101;right=304;bottom=134;align="center";edge=1;z=19};
button={cls="button";text="初始化";left=40;top=146;right=140;bottom=181;z=1};
button10={cls="button";text="设置非阻塞";left=40;top=251;right=140;bottom=286;z=10};
button11={cls="button";text="发送功能报告";left=381;top=146;right=481;bottom=181;z=11};
button12={cls="button";text="获取功能报告";left=381;top=199;right=481;bottom=234;z=12};
button13={cls="button";text="关闭";left=553;top=302;right=653;bottom=337;z=13};
button14={cls="button";text="获取用户码";left=219;top=146;right=319;bottom=181;z=14};
button15={cls="button";text="获取产品码";left=219;top=198;right=319;bottom=234;z=15};
button16={cls="button";text="获取序列号";left=219;top=251;right=319;bottom=286;z=16};
button17={cls="button";text="获取首字符";left=219;top=304;right=319;bottom=338;z=17};
button18={cls="button";text="获取错误信息";left=553;top=248;right=653;bottom=283;z=18};
button2={cls="button";text="退出";left=553;top=356;right=653;bottom=392;z=2};
button3={cls="button";text="枚举";left=381;top=304;right=481;bottom=339;z=3};
button4={cls="button";text="释放枚举";left=381;top=357;right=481;bottom=391;z=4};
button5={cls="button";text="普通方式打开";left=40;top=198;right=140;bottom=234;z=5};
button6={cls="button";text="路径名方式打开";left=381;top=252;right=481;bottom=286;z=6};
button7={cls="button";text="发送";left=40;top=304;right=140;bottom=340;z=7};
button8={cls="button";text="读取(超时)";left=219;top=356;right=319;bottom=391;z=8};
button9={cls="button";text="读取";left=40;top=357;right=140;bottom=391;z=9};
static={cls="static";text="发送数据:";left=63;top=109;right=121;bottom=132;transparent=1;z=21};
static2={cls="static";text="接收数据:";left=356;top=106;right=413;bottom=125;transparent=1;z=22};
static3={cls="static";text="UsbHid库调用示例";left=241;top=12;right=436;bottom=50;align="center";font=LOGFONT(h=-24);transparent=1;z=23}
)
/*}}*/
hid_device_info = class {
string path ;
WORD vendor_id ;
WORD product_id ;
ustring serial_number;
WORD release_number ;
ustring manufacturer_string ;
ustring product_string ;
WORD usage_page ;
WORD usage;
int interface_number;
pointer next ;
};
//转换成AISCII
Aiscii=function(data,len){
var str="";
for(i=1;len;1){
str ++=string.pack(data[i]);
}
return str;
}
var Hiddevice;
mainForm.button3.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button3.text );
var devs = hid_device_info();
var cur_dev;
var devlist = Usbhidapi.Hidapi_enumerate(0,0);
cur_dev = raw.convert(devlist,hid_device_info());
while(cur_dev){
console.dump(cur_dev);
cur_dev = cur_dev.next;
if(cur_dev){
cur_dev = raw.convert(cur_dev,hid_device_info());
}
else {
break;
}
}
}
mainForm.button6.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button6.text );
//mainForm.msgbox( "这个功能下面的写的不对,就没测了" );
/*
var str = "\\?\hid#vid_5555&pid_5555#6&8282ff9&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}";
Hiddevice = Usbhidapi.Hidapi_get_feature_report(str);
if(Hiddevice){
console.log("路径方式打开成功!")
}else {
console.log("路径方式打开失败!")
}
*/
}
mainForm.button12.oncommand = function(id,event){
mainForm.msgbox( "硬件电路里面没有使用到控制端点,所以这个功能没有测试" );
//硬件电路里面没有使用到控制端点,所以这个功能没有测试
/**
var buf = raw.malloc(1000, 0);
var len = 100;
var Rxlength = Usbhidapi.Hidapi_get_feature_report(Hiddevice, buf, len);
if(Rxlength == -1){
console.log("读取失败!")
}else {
console.log("读取成功!!读取长度为:",Rxlength,'\n读取到:' /*,raw.tostring(databuf)*/);
//console.log(raw.tostring(databuf, 1, Rxlength));
if(Rxlength == 0){
return; //没数据退出
}
var str = "";
str = Aiscii(buf,Rxlength);
mainForm.Rxdata.add(str);
console.log(str);
}
**/
}
mainForm.button11.oncommand = function(id,event){
mainForm.msgbox( "硬件电路里面没有使用到控制端点,所以这个功能没有测试" );
//硬件电路里面没有使用到控制端点,所以这个功能没有测试
/*
var Txlength = Usbhidapi.Hidapi_send_feature_report(Hiddevice,'\0'+mainForm.Txdata.text,#mainForm.Txdata.text+1);
if(Txlength == -1){
console.log("发送失败!")
}else {
console.log("发送成功!!发送长度为:",Txlength)
}
*/
}
mainForm.button8.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button8.text );
var buf = raw.malloc(1000, 0);
var len,ms = 100,1000;
var Rxlength = Usbhidapi.Hidapi_read_timeout(Hiddevice, buf, len, ms);
if(Rxlength == -1){
console.log("读取失败!")
}else {
console.log("读取成功!!读取长度为:",Rxlength,'\n读取到:' /*,raw.tostring(databuf)*/);
//console.log(raw.tostring(databuf, 1, Rxlength));
if(Rxlength == 0){
return; //没数据退出
}
var str = "";
str = Aiscii(buf,Rxlength);
mainForm.Rxdata.add(str);
console.log(str);
}
}
mainForm.button17.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button17.text );
var buf = raw.malloc(1000);
//var buf= "";
var Maxlen = 255;
var ret,buf2= Usbhidapi.Hidapi_get_indexed_string(Hiddevice,3,buf,Maxlen);
if(ret == -1){
console.log("获取首字符失败!")
}else {
console.log("获取首字符成功!")
console.log("首字符为:",buf2);
}
}
mainForm.button16.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button16.text );
var buf = raw.malloc(1000);
//var buf= "";
var Maxlen = 255;
var ret,buf2= Usbhidapi.Hidapi_get_serial_number_string(Hiddevice,buf,Maxlen);
if(ret == -1){
console.log("获取序列号失败!")
}else {
console.log("获取序列号成功!")
console.log("序列号为:",buf2);
}
}
mainForm.button15.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button15.text );
var buf = raw.malloc(1000);
//var buf= "";
var Maxlen = 255;
var ret,buf2= Usbhidapi.Hidapi_get_product_string(Hiddevice,buf,Maxlen);
if(ret == -1){
console.log("获取产品码失败!")
}else {
console.log("获取产品码成功!")
console.log("产品码为:",buf2);
}
}
mainForm.button14.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button14.text );
var buf = raw.malloc(1000);
//var buf= "";
var Maxlen = 255;
var ret,buf2= Usbhidapi.Hidapi_get_manufacturer_string(Hiddevice,buf,Maxlen);
if(ret == -1){
console.log("获取用户码失败!")
}else {
console.log("获取用户码成功!")
console.log("用户码为:",buf2);
}
}
mainForm.button13.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button13.text );
Usbhidapi.Hidapi_close(Hiddevice);
console.log("已经关闭");
}
//var buf = raw.malloc(100, 0);
mainForm.button9.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button9.text );
var buf = raw.malloc(1000, 0);
var len = 100;
var Rxlength = Usbhidapi.Hidapi_read(Hiddevice, buf, len);
if(Rxlength == -1){
console.log("读取失败!")
}else {
console.log("读取成功!!读取长度为:",Rxlength,'\n读取到:' /*,raw.tostring(databuf)*/);
//console.log(raw.tostring(databuf, 1, Rxlength));
if(Rxlength == 0){
return; //没数据退出
}
var str = "";
str = Aiscii(buf,Rxlength);
mainForm.Rxdata.add(str);
console.log(str);
}
}
mainForm.button7.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button7.text );
var Txlength = Usbhidapi.Hidapi_write(Hiddevice,'\0'+mainForm.Txdata.text,#mainForm.Txdata.text+1);
if(Txlength == -1){
console.log("发送失败!")
}else {
console.log("发送成功!!发送长度为:",Txlength)
}
}
mainForm.button10.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button10.text );
var ret = Usbhidapi.Hidapi_set_nonblocking(Hiddevice, 1);
if(ret == -1){
console.log("设置非阻塞模式失败!")
}else {
console.log("设置非阻塞模式成功!")
}
}
mainForm.button5.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button5.text );
Hiddevice = Usbhidapi.Hidapi_open(21845, 21845, null);
if(Hiddevice){
console.log("Vid和Pid方式打开成功!")
}else {
console.log("Vid和Pid方式打开失败!")
}
}
mainForm.button.oncommand = function(id,event){
//mainForm.msgbox( mainForm.button.text );
var ret = Usbhidapi.Hidapi_init();
if(ret == -1){
console.log("初始化失败!")
}else {
console.log("初始化成功!")
}
}
mainForm.show()
return win.loopMessage();
下面是这个软件实现后的图片.
功能演示:
29 个回复
zdsurge
赞同来自:
历害,慢慢学习中。
admin
赞同来自:
软件程序截图:
admin
赞同来自: 楚鸟 、 xiaolong361
源码和exe下载:本站网盘,名称:Usbhid_aardio上位机实现.zip
http://pan.baidu.com/s/1hsbT6Eoadmin
赞同来自:
这里写下编写过程中遇到的问题:
首先是dll库的声明中某些数据类型写错导致后续获取不到想要的数据
比如:open的C原型为
在这里遇到的问题就是这个hid_device* 指针,我试图把这个指针的结构体都弄出来了,因为我在他的文档里面看到了
所以我理所当然的认为应该定义为一个struct类型,然后列出这个struct类型,下面就是我定义错误的代码:
这个错误导致后面获取hid_device这个handle一直失败,陷入了死胡同!
这里正确的理解应该是,这个函数返回一个指针,这个指针里存储了一个handle句柄.
由于后面是直接利用了这个handle句柄,无需我们自己解包,所以,这里就以指针类型输出即可.
正确的定义应该是pointer:
这里定义好之后,那么就是写入,就像我上面说的,写入是利用了这个handle句柄,无需我们参与,所以写入就好声明了.
写入的函数原型是:
这里我们可以利用aardio的工具直接生成:
注意复制进去之后要删除掉一些识别不了的命名,比如那个hid_api_call和hid_api_export
变为
然后将这个利用工具转换为aardio的声明
生成后为
这里看到有好几个声明中的数据类型是不常见的,所以我们有需要把他们改成咱们能认识的类型
这里的hid_device&就要改为POINTER device,大写的意思就是不接受NULL空指针,后面的data是咱们要发送的数组,即数据组合,那么可以改为pointer指针类型,一般情况下数组都用指针类型声明.在看后面的size_t这个类型明显是识别不了的,这个length是要写入的长度,所以我们改为INT,大写的意思是无符号32位整型数据.长度不可能是负的,所以是INT,如果是返回值的话,因为可能会返回-1这个错误提示,所以定义为int小写,有符合数据.
admin
赞同来自:
再者就是读取各种string时候遇到的问题:
比如原型为:
利用工具直接生成的aardio调用为
然后我按照自己的理解,这个就是返回一个字符串嘛,我就定义成了下面的错误方式:
后续这样调用的时候发生了一个问题就是,返回的数据string一直都只是一个字节,就是只显示了一个
在这里一直纳闷了好久,最后无奈,只好从头按照aardio的帮助文档一个类型一个类型的对照改了下,我发现文档的api数据类型中这样写道
这里明明已经写的很明白了,wchar_t*就要转换为ustring这种类型,所以我就改为
这里再啰嗦下,ustring&的&的意思也在帮助手册中:
意思是返回.
之后调试就返回了正确的结果了.
这里我只想说明: 官方提供的帮助文档真的很重要!要多看,不要自己主观臆断!!!
admin
赞同来自:
继续:
话说我前面其实下载过很多个hid相关的dll,都调用过,基本上没有成功过,一般都是卡到了枚举这里.所以我其他功能都实现了之后才开始编写这个Hidapi_enumerate枚举端口指令.
函数原型:
这里将dll调用声明没什么可说的,返回的是struct很明显,所以我写作:
好了问题来了,我返回了一个struct的hid_device_info结构体,这个结构体是怎么定义的?
返回的这个结构体里面肯定是枚举出来的一系列数据,数据应该怎么取出来?
这个结构体的原型,文档也提供了
于是,我继续利用aardio提供的工具进行转换:结果如下
咱们看看他转换的,path原来是char*,变成了pointer,这里我也不讨论它对不对了,我直接和帮助文档进行了比对
于是我果断把它改为了string path;
继续看下面的wchar_t *serial_number; 上面我们已经说过,aardio中wchar_t要变成什么?
对,就是ustring ,所以我也改变它为:ustring serial_number;
依次类推,其他类似的都改掉.
最后看
这个是指向下一个设备的指针,他的原型是它自己的struct,这个应该怎么弄啊??
工具把它转换为了一个
但是明显是不对的,有两个类型声明.那么应该删掉哪个?
删掉struct,这里我已经说了这个是指向下个设备的指针,指针,指针....
果断改为pointer next;
好了,改后变成了
这个可是完全按照aardio帮助文档来写的!!
admin
赞同来自:
之后就是这个结构体怎么使用了.
这里也卡住了好久,因为对aar确实不熟悉.
纠结的过程就不写了.直接写怎么弄出来的.
先打开这个dll官方提供的一个示例代码hidtest.cpp 这个貌似用c++写的,我们可以参考改改
看到了三个重点标注.
一个获取设备列表,第二个把列表进行struct转换,然后判断结构体中有没数据,有数据就输出里面的数据,while最后一句把指针更改为next的指向,即指向下一个设备,然后再while进行判断是不是空数据,不是空的就输出第二个设备,然后继续......
看到这,我猜想:
ok,这样操作的结果证明猜想的正确性!
同时也学会了怎么使用next这种自己调用自己的形式.
我这里枚举出了9个设备,最后一个是我用开发板做的一个custom_hid,数据完全正确!
为了验证正确性,我也下载了一个网上的usb调试软件,得出的结果和上面的一致
好了,这里hid上位机dll调用编写测试就基本结束了.
再次感谢aardiio群里很多大神对我的帮助
admin
赞同来自:
想要实现插入usb设备界面能提示插入了这个设备功能:
参考了aar的官方示例u盘检测:然后修改下面的
说明:
框架调用这个函数以通知应用程序或设备驱动程序,设备或计算机的硬件配置发生了改变。
对于提供了软件控制功能,如弹出和锁定的设备,操作系统通常发送一条DBT_DEVICEREMOVEPENDING消息,以便使应用程序和设备驱动程序停止对设备的使用。
如果操作系统强行清除了一个设备,它可能不会发送DBT_DEVICEQUERYREMOVE消息。
nEvent参数可以是下列值之一:
注意 框架调用这个成员函数以允许你的应用程序处理一个Windows消息。传递给你的成员函数的参数反映了接收到消息时框架接收到的参数。如果你调用了这个函数的基类实现,则该实现将使用最初传递给消息的参数(而不是你提供给这个函数的参数)。
程序里为什么是5/*_DBT_DEVTYP_DEVICEINTERFACE*/ ???
看
admin
赞同来自:
线程的使用,
利用线程后台读取数据的方法:
admin
赞同来自:
多线程的使用,暂停,继续
admin
赞同来自:
hid软件新增:
Ascii码和Hex码相互转换功能,增加窗口固顶功能,添加 打开设备时候显示 设备用户设备名提示功能.
完整代码如下:
admin
赞同来自:
更新: 增加弹出菜单功能[删除]
添加下列代码即可:
admin
赞同来自:
继续更新,
新增列表框内容保存到txt功能,新增右键菜单清空数据功能:
admin
赞同来自:
wps表格和图表操作:
随手记录一个: 标准库自带的解析器
admin
赞同来自:
更新:
增加导出数据到excel表:
admin
赞同来自:
更新:
增加发送前对输入数据进行判断,1:是否是hex规定的字符,2:是否hex输入的是偶数个
admin
赞同来自:
更新:
发布为exe之后,发现问题: 打开设备然后关闭设备,再次点击打开就会提示打开出错....
经过多次修改和调试后,发现是因为死在了read读取的线程中hid_read函数地方,初步判断是read貌似在线程中模式是阻塞模式,但是我线程中添加设置为非阻塞还是同样的问题,现在不得而知,
解决办法是: read换成超时模式,read_timeout
另外增加线程的打开和关闭判断,使用了thread.set(标志)和get(标志)
另外将hid_open获取到的Hiddevice,也用thread.set(HHiddevice,Hiddevice)和get来传递,利于判断非空.
admin
赞同来自:
监控并获取Bushound中的数据导入到aar的listview中:
admin
赞同来自:
bushound数据获取:
更新: 打开bushound后,打开aar做的监控软件,自动获取窗口中数据句柄,无需手动输入了.后台线程自动更新数据
admin
赞同来自: tinyun
bushound数据采集更新: 增加虚拟桌面 使用
先前的程序使用的前提必须先手动打开bushound软件,然后打开这个采集软件,这样就出现了个问题,桌面上即要显示bushound软件也要现世采集软件,桌面不干净.
于是先前想了一种办法: 点击采集软件自动后台线程process打开bushound,然后瞬间将bushound的窗口最小化并隐藏到进程中.
但是这个导致了一个问题,打开采集软件的同时会突然发现桌面上闪现了一下bushound的窗口然后消失了..
闪现的这下怎么看都不舒服,
于是我想了另外一种办法:
通过 虚拟桌面 来在虚拟桌面里面打开bushound,然后主桌面里采集程序通过线程来采集bushound的数据.
虚拟桌面的好处是他专门开辟一个内存来存放打开的程序,在主桌面里面是看不到这个程序运行的.而且也不会有任何能使人感受不好的现象发生.
admin
赞同来自: tinyun
额,接上面 虚拟桌面 方式采集数据:
忘了记录一个功能: 虚拟桌面中
这个可以直接在新虚拟桌面中打开一个exe
mengydz.com
赞同来自:
太猛了,厉害厉害
PamPam
赞同来自:
工程哪里可以下载
天涯若比邻
赞同来自:
长春老猫
赞同来自:
京城老付
赞同来自:
dashen
flydreamsky
赞同来自:
厉害厉害
qzusertury
赞同来自:
学习了,
苍术
赞同来自:
遇到一个问题,检测设备时能显示设备插入/拔出,使用VID与PID打开设备时总是失败