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

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
    };
}


http://www.kler.cn/a/148373.html

相关文章:

  • 计算机网络 (1)互联网的组成
  • 前端系统设计面试题(二)Javascript\Vue
  • 速盾:高防 CDN 和 CDN 的缓存机制都一样吗?
  • 解决Anaconda出现CondaHTTPError: HTTP 000 CONNECTION FAILED for url
  • CentOS 服务
  • 图像处理实验二(Image Understanding and Basic Processing)
  • ubuntu22.04 arrch64版在线安装java环境
  • C语言-指针讲解(3)
  • 用通俗的方式讲解Transformer:从Word2Vec、Seq2Seq逐步理解到GPT、BERT
  • 人机交互3——多主题多轮对话
  • TOD和PPS精确时间同步技术
  • C#面向对象
  • 2023网络安全产业图谱
  • 02-Java集合之双列集合,如HashMap,Hashtable,Properties,TreeMap的底层结构
  • 人工智能技术发展漫谈
  • 【Linux】信号
  • 《2023全球隐私计算报告》正式发布!
  • C语言错误处理之“非局部跳转<setjmp.h>头文件”
  • python 爬虫之 爬取网站信息并保存到文件
  • C++初阶--String类的使用
  • TCP 传输可靠性问题
  • DMX512协议及对接口电路的分析
  • openssl版本号解析
  • HTML新手入门笔记整理:HTML基本标签
  • 【DevOps】SonarQube 指标解读
  • Vue3 实现elementPlus的table列宽调整和拖拽