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编码的u8
        println!("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 104
        let sv = &arg.c[0..strlen(arg.c.as_ptr() as *const i8)]; // 通过strlen构造对应字符串的数组
        let s = String::from_utf8_unchecked(sv.to_vec()); // 字符串使用utf8编码的u8
        println!("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
    };
}

使用 TauriRust 结合实现接入 AI 是一种可行的方式。以下是一种简单的实现思路: 1. 选择 AI : 首先,选择一个适合你的需求的 AI 。在 Rust ,有许多成熟的 AI 可供选择,例如 TensorFlow、PyTorch、ONNX Runtime 等。根据你的具体需求,选择一个合适的来进行 AI 模型的加载和推理。 2. 在 Rust 加载和使用 AI 模型: 使用选定的 AI ,在 Rust 加载和使用 AI 模型。这通常涉及到模型的加载、预处理输入数据、调用 AI 模型进行推理,以及处理输出结果等步骤。根据你选择的 AI 和具体的 AI 模型,可以参考相应的文档和示例代码来实现这些功能。 3. 在 Tauri 创建自定义 API: 在 TauriRust 代码创建自定义 API,用于将 AI 功能封装为 Tauri 的 API。这样,你就可以在 JavaScript/TypeScript 通过 Tauri API 调用 AI 功能。 4. 在前端代码调用 Tauri API: 在你的前端代码(如 Angular)使用 Tauri 提供的方法来调用自定义的 AI API。通过调用 Tauri API,你可以将输入数据发送到 Rust 代码进行 AI 推理,并将结果返回给前端进行展示或后续处理。 需要注意的是,具体的实现方式会因为选择的 AI 和具体的 AI 模型而有所不同。你可以参考 Tauri 和选定 AI 的文档、示例代码以及相关社区资源来实现 AI 功能的接入。另外,确保在 Rust 使用 AI 时,根据需要处理好内存管理、并发性能等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Alaia.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值