QT6 调用 RUST Dll 笔记

QT6 绘制交互界面,rust 用来实现复杂逻辑,是一种兼顾开发效率、高安全性、高运行速度、低耦合的开发方式。但是,目前相关教程多基于老版本的 QT ,或是仅仅在谈 QT6 与 C\C++ 之间的动态链接库开发。经综合多处教程,加之实测运行,得到如下方案,仅记录以备忘。

一、无参数和返回值的调用方法

(一)RUST 部分

在IDE中创建rust的库项目,命名为:rust_dll_call_by_c,删除lib.rs下所有自动生成的代码,写入自己需要的函数:

//lib.rs
#[no_mangle]
pub extern fn my_print() {
    println!("This is my rust dll print.");
}

代码解释:

  1. #[no_mangle] 字面意思是不要熨平。实际作用是不要在编译时改动函数名(my_print),以便调用ddl 的其他语言代码能够按照名称找到函数。
  2. pub extern 是向外暴露函数的关键字。

在Cargo.toml文件中加入以下代码:

[lib]
name="rust_dll_call_by_c"
crate-type = ["cdylib"]

代码解释: crate-type = [“cdylib”] 是生成 dll 的关键。

这时,直接构建RUST,即可生成rust_dll_call_by_c.dll文件:
QT6 调用 RUST Dll 笔记_第1张图片

(二) QT6 部分

在qt creator中新建一个对话框项目,命名为:qt_call_rust_dll,自动生成的QT项目结构如下:
QT6 调用 RUST Dll 笔记_第2张图片
直接点击构建按钮,即可在项目路径外生成一个构建目录如下:
项目目录和构建目录
将rust_dll_call_by_c.dll文件,拷贝至build-qt_call_rust_dll-Desktop_Qt_6_2_2_MSVC2019_64bit-Debug目录中的Debug文件夹下,如图:
QT6 调用 RUST Dll 笔记_第3张图片
在头文件dialog.h中添加如下代码:

#include 

在界面设计视图拖拽一个pushButton控件,显示文字改为:调用rust编写的DLL(无参数无返回值),控件名改为:pushButton_without_arg_void,之后在控件上右键转到槽。
QT6 调用 RUST Dll 笔记_第4张图片
在该按钮的click槽方法中写入调用DLL逻辑如下:

void Dialog::on_pushButton_without_arg_void_clicked()
{
    QLibrary rust_lib("rust_dll_call_by_c");         // 定义DLL库名称

    rust_lib.load();                                 // 加载DLL库

    if (rust_lib.isLoaded()) {                       // 判断是否加载成功
        qDebug() << "加载rust库成功";
        auto rust_fn = rust_lib.resolve("my_print"); // 定位并解析DLL库中的目标函数
        rust_fn();                                   // 运行目标函数
        rust_lib.unload();                           // 用完卸载DLL
    } else {
        qDebug() << "加载rust库失败";
    }
}

代码解释: 调用DLL分五步:

  1. 初始化一个 QLibrary 实例 rust_lib,并指明 dll 名称 rust_dll_call_by_c。*(注意:此处不用加.dll后缀)
  2. 调用 load 方法 加载库,并用 isLoaded 方法判断是否成功加载,分情况处理
  3. 调用 resolve 方法,实现 my_print 函数的自动定位和解析,并返回 rust_fn 作为 QT 中可以使用的函数名
  4. 使用函数 rust_fn
  5. 调用 unload 方法卸载DLL,释放内存。

点击运行代码,点击按钮,输出如下:
QT6 调用 RUST Dll 笔记_第5张图片

有参数和返回值的DLL调用

(一)RUST 部分

在lib.rs文件中加入一下代码:

use std::os::raw::c_int;

#[no_mangle]
pub extern fn my_add(a: c_int, b: c_int)-> c_int{
    a + b
}

代码解释: 引入C底层类型:
rust编写的DLL在qt6调用时,如果存在形参和返回值,则形参和返回值的类型,既不能直接使用rust类型,也不能使用qt6的类型,必须采用一个”交集“类型——C-ABI。这种底层类型在rust的标准库中已经定义在 std::os::raw 路径下,直接引入使用即可。

重新构建RUST,生成dll文件,拷贝dll到QT的构建目录下。

(一)QT6 部分

添加一个按钮,命名为:pushButton_with_arg_return,转到槽。
QT6 调用 RUST Dll 笔记_第6张图片
在槽函数中实现调用DLL逻辑:

void Dialog::on_pushButton_with_arg_return_clicked()
{
    QLibrary rust_lib("rust_dll_call_by_c"); // 定义DLL库名称

    rust_lib.load();                         // 加载DLL库

    if (rust_lib.isLoaded()) {               // 判断是否加载成功
        qDebug() << "加载rust库成功";

        // 强制转换解析后的my_add函数类型为Fun1类型
        Fun1 rust_my_add = (Fun1)rust_lib.resolve("my_add"); // 定位并解析DLL库中的目标函数
        qDebug() << rust_my_add(1, 2);                       // 运行目标函数
        rust_lib.unload();                                   // 用完卸载DLL
    } else {
        qDebug() << "加载rust库失败";
    }
}

代码解释: 注意强制转换函数类型:
my_add 函数解析后,QT 并不知道这个函数的形参和返回值的类型,因此也无法使用这个函数。这时,就要定义这个函数在 QT 中的“签名”,然后用这个函数签名强制转换解析后的函数为自定义的 Fun1 类型。下面,我们来定义这个 Fun1 类型。

在 Dialog.h 文件中插入如下代码:

typedef int (*Fun1)(int,int);

点击运行代码,弹出对话框如下:
QT6 调用 RUST Dll 笔记_第7张图片
点击按钮 “调用rust编写的DLL(有参数有返回值)”,输入如下:
QT6 调用 RUST Dll 笔记_第8张图片


2021年12月24日

你可能感兴趣的:(rust,rust,开发语言,后端,qt)