Loading

RS调用DLL

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

posted @ 2022-09-25 14:36  _朝晖  阅读(78)  评论(0编辑  收藏  举报