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)的原理、常用的图像数据集以及图像识别的实际应用场景&…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
