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