当前位置: 首页 > news >正文

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&#xff0c;现在需要调用读卡器等硬件设备&#xff0c;硬件厂商提供了32位的动态链接库&#xff0c;现在记录例子&#xff0c;需要注意的点是使用libloading库和libc库&#xff0c; [package] name "yyt-device-rust" version &q…...

Leetcode—739.每日温度【中等】

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

毕业设计单片机可以用万能板吗?

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

spring boot整合Jasypt实现配置加密

文章目录 目录 文章目录 前言 一、Jasypt是什么&#xff1f; 二、使用步骤 1.引入 2.测试使用 3.结果 总结 前言 一、Jasypt是什么&#xff1f; Jasypt&#xff08;Java Simplified Encryption&#xff09;是一个Java库&#xff0c;提供了一种简单的加密解密方式&#xff0c…...

java学校高校运动会报名信息管理系统springboot+jsp

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

Java(七)(Lambda表达式,正则表达式,集合(Collection,Collection的遍历方式))

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

华为OD机试 - 二叉树计算(Java JS Python C)

目录 题目描述 输入描述 输出描述 用例 题目解析 JS算法源码 Java算法源码...

鸿蒙(HarmonyOS)应用开发——基础组件

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

Vue3的项目创建到启动

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

开关电源基础而又硬核的知识

1.什么是Power Supply? Power Supply是一种提供电力能源的设备&#xff0c;它可以将一种电力能源形式转换成另外一种电力能源形式&#xff0c;并能对其进行控制和调节。 根据转换的形式分类&#xff1a;AC/DC、DC/DC、DC/AC、AC/AC 根据转换的方法分类&#xff1a;线性电源、…...

LightDB23.4 支持转换sql中中文空格和逗号为英文空格和逗号

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

EM@常见平面曲线的方程的不同表示方式

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

element使用小结

1、tabel表头文字自定义效果&#xff08;换行&#xff0c;不同颜色&#xff09; 换行&#xff1a; // 方法一 <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&#xff08;e&#xff09;SMU卡路由到PXI&#xff08;e&#xff09;机箱上的背板触发线。该过程涉及使用NI-DCPowerVI将SMU信号导出到PXI_TRIG线上。 在继续操作之前&#xff0c;请确保在开发PC上安装了兼容版…...

[蓝桥杯习题]———位运算、判断二进制1个数

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

3DCAT为华东师大设计学院打造元宇宙数字虚拟学院

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

AIGC 3D即将爆发,混合显示成为产业数字化的生产力平台

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

时间序列预测实战(二十一)PyTorch实现TCN卷积进行时间序列预测(专为新手编写的自研架构)

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

探索计算机视觉:深度学习与图像识别的融合

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

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

【机器视觉】单目测距——运动结构恢复

ps&#xff1a;图是随便找的&#xff0c;为了凑个封面 前言 在前面对光流法进行进一步改进&#xff0c;希望将2D光流推广至3D场景流时&#xff0c;发现2D转3D过程中存在尺度歧义问题&#xff0c;需要补全摄像头拍摄图像中缺失的深度信息&#xff0c;否则解空间不收敛&#xf…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...