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.");
}
代码解释:
- #[no_mangle] 字面意思是不要熨平。实际作用是不要在编译时改动函数名(my_print),以便调用ddl 的其他语言代码能够按照名称找到函数。
- 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 部分
在qt creator中新建一个对话框项目,命名为:qt_call_rust_dll,自动生成的QT项目结构如下:
直接点击构建按钮,即可在项目路径外生成一个构建目录如下:
将rust_dll_call_by_c.dll文件,拷贝至build-qt_call_rust_dll-Desktop_Qt_6_2_2_MSVC2019_64bit-Debug目录中的Debug文件夹下,如图:
在头文件dialog.h中添加如下代码:
#include <QLibrary>
在界面设计视图拖拽一个pushButton控件,显示文字改为:调用rust编写的DLL(无参数无返回值),控件名改为:pushButton_without_arg_void,之后在控件上右键转到槽。
在该按钮的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分五步:
- 初始化一个 QLibrary 实例 rust_lib,并指明 dll 名称 rust_dll_call_by_c。*(注意:此处不用加.dll后缀)
- 调用 load 方法 加载库,并用 isLoaded 方法判断是否成功加载,分情况处理
- 调用 resolve 方法,实现 my_print 函数的自动定位和解析,并返回 rust_fn 作为 QT 中可以使用的函数名
- 使用函数 rust_fn
- 调用 unload 方法卸载DLL,释放内存。
点击运行代码,点击按钮,输出如下:
有参数和返回值的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,转到槽。
在槽函数中实现调用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);
点击运行代码,弹出对话框如下:
点击按钮 “调用rust编写的DLL(有参数有返回值)”,输入如下:
2021年12月24日