tauri中使用rust调用动态链接库例子(使用libloading库和libc库)

2023-11-30 21:45

本文主要是介绍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库)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/438682

相关文章

Java使用Thumbnailator库实现图片处理与压缩功能

《Java使用Thumbnailator库实现图片处理与压缩功能》Thumbnailator是高性能Java图像处理库,支持缩放、旋转、水印添加、裁剪及格式转换,提供易用API和性能优化,适合Web应... 目录1. 图片处理库Thumbnailator介绍2. 基本和指定大小图片缩放功能2.1 图片缩放的

Python使用Tenacity一行代码实现自动重试详解

《Python使用Tenacity一行代码实现自动重试详解》tenacity是一个专为Python设计的通用重试库,它的核心理念就是用简单、清晰的方式,为任何可能失败的操作添加重试能力,下面我们就来看... 目录一切始于一个简单的 API 调用Tenacity 入门:一行代码实现优雅重试精细控制:让重试按我

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

使用Python构建智能BAT文件生成器的完美解决方案

《使用Python构建智能BAT文件生成器的完美解决方案》这篇文章主要为大家详细介绍了如何使用wxPython构建一个智能的BAT文件生成器,它不仅能够为Python脚本生成启动脚本,还提供了完整的文... 目录引言运行效果图项目背景与需求分析核心需求技术选型核心功能实现1. 数据库设计2. 界面布局设计3

使用IDEA部署Docker应用指南分享

《使用IDEA部署Docker应用指南分享》本文介绍了使用IDEA部署Docker应用的四步流程:创建Dockerfile、配置IDEADocker连接、设置运行调试环境、构建运行镜像,并强调需准备本... 目录一、创建 dockerfile 配置文件二、配置 IDEA 的 Docker 连接三、配置 Do

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

python使用try函数详解

《python使用try函数详解》Pythontry语句用于异常处理,支持捕获特定/多种异常、else/final子句确保资源释放,结合with语句自动清理,可自定义异常及嵌套结构,灵活应对错误场景... 目录try 函数的基本语法捕获特定异常捕获多个异常使用 else 子句使用 finally 子句捕获所

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

C#中lock关键字的使用小结

《C#中lock关键字的使用小结》在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块,下面就来介绍一下lock关键字的使用... 目录使用方式工作原理注意事项示例代码为什么不能lock值类型在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时