当前位置: 首页 > 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;的原理、常用的图像数据集以及图像识别的实际应用场景&…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

Oracle11g安装包

Oracle 11g安装包 适用于windows系统&#xff0c;64位 下载路径 oracle 11g 安装包...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文通过代码驱动的方式&#xff0c;系统讲解PyTorch核心概念和实战技巧&#xff0c;涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...