Qt调用Matlab生成dll的配置过程与问题排查解决记录

前置

  1. 编译器 msvc_2019_x64
  2. Qt 5.15.2
  3. Matlab R2022b

1. Matlab生成dll

确保你的.m文件是一个funciton,并且有返回值,长得像这样
Matlab function示例

然后在Matlab控制台输入命令:

1
mex -setup

如图:
mex -setup

选择你的编译器,我这里选择的是Microsoft Visual C++ 2019 (C),总之前后要对应起来。

接下来选择:

1
mex -setup C++ 

看到输入如下字样后:

1
MEX 配置为使用 'Microsoft Visual C++ 2019' 以进行 C++ 语言编译。

接着输入:

1
mcc -W cpplib:detect -T link:lib detect.m -C
  • -W 控制编译之后的封装格式
  • cpplib 指编译成C++的lib文件;若要编译成C的lib文件,就用需要将cpplib改为lib
  • detect 指生成文件的文件名
  • -T 表示目标
  • link:lib 表示要连接到一个库文件的目标,目标的名字即是.m函数的名字
    完成之后会在Matlab源码的当前路径生成如下格式的文件:

Matlab生成的文件

需要用到的有

  • detect.dll
  • detect.h
  • detect.lib
  • detect.ctf(在v2文件夹里)

2. Qt配置库链接

在项目中需要调用 MATLAB 生成的 detect.dll,使用 Qt (MSVC 编译器) 进行集成。在 .pro 文件中配置了 Matlab Runtime 和自定义库的链接。

  • 将detect.lib拷贝到项目根路径libs文件夹中
  • detect.dll和detect.ctf放到exe同级路径
  • detect.h放到你常规的header路径,然后增加到项目中,引用进来
    pro文件关于lib以及matlab的配置如下
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # matlab
    INCLUDEPATH += "D:/Program Files/MATLAB/MATLAB Runtime/R2022b/extern/include"
    LIBS += -L$$PWD/libs \
    -ldetect

    INCLUDEPATH += "D:/Program Files/MATLAB/R2022b/extern/include"
    INCLUDEPATH += "D:/Program Files/MATLAB/R2022b/extern/include/cpp"

    LIBS += -L"D:/Program Files/MATLAB/R2022b/extern/lib/win64/microsoft"
    LIBS += -L"D:/Program Files/MATLAB/R2022b/extern/bin/win64"

    LIBS += -lmclmcrrt

    3. 环境变量

    添加系统变量PATH
    1
    2
    D:\Program Files\MATLAB\R2022b\bin\win64
    D:\Program Files\MATLAB\R2022b\runtime\win64

4. 代码调用测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 自定义错误输出函数
int customErrorHandler(const char* msg)
{
qDebug() << "[MCR ERROR] " << msg;
return 1;
}

// 自定义标准输出函数(可选)
int customPrintHandler(const char* msg)
{
qDebug() << "[MCR PRINT] " << msg;
return 1;
}
//初始化matlab
if (!detect_yuyuInitializeWithHandlers(customErrorHandler, customPrintHandler)) {
qDebug() << "Could not initialize faultdetection_yuyu.";
}

//也可以使用没有回调的版本detect_Initialize(); //二选一不要两个都调用

注意不要调用:

1
2
3
4
5
if (!mclInitializeApplication(NULL, 0)) {
std::cerr << "Failed to initialize MCR." << std::endl;
}

//这个初始化已经在dll中进行了,这里不能再执行了会报错

完成detect_Initialize()方法的调用之后控制台会打印:
matlab dll 初始化完成
我也不知道为什么会输出两次,而且很慢(卡住主线程,可以考虑放在单独线程)

之后可进行业务方法调用了:

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
void invokeMatlab2(QVector<QVector<double>> data) {

data.resize(10000);
int rows = data.size();
int cols = data[0].size();

// 将 QVector<QVector<double>> 转换为 mwArray
mwArray inputData(rows, cols, mxDOUBLE_CLASS);
for (int row = 0; row < rows; ++row) {
for (int col = 0; col < cols; ++col) {
// 注意 MATLAB 使用列优先存储
inputData(row + 1, col + 1) = data[row][col];
}
}

// 调用 MATLAB 函数
mwArray result; // 用来接收返回结果
try {
detection(1, result, inputData);

// 将结果转换为字符串
std::string resultStr = result.ToString();
qDebug()<<(QString::fromStdString(resultStr));
}
catch (const mwException& e) {
qDebug() << "MATLAB function call failed:" << e.what();
qDebug()<<("Error: " + QString::fromStdString(e.what()));
}
catch (...) {
qDebug() << "Unknown error occurred while calling MATLAB.";
qDebug()<<("Unknown MATLAB error.");
}

}

输出结果:
matlab执行结果

最后记得在析构函数里调用释放方法:

1
2
detect_Terminate();
mclTerminateApplication();

5. 遇到的问题与报错

  1. 无法打开文件 “mclcpp.lib”
    1
    error: LNK1104: 无法打开文件“mclcpp.lib”
  2. 无法打开文件 “Files\MATLAB\R2022b\extern\lib\win64\microsoft.obj”
    1
    error: LNK1104: 无法打开文件“Files\MATLAB\R2022b\extern\lib\win64\microsoft.obj”
  3. DLL无效或损坏
    1
    error: LNK1107: 文件无效或损坏: 无法在 0x300 处读取
  4. 函数重复定义
    1
    error: LNK2005: "void __cdecl detect(int,class mwArray &,class mwArray const &)" 已经在 detect.obj 中定义

解决步骤

1. 检查 MATLAB 运行时库链接

  • 正确设置 MATLAB Runtime 路径:
    1
    2
    3
    4
    5
    6
    INCLUDEPATH += "D:/Program Files/MATLAB/R2022b/extern/include"
    INCLUDEPATH += "D:/Program Files/MATLAB/R2022b/extern/include/cpp"

    LIBS += -L"D:/Program Files/MATLAB/R2022b/extern/lib/win64/microsoft"
    LIBS += -L"D:/Program Files/MATLAB/R2022b/extern/bin/win64"
    LIBS += -lmclmcrrt
  • 注意 不要 错误地链接成 .obj 文件路径,LIBS 后面只能跟 库文件 (.lib) 路径!

2. 处理 mclcpp.lib 缺失问题

  • 发现 mclcpp.lib 原本想手动链接,但其实实际只需要 mclmcrrt.lib,不需要单独引入 mclcpp.lib
  • 因此注释掉了:
    1
    # LIBS += -lmclcpp

3. 解决 DLL 无效或损坏报错

  • 出错原因:直接将 DLL 文件用作了 .lib 文件链接。

  • 正确做法:应使用 MATLAB Compiler SDK 工具链生成对应的 .lib 文件,并链接 .lib,运行时动态加载 .dll

  • 修改链接方式为:

    1
    LIBS += -L$$PWD/dll -ldetect
  • 确保编译时用的是 .lib,不是 .dll

4. 避免函数重复定义

  • 发现源码中存在 detect.cpp 文件,同时又链接了 detect.lib
  • 这样导致同一函数既在静态库又在源文件中定义,引起 LNK2005 冲突。
  • 解决办法:
    • 删除 项目中的 detect.cppdetect.h
    • 保持仅通过 detect.lib 调用外部函数接口。

最终 .pro 文件关键配置(MATLAB部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Matlab 头文件路径
INCLUDEPATH += "D:/Program Files/MATLAB/R2022b/extern/include"
INCLUDEPATH += "D:/Program Files/MATLAB/R2022b/extern/include/cpp"

# Matlab 库文件路径
LIBS += -L"D:/Program Files/MATLAB/R2022b/extern/lib/win64/microsoft"
LIBS += -L"D:/Program Files/MATLAB/R2022b/extern/bin/win64"

# 仅链接必要库
LIBS += -lmclmcrrt

# 自定义 DLL 接口库
LIBS += -L$$PWD/dll -ldetect

# 包括头文件
INCLUDEPATH += $$PWD/dll
DEPENDPATH += $$PWD/dll

小结

  • .lib 是编译时用的,.dll 是运行时用的,两者不可混淆。
  • 链接路径中有空格的地方一定要加上引号 "..."
  • 遇到 LNK2005,注意不要重复定义同名函数。
  • INCLUDEPATHLIBS 的顺序也可能影响编译。

参考

Matlab打包为dll,并使用C++调用