tauri中使用rust调用动态链接库例子(使用libloading库和libc库)
前言
当前采用桌面端框架位tauri,现在需要调用读卡器等硬件设备,硬件厂商提供了32位的动态链接库,现在记录例子,需要注意的点是使用libloading库和libc库,
[package]
name = "yyt-device-rust"
version = "0.0.1"
description = "yyt-device-rust"
authors = ["Alaia"]
license = ""
repository = ""
edition = "2021"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[build-dependencies]
tauri-build = { version = "1.2", features = [] }[dependencies]
tauri = { version = "1.2", features = [ "api-all"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
mac_address="*"
//重点依赖
libloading = "0.8"
encoding = "0.2.33"
libc = "0.2"[features]
# this feature is used for production builds or when `devPath` points to the filesystem
# DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"]
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]use std::ffi::CString;
use libc::*;
use libloading::{Library, Symbol};
use encoding_rs::*;type DcInit = extern "stdcall" fn(port: c_int, baud: c_int) -> *mut c_int;
type DcFindSpeed = extern "stdcall" fn(icdev: *mut c_int) -> *mut c_int;
type DcCardInfo = extern "stdcall" fn(icdev: *mut c_int,typeNum: c_int,text_len: &c_int,text: *mut c_char,photo_len: &c_int,photo: *mut c_char,fingerprint_len: &c_int, fingerprint: *mut c_char,extra_len: &c_int,extra: *mut c_char,
) -> *mut c_int;
type DcParseTextInfo = extern "stdcall" fn(icdev: *mut c_int,typeNum: c_int,text_len: &c_int,text: *mut c_char,name: *mut c_char,sex: *mut c_char,nation: *mut c_char,birth_day: *mut c_char,address: *mut c_char,id_number: *mut c_char,department: *mut c_char,expire_start_day: *mut c_char,expire_end_day: *mut c_char,reserved: *mut c_char,
) -> *mut c_int;
type DcExit = extern "stdcall" fn(icdev: *mut c_int,
) -> *mut c_int;type PrintInput = extern "stdcall" fn(InInfo: *mut c_char,OutInfo: *mut c_char,
) -> *mut c_void;#[tauri::command]
fn get_mac_addr() -> Result<String, String> {let mac_result = mac_address::get_mac_address();if let Ok(Some(mac)) = mac_result {Ok(mac.to_string().into())} else {println!("Get Address Error");Err("Rust Get Address Error".into())}
}#[tauri::command]
fn print_tickertape(input: String) -> Result<String, String> {let res: String = call_dynamic(input).unwrap();println!("Hello, world! {:?}", res);return Ok(res);
}#[tauri::command]
fn read_id_card() -> Result<String, String> {let res: String = call_dynamic_card().unwrap();println!("Hello, world! {:?}", res);return Ok(res);
}fn call_dynamic_card() -> Result<String, Box<dyn std::error::Error>> {unsafe {let lib: libloading::Library = libloading::Library::new("lib/dekabig/idCard/dcrf32.dll")?;let dc_init: libloading::Symbol<DcInit> = lib.get(b"dc_init")?;let dc_find_i_d_speed: libloading::Symbol<DcFindSpeed> = lib.get(b"dc_find_i_d_speed")?;let dc_sam_aread_card_info: libloading::Symbol<DcCardInfo> = lib.get(b"dc_SamAReadCardInfo")?;let dc_parse_text_info: libloading::Symbol<DcParseTextInfo> = lib.get(b"dc_ParseTextInfo")?;let dc_exit: libloading::Symbol<DcExit> = lib.get(b"dc_exit")?;let device_no: *mut c_int = dc_init(100, 115200);let dc_find_i_d_speed = dc_find_i_d_speed(device_no);println!("{:?}", device_no);println!("{:?}", dc_find_i_d_speed);let mut text_len = 256i32;let mut photo_len = 1024i32;let mut fingerprint_len = 1024i32;let mut extra_len = 70i32;let mut into_text = [0u8; 256];let mut into_photo = [0u8; 1024];let mut into_fingerprint = [0u8; 1024];let mut into_extra = [0u8; 70];let dc_sam_aread_card_info = dc_sam_aread_card_info(device_no,3,&text_len,into_text.as_mut_ptr() as *mut i8,&photo_len,into_photo.as_mut_ptr() as *mut i8,&fingerprint_len,into_fingerprint.as_mut_ptr() as *mut i8,&extra_len,into_extra.as_mut_ptr() as *mut i8,);println!("{:?}", dc_sam_aread_card_info);let mut name = [0u8; 64];let mut sex = [0u8; 8];let mut nation = [0u8; 12];let mut birth_day = [0u8; 36];let mut address = [0u8; 144];let mut id_number = [0u8; 76];let mut department = [0u8; 64];let mut expire_start_day = [0u8; 36];let mut expire_end_day = [0u8; 36];let mut reserved = [0u8; 76];let dc_parse_text_info = dc_parse_text_info(device_no,0,&text_len,into_text.as_mut_ptr() as *mut i8,name.as_mut_ptr() as *mut i8,sex.as_mut_ptr() as *mut i8,nation.as_mut_ptr() as *mut i8,birth_day.as_mut_ptr() as *mut i8,address.as_mut_ptr() as *mut i8,id_number.as_mut_ptr() as *mut i8,department.as_mut_ptr() as *mut i8,expire_start_day.as_mut_ptr() as *mut i8,expire_end_day.as_mut_ptr() as *mut i8,reserved.as_mut_ptr() as *mut i8,);let nameC = &name[0..strlen(name.as_ptr() as *const i8)];let (nameutf, _, _) = GBK.decode(nameC);println!("nameutf: {}", nameutf);let dc_exit = dc_exit(device_no);println!("dc_exit: {:?}", dc_exit);return Ok("123".into());}
}fn call_dynamic(json: String) -> Result<String, Box<dyn std::error::Error>> {unsafe {let lib: libloading::Library = libloading::Library::new("lib/dekabig/tickertape/DC_Print.dll")?;let print_t: libloading::Symbol<PrintInput> = lib.get(b"Print_Input")?;let (json_out, _, _) = GBK.encode(&json);let cstring = CString::new(json_out).expect("cstring error");let mut output = [0u8; 1024];print_t(cstring.into_raw(), output.as_mut_ptr() as *mut i8,);let output_c: &[u8] = &output[0..strlen(output.as_ptr() as *const i8)];let (outputf, _, _)= GBK.decode(output_c);println!("nameutf: {}", outputf);return Ok(outputf.to_string());}
}fn main() {tauri::Builder::default().invoke_handler(tauri::generate_handler![get_mac_addr,print_tickertape,read_id_card]).run(tauri::generate_context!()).expect("error while running tauri application");
}
其他注意项:首先,对于只需要传值的字符串,很好解决,&str/ String都可以简单地传递就能使用。
对于需要提前分配的char*/char[],简单办法就是使用固定长度数组作为参数。
获取返回值如果存的是字符串,使用strlen得到修改后的真正长度,然后构建vec,然后通过vec构建String。
如果是函数返回值char*则,简单方法使用CStr加载;也可以类似参数那样,使用strlen探测长度,然后构建vec然后转到String。
在libc中,c_char/c_int/c_float…都是i8/i32…的别名,所以,一般使用rust固有类型可能会更好理解。
回调函数也可以作为普通的指针传递就OK了。
参考例子
//参考例子extern crate libc;
extern crate libloading;use libc::*;
use libloading::{Library, Symbol};/*
// test_c.dll 接口内容SDK_API void test_normal(int a, float b, const char* c){printf("a: %d, b: %.2f, c: %s\n", a, b, c);
}SDK_API void test_p(int* a, float* b, char* c){printf("set *a=112233, *b=3.1415926, c='1234567890'\n");*a = 112233;*b = 3.1415926f;strcpy(c, "1234567890");
}typedef struct _TEST_OBJ
{int a;float* b;char c[256];
}TEST_OBJ;SDK_API void test_t(TEST_OBJ* arg){arg->a = 222;arg->b = NULL;strcpy(arg->c, u8"hello, world! 你好!~");
}
typedef int(*CB_FUN)(int a);SDK_API int test_cb(CB_FUN p){printf("cb: %X, call p(10)\n", p);int a = p(10);return a;
}
*/type fn_test_normal = unsafe fn(c_int, c_float, &str);
type fn_test_p = unsafe fn(&c_int, &c_float, *mut c_char);#[repr(C)]
#[derive(Clone, Copy)]
struct fn_struct_t {a: c_int,b: *mut c_float,c: [u8; 256],
}type fn_test_t = unsafe fn(&mut fn_struct_t);type fn_test_cb = unsafe fn( fn(i32)->i32 ) -> i32;fn main() {let lib = Library::new("test_c.dll").unwrap();unsafe {let fun1: Symbol<fn_test_normal> = lib.get(b"test_normal").unwrap();fun1(1, 2.21, "Hello,world\0"); // rust 字符串没有结尾的\0,};println!();unsafe {let mut arg1 = 0i32;let mut arg2 = 0f32;let mut arg3 = [0u8; 256]; // 分配存储空间let fun2: Symbol<fn_test_p> = lib.get(b"test_p").unwrap();fun2(&arg1, &arg2, arg3.as_mut_ptr() as *mut i8); // set *a=112233, *b=3.1415926, c='1234567890'println!("{} {} {:?}", arg1, arg2, arg3[0]); // 112233 3.1415925 49
// let s = String::from_raw_parts(arg3.as_mut_ptr() as *mut u8, strlen(arg3.as_ptr()), arg3.len());
// println!("ret: {}", s); // ret: 1234567890, 有buf, 会导致下面的无输出,故使用3的方式构建字符串let sv = &arg3[0..strlen(arg3.as_ptr() as *const i8)]; // 通过strlen构造对应字符串的数组let s = String::from_utf8_unchecked(sv.to_vec()); // 字符串使用utf8编码的u8println!("ret: {}", s); // ret: 1234567890};println!();unsafe {let mut arg = fn_struct_t { a: 0, b: 0 as *mut f32, c: [0u8; 256] };let fun3: Symbol<fn_test_t> = lib.get(b"test_t").unwrap();fun3(&mut arg);println!("{} {:?} {:?}", arg.a, arg.b, arg.c[0]); // 222 0x0 104let sv = &arg.c[0..strlen(arg.c.as_ptr() as *const i8)]; // 通过strlen构造对应字符串的数组let s = String::from_utf8_unchecked(sv.to_vec()); // 字符串使用utf8编码的u8println!("ret: {}", s); // ret: hello, world! 你好!~};println!();unsafe {let arg = |arg:i32| {println!("arg cb called! arg is {}", arg); arg*10+123};let fun4: Symbol<fn_test_cb> = lib.get(b"test_cb").unwrap();let r = fun4(arg);println!("ret: {}", r); // 223};
}
相关文章:

tauri中使用rust调用动态链接库例子(使用libloading库和libc库)
前言 当前采用桌面端框架位tauri,现在需要调用读卡器等硬件设备,硬件厂商提供了32位的动态链接库,现在记录例子,需要注意的点是使用libloading库和libc库, [package] name "yyt-device-rust" version &q…...

Leetcode—739.每日温度【中等】
2023每日刷题(四十二) Leetcode—739.每日温度 单调栈实现思想 从右到左实现代码 class Solution { public:vector<int> dailyTemperatures(vector<int>& temperatures) {int n temperatures.size();stack<int> st;vector<i…...

毕业设计单片机可以用万能板吗?
毕业设计单片机可以用万能板吗? 可以是可以,就是焊接起来比较麻烦,特别是有好几个重复连线点的时候,检测起来就不那么容易了,而且布线看起来乱糟糟的,如果后期一不小心把线弄断了,查起来就更麻烦了&#x…...

spring boot整合Jasypt实现配置加密
文章目录 目录 文章目录 前言 一、Jasypt是什么? 二、使用步骤 1.引入 2.测试使用 3.结果 总结 前言 一、Jasypt是什么? Jasypt(Java Simplified Encryption)是一个Java库,提供了一种简单的加密解密方式,…...

java学校高校运动会报名信息管理系统springboot+jsp
课题研究方案: 结合用户的使用需求,本系统采用运用较为广泛的Java语言,springboot框架,HTML语言等关键技术,并在idea开发平台上设计与研发创业学院运动会管理系统。同时,使用MySQL数据库,设计实…...

Java(七)(Lambda表达式,正则表达式,集合(Collection,Collection的遍历方式))
目录 Lambda表达式 省略写法(要看懂) 正则表达式 语法 案例 正则表达式的搜索替换和分割内容 集合进阶 集合体系结构 Collection Collection的遍历方式 迭代器 增强for循环 Lambda表达式遍历Collection List集合 ArrayList LinkedList 哈希值 HashSet底层原理 …...

华为OD机试 - 二叉树计算(Java JS Python C)
目录 题目描述 输入描述 输出描述 用例 题目解析 JS算法源码 Java算法源码...

鸿蒙(HarmonyOS)应用开发——基础组件
组件 组件化是一种将复杂的前端应用程序分解成小的、独立的部分的方法。这些部分被称为组件,它们可以重复使用,可以与其他组件组合使用以创建更复杂的组件,并且它们有自己的生命周期和状态。 组件化的目的是提高开发效率和代码重用率&#…...

Vue3的项目创建到启动
Vue3的项目创建 检查node版本创建 npm init vuelatest 安装依赖 项目启动 启动成功...

开关电源基础而又硬核的知识
1.什么是Power Supply? Power Supply是一种提供电力能源的设备,它可以将一种电力能源形式转换成另外一种电力能源形式,并能对其进行控制和调节。 根据转换的形式分类:AC/DC、DC/DC、DC/AC、AC/AC 根据转换的方法分类:线性电源、…...

LightDB23.4 支持转换sql中中文空格和逗号为英文空格和逗号
功能介绍 在Lightdb数据库兼容Oracle的语法时,发现Oracle支持sql语句中使用中文空格和中文逗号,为了方便用户迁移到Lightdb,在Lightdb23.4版本中支持了转换中文空格和逗号的功能。该功能由GUC参数lightdb_convert_chinese_char来控制开关&am…...

EM@常见平面曲线的方程的不同表示方式
文章目录 abstract常见曲线的不同形式小结:一览表分析圆锥曲线的极坐标方程非标准位置的圆锥曲线参数方程应用比较 refs abstract 常见平面曲线的方程的不同表示方式 常见曲线的不同形式 下面以平面曲线为对象讨论参数方程通常是对普通方程的补充和增强,曲线的普通方程(直角…...

element使用小结
1、tabel表头文字自定义效果(换行,不同颜色) 换行: // 方法一 <el-table-columnprop"otherCost":label"本期累计\n(元)"> // 通过:label添加\n </el-table-column>.xx .cell {white-space: pre-…...

自动驾驶DCLC 功能规范
目录 1 概述Summary....................................................................................................... 4 1.1 目的Purpose....................................................................................................... 4 1.2 范围Ran…...

LabVIEW中将SMU信号连接到PXI背板触发线
LabVIEW中将SMU信号连接到PXI背板触发线 本文介绍如何将信号从PXI(e)SMU卡路由到PXI(e)机箱上的背板触发线。该过程涉及使用NI-DCPowerVI将SMU信号导出到PXI_TRIG线上。 在继续操作之前,请确保在开发PC上安装了兼容版…...

[蓝桥杯习题]———位运算、判断二进制1个数
⭐Hello!这里是欧_aita的博客。 ⭐今日语录:行动胜过一切。 ⭐个人主页:欧_aita ψ(._. )>⭐个人专栏: 数据结构与算法(内含蓝桥杯习题) MySQL数据库 位运算 位运算位运算的定义简单运用 实战刷题题目思路代码实现声…...

3DCAT为华东师大设计学院打造元宇宙数字虚拟学院
6月11日,华东师范大学设计学院在chi K11美术馆举办了一场别开生面的 2023 年本科毕业设计暨项目实践教学现场演示展。其中,元宇宙数字虚拟学院(一期)的现场发布会引起了现场震撼,吸引了众多观众的目光和参与。 该元宇宙…...

AIGC 3D即将爆发,混合显示成为产业数字化的生产力平台
2023年,大语言模型与生成式AI浪潮席卷全球,以文字和2D图像生成为代表的AIGC正在全面刷新产业数字化。而容易为市场所忽略的是,3D图像生成正在成为下一个AIGC风口,AIGC 3D宇宙即将爆发。所谓AIGC 3D宇宙,即由文本生成3D…...

时间序列预测实战(二十一)PyTorch实现TCN卷积进行时间序列预测(专为新手编写的自研架构)
一、本文介绍 本篇文章给大家带来的是利用我个人编写的架构进行TCN时间序列卷积进行时间序列建模(专门为了时间序列领域新人编写的架构,简单不同于市面上大家用GPT写的代码),包括结果可视化、支持单元预测、多元预测、模型拟合效…...

探索计算机视觉:深度学习与图像识别的融合
探索计算机视觉:深度学习与图像识别的融合 摘 要: 本文将探讨计算机视觉领域中的深度学习技术,并重点关注图像识别方面的应用。我们将介绍卷积神经网络(CNN)的原理、常用的图像数据集以及图像识别的实际应用场景&…...

屏蔽WordPress评论中长URL地址方法
由于WordPress是比较常见的CMS程序之一,所以很多网络营销推广也会基于WP去群发外链和广告信息。这里,我们可以通过屏蔽特定关键字、屏蔽特定字符的方式,或者是屏蔽评论内容的长短来限制评论。还有一个我们可以通过评论内容的URL地址的长度来屏…...

【教程】 一文部署配置并入门 Redis
综述 什么是Redis Redis官网——Redis.io Redis, 作为一个高性能的键值对数据库,主要应用于以下场景: 缓存系统:由于其高速读写能力,Redis 非常适合用作缓存系统,减少数据库负载。 会话存储(Session St…...

数据被锁住了?如何应对.mkp病毒的攻击
导言: 在数字时代的舞台上,.mkp勒索病毒如幽灵般悄然崭露头角,威胁着无数个体和组织的数据安全。本文将深度挖掘.mkp勒索病毒的狡猾本质,并为你揭示应对感染的独特方法,以及如何巧妙规避这个数字威胁。 如果您在面对被…...

【Shell】Shell基础学习
一、shell脚本 (1)第一个shell脚本 #!/bin/bash #this is a comment echo "hello world"一个shell脚本永远以“#!”开头,这是一个脚本开始的标记,它是告诉系统执行这个文件需要用某个解释器,后面的/bin/bash就是指明解释器的具体位置。 “#”开头是注释 …...

python文件读取
相对路径 读文件 打印txt文件 fopen(".\data.txt","r",encoding"utf-8") contentf.read() print(content) f.close()with open(".\data.txt","r",encoding"utf-8") as f:contentf.read()print(content)contentf…...

第16关 革新云计算:如何利用弹性容器与托管K8S实现极速服务POD扩缩容
------> 课程视频同步分享在今日头条和B站 天下武功,唯快不破! 大家好,我是博哥爱运维。这节课给大家讲下云平台的弹性容器实例怎么结合其托管K8S,使用混合服务架构,带来极致扩缩容快感。 下面是全球主流云平台弹…...

算法通关村第十二关|黄金挑战|最长公共前缀字符串压缩
1.最长公共前缀 原题:力扣14. 1.从前到后比较每个字符串的同一个位置。 public String longestCommonPrefix(String[] strs) {if (strs null || strs.length 0) {return "";}int length strs[0].length();int count strs.length;for (int i 0; i …...

池式组件 ----- Mysql连接池的原理实现
前言 本文是mysql连接池的实现。学完mysql连接池之后,接下来会结合多线程来进行测试,看看使用连接池性能高,还是不要连接池性能高,具体能差多少。当然这是下一篇文章了哈哈哈哈哈。当前首要任务是学会连接池,会都不会…...

1.自动化运维工具Ansible的安装
1.物料准备 四台服务器,其中一个是主控机,三个为host 2.安装 在主控机上安装ansible 2.1 设置EPEL仓库 Ansible仓库默认不在yum仓库中,因此我们需要使用下面的命令启用epel仓库。 yum install epel-release -y2.2 执行安装命令 yum i…...

[个人笔记] Apache2.4配置TLS1.3安装openssl1.1.1
Linux - 运维篇 第二章 Apache2.4配置TLS1.3&安装openssl1.1.1 Linux - 运维篇系列文章回顾Apache2.4配置TLS1.3&安装openssl1.1.1参考来源 系列文章回顾 第一章 php-fpm编译和使用openssl扩展 Apache2.4配置TLS1.3&安装openssl1.1.1 [rootlocalhost ~]# yum ins…...