https://blog.csdn.net/bbdxf/article/details/87890141
Rust调用DLL
简单调用(动态)
extern crate libloading;
use std::env;
use libloading::{Library, Symbol};
type AddFunc = fn(isize, isize) -> isize;
fn main() {
let library_path = env::args().nth(1).expect("USAGE: loading <LIB>");
println!("Loading add() from {}", library_path);
let lib = Library::new(library_path).unwrap();
unsafe {
let func: Symbol<AddFunc> = lib.get(b"add").unwrap();
let answer = func(1, 2);
println!("1 + 2 = {}", answer);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
高级Demo(动态)
这个Demo解决了,c_char, c_char*, 数组,可变数组,String之间的类型转换,回调函数等。包含结构体的使用以及如何传递指针。
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
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
首先,对于只需要传值的字符串,很好解决,&str/ String都可以简单地传递就能使用。
对于需要提前分配的char*/char[],简单办法就是使用固定长度数组作为参数。如果存的是字符串,使用strlen得到修改后的真正长度,然后构建vec,然后通过vec构建String。
如果是函数返回值char*则,简单方法使用CStr加载;也可以类似参数那样,使用strlen探测长度,然后构建vec然后转到String。
在libc中,c_char/c_int/c_float...都是i8/i32...的别名,所以,一般使用rust固有类型可能会更好理解。
回调函数也可以作为普通的指针传递就OK了。
静态链接Lib
https://doc.rust-lang.org/nomicon/ffi.html
块link上的属性extern提供了基本的构建块,用于指示rustc如何链接到本机库。今天有两种可接受的链接属性形式:
#[link(name = "foo")]
#[link(name = "foo", kind = "bar")]
在这两种情况下,foo是我们要链接到的本机库的名称,在第二种情况下bar是编译器链接到的本机库的类型。目前有三种已知类型的本机库:
动态 - #[link(name = "readline")]
静态的 - #[link(name = "my_build_dependency", kind = "static")]
构架 - #[link(name = "CoreFoundation", kind = "framework")] 请注意,框架仅适用于macOS目标。
不同的kind值旨在区分本机库如何参与链接。从链接角度来看,Rust编译器会创建两种工件:partial(rlib / staticlib)和final(dylib / binary)。本机动态库和框架依赖关系传播到最终工件边界,而静态库依赖关系根本不传播,因为静态库直接集成到后续工件中。
有关如何使用此模型的一些示例如下:
本机构建依赖项。在编写一些Rust代码时,有时需要一些C / C ++粘合剂,但是以库格式分发C / C ++代码是一种负担。在这种情况下,代码将被存档libfoo.a,然后Rust crate将通过声明依赖#[link(name = "foo", kind = "static")]。
无论包的输出风格如何,本机静态库都将包含在输出中,这意味着不需要分发本机静态库。
正常的动态依赖。常见系统库(如readline)可在大量系统上使用,并且通常无法找到这些库的静态副本。当此依赖项包含在Rust crate中时,部分目标(如rlibs)将不会链接到库,但是当rlib包含在最终目标(如二进制文件)中时,将链接本机库。
在macOS上,框架的行为与动态库的语义相同。
extern crate libc;
use libc::size_t;
#[link(name = "snappy")]
extern {
fn snappy_max_compressed_length(source_length: size_t) -> size_t;
}
fn main() {
let x = unsafe { snappy_max_compressed_length(100) };
println!("max compressed length of a 100 byte buffer: {}", x);
}
1
2
3
4
5
6
7
8
9
10
11
12
回调函数:
// callback
#[repr(C)]
struct RustObject {
a: i32,
// Other members...
}
extern "C" fn callback(target: *mut RustObject, a: i32) {
println!("I'm called from C with value {0}", a);
unsafe {
// Update the value in RustObject with the value received from the callback:
(*target).a = a;
}
}
#[link(name = "extlib")]
extern {
fn register_callback(target: *mut RustObject, cb: extern fn(*mut RustObject, i32)) -> i32;
fn trigger_callback();
}
fn main() {
// Create the object that will be referenced in the callback:
let mut rust_object = Box::new(RustObject { a: 5 });
unsafe {
register_callback(&mut *rust_object, callback);
trigger_callback();
}
}
// c dll/lib
typedef void (*rust_callback)(void*, int32_t);
void* cb_target;
rust_callback cb;
int32_t register_callback(void* callback_target, rust_callback callback) {
cb_target = callback_target;
cb = callback;
return 1;
}
void trigger_callback() {
cb(cb_target, 7); // Will call callback(&rustObject, 7) in Rust.
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
访问修改全局变量
extern crate libc;
use std::ffi::CString;
use std::ptr;
#[link(name = "readline")]
extern {
static mut rl_prompt: *const libc::c_char;
}
fn main() {
let prompt = CString::new("[my-awesome-shell] $").unwrap();
unsafe {
rl_prompt = prompt.as_ptr();
println!("{:?}", rl_prompt);
rl_prompt = ptr::null();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
一个Demo
// libdemo1.dylib
#include <stdio.h>
#define EXPORT __attribute__((visibility("default")))
#ifdef __cplusplus
extern "C" {
#endif
EXPORT
int test_normal(int a, float b, char *c) {
printf("a: %d, b: %.2f c: %s\n", a, b, c);
return 11;
}
#ifdef __cplusplus
}
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
rust调用:
首先要export LIBRARY_PATH=/Users/{user}/cprj/demo1/cmake-build-release将lib库加入LIBRARY_PATH中,这样rust能找到lib文件。然后如下方式调用:
extern crate libc;
#[link(name = "demo1")]
extern {
fn test_normal(a: i32, b: f32, c: *const u8) -> i32;
}
fn main() {
let x = unsafe { test_normal(100, 3.2, "213213\0".as_ptr()) };
println!("test: {}", x);
}
1
2
3
4
5
6
7
8
9
10
11
————————————————
版权声明:本文为CSDN博主「笨笨D幸福」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/bbdxf/article/details/87890141