首页 > 其他分享 >RS调用DLL

RS调用DLL

时间:2022-09-25 14:45:47浏览次数:41  
标签:mut 调用 RS DLL callback let arg test fn

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

标签:mut,调用,RS,DLL,callback,let,arg,test,fn
From: https://www.cnblogs.com/dhcn/p/16727825.html

相关文章

  • python-the first week
    python-thefirstweek目录python-thefirstweektypora软件安装文件路径markdown语法标题小标题语言环境表格表情查看源代码图片展示水平线目录链接下划线使用删除线计算......
  • 一个用go语言写的简单HttpServer,供大家调用。
    GET请求(postman):  返回: 其实:count的值,每次调用一次,增加1(线程安全的)。问题:这里的线程安全,你知道是什么意思吗?===============Post请求:  返回:  附:GO语......
  • winform程序如何调用webapi接口?附源码
    我们开发winform程序时,通常使用传统的三层结构,包括:数据层、业务层、页面层。Form页面调用时,直接new个Service类,实现业务需求。但这种模式有个弊端,就是业务逻辑全部放到了客......
  • 【微服务】- 服务调用 - OpenFeign
    服务调用-OpenFeign......
  • 组合测试术语:Pairwise/All-Pairs、OATS(Orthogonal Array Testing Strategy)
    组合测试组合测试(CombinatorialTest)是一种黑盒测试用例生成方法,主要针对多输入参数组合场景。目前业界较流行的两种组合测试方法,一种是Pairwise/All-Pairs,即配对组合。OA......
  • c#调用易语言写的dll出现闪退的情况
    调用方法就不再说了,最近调用一个朋友给的易语言的dll出现了奇怪的情况,第一次调用某个接口正常,第二次就闪退,花了很长时间才解决.按:调用的返回值的时候不能直接调用,......
  • #Lua:Lua调用C++生成的DLL库
    Lua调用C++生成的DLL库本文参考了某大佬的博客,写得十分详细,推荐!!!需求:在之前的求解器中添加了Lua库,使得程序可以在Lua脚本中实现自定义函数功能,考虑到未来可能需要与......
  • iptables中limit 和 limit-burst 说明
    Limitmatch    这个匹配操作必须由-mlimit明确指定才能使用。有了他的帮助,就能对指定的规则的日志数量加以限制,以免你被信息的洪流淹没哦。比如,你能事先设定一个限定......
  • 各编程语言 + aardio 相互调用示例
    代码都很简单,复制可直接运行。aardio快速调用C,C++,C#,Java,R,V,Python,JavaScript,Node.js,Rust,PHP,Ruby,PowerShell,Fortran,Delphi,Julia,Nim,批处理……演示。......
  • 海康摄像机使用GB28181接入SRS服务器的搭建步骤---封装docker镜像运行容器的方式,本篇
    使用第三方提供的镜像说明:这个是我在网上找的,没有实际验证及使用过,具体效果未知,看下文,我采用的是自己依据官方基础镜像进一步二次封装的镜像地址:https://hub.docker.com/......