【问题标题】:MATLAB crashes due to calllib (C++ .h file and FORTRAN .dll)MATLAB 由于 calllib(C++ .h 文件和 FORTRAN .dll)而崩溃
【发布时间】:2019-01-22 08:29:00
【问题描述】:

我正在尝试在 MATLAB 中包含一个基于外部 FORTRAN(使用英特尔 Fortran 编译器编译)的 DLL。由于它是外部的,我无法对 DLL 的运行时库进行任何调整。目前,我用 C++ 编写了一个随附的头文件,以便能够调用 DLL。使用 loadlibrary 库被加载到 MATLAB(没有错误 - 一个警告),但是,当使用 calllib 时,MATLAB 崩溃并且不提供错误。

我认为以下原因之一可能是造成这种情况的原因,但由于我对使用 DLL(尤其是 C++ 编码)缺乏经验,我自己还没有发现错误。

  • 我还从供应商处获得了一个 .lib 文件,但我尚未将其合并到 MATLAB 文件或 C++ 头文件中。
  • FILEAFILEB 变量是输入到 DLL 的两个文本文件的路径,我想我可能没有在 C++ 中正确合并这些文件。
  • mHeader 文件(MATLAB 头文件)中,stdcall 仅在注释部分提及,而不在编码部分提及。

C++头文件的代码和我的MATLAB脚本如下所示:

#ifndef _MYMODEL
#define _MYMODEL
#ifdef __cplusplus
extern "C" {
#endif // _cplusplus
    // Functions and data types defined
     void __stdcall MYFUN(char FILEA[], char FILEB[], int *IDTask, int 
    *nErrorCode, int *ErrorCode, double *Props, double *Out1, double *Out2, 
    double *Out3, double *Out4, double *Out5);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // !_MYMODEL      

MATLAB (r2018b):

%% Input to model
FILEA       = 'PATH\FILEA.txt';
FILEB       = 'PATH\FILEB.txt';
IDTask      = 1; %Multiple tasks possible in the .dll
%% Determine pointers
lpFILEA         = libpointer('cstring', FILEA);
lpFILEB         = libpointer('cstring', FILEB);
lpIDTask        = libpointer('int32Ptr', IDTask);
lpnErrorCode    = libpointer('int32Ptr');
lpErrorCode     = libpointer('int32Ptr');
lpProps         = libpointer('doublePtr');
lpOut1          = libpointer('doublePtr');
lpOut2          = libpointer('doublePtr');
lpOut3          = libpointer('doublePtr');     
lpOut4          = libpointer('doublePtr');
lpOut5          = libpointer('doublePtr');      

%% LoadLibrary
    [notfound, warnings] = loadlibrary('MYMODEL.dll','MYMODEL.h' ,'mfilename', 'mHeader');
%% Call .dll
[~,~, ~, nErrorOut, ErrorCodeOut, PropsOut, Out1_, ~, ~, Out4_, Out5_] ...
    = calllib('MYMODEL', 'MYFUN', lpFILEA, ...
    lpFILEB, lpIDTask, lpnErrorCode, lpErrorCode, lpProps, lpOut1, ...
    lpOut2, lpOut3, lpOut4, lpOut5);

提前感谢您的帮助!

【问题讨论】:

  • when using calllib MATLAB crashes and does provide an error. -> 也很高兴在这里看到错误输出。
  • 您可以做的是将调试器(例如,使用 Attach to the Process 从 Visual Studio 连接到 Matlab 进程)。并在 Matlab 进入你的 calllib 函数之前设置一个断点。然后您可以进入代码,前提是 dll 具有调试符号,即假设它是在调试模式而不是发布模式下编译的。我记得 5 到 6 年前为一个用 C 语言编写的 mex 文件执行此操作,该文件是从 Matlab 调用的。我不能 100% 确定,dll 文件是否也可以这样做。
  • 如果使用调试器看起来很麻烦(或者不适用于 dll 文件),那么您还可以做的是,如果您有源代码Fortran 代码,将调试行放在 matlab 进入该 dll 文件的 fortran 代码的开头。这样,您至少会知道对 dll 的调用可以正常工作,并且错误发生在 dll 文件中的某处。然后,您可以使用跟踪线 walk the code 来定位,直到代码正常工作为止。跟踪线可以进入文本文件。我知道,这看起来很原始,但是当没有其他方法起作用时,就是这样。
  • 你能从一个fortran程序中毫无问题地链接和调用fortran库的成员函数吗?
  • @newkid 不,我不知道。我会就此联系 .dll 供应商,这样我就可以解决这个问题(也许可以在另一个问题中解决......)

标签: c++ matlab dll fortran


【解决方案1】:

我认为您的问题是您将 NULL 指针传递给 FORTRAN 函数,然后该函数将尝试写入非法地址。您需要先为输出分配内存,然后将指向该内存的指针传递给您的函数。像这样的:

% Input to model
FILEA       = 'PATH\FILEA.txt';
FILEB       = 'PATH\FILEB.txt';
IDTask      = 1;
% Determine pointers
lpnErrorCode    = libpointer('int32Ptr',0); % !!! You need to know the size of these outputs!
lpErrorCode     = libpointer('int32Ptr',0);
lpProps         = libpointer('doublePtr',zeros(10,1)); 
lpOut1          = libpointer('doublePtr',zeros(4,1));
lpOut2          = libpointer('doublePtr',zeros(8,1));
lpOut3          = libpointer('doublePtr',zeros(2,1));     
lpOut4          = libpointer('doublePtr',zeros(5,1));
lpOut5          = libpointer('doublePtr',zeros(7,1));      

% LoadLibrary
[notfound, warnings] = loadlibrary('MYMODEL.dll','MYMODEL.h' ,'mfilename', 'mHeader');
% Call DLL
calllib('MYMODEL', 'MYFUN', [uint8(FILEA),0], [uint8(FILEB),0], ...
        IDTask, lpnErrorCode, lpErrorCode, lpProps, lpOut1, ...
        lpOut2, lpOut3, lpOut4, lpOut5);
% Get output values
nErrorCode = lpnErrorCode.Value;
clear lpnErrorCode
ErrorCode = lpErrorCode.Value;
clear lpErrorCode
% ... etc.

对于每个输出,我使用zeros 函数创建了数据。前两个是标量值(一个数据元素),其他是各种大小的数组。我不知道你的 FORTRAN 函数在那里期望什么,所以我只是编了一些尺寸。请检查您的函数以查看每个指针应指向的内存大小。

请注意,我还更改了将输入数据传递给函数的方式。 MATLAB 应自动将数据转换为正确的类型。 [uint8(FILEA),0] 从 MATLAB char 数组 FILEA 创建一个以零结尾的 c 样式字符串。在 C 中,字符串必须以零结尾。我不知道 FORTRAN 如何确定字符串的长度,我认为它是相同的,因为该函数使用“C”接口。

【讨论】:

  • 感谢您的帮助!事情肯定发生了变化,因为 MATLAB 似乎运行得更快了。但是,在运行 calllib 时,MATLAB 关闭,没有提供任何信息 (??)
  • @SaravanHoogstraten:您将不得不使用调试器,或者至少将一些控制台或文件输出添加到您的 FORTRAN 函数中,以弄清楚发生了什么以及为什么(如 Joey suggested yesterday。当 MATLAB 关闭时,这很可能是因为您将数据写入了您不应该拥有的内存部分。请确保您为此函数调用分配的数组足够大。
  • 是的,我确实想到了这样的事情。不幸的是,外部 .dll 是在发布模式下编译的,我无权访问源代码。我会就此联系 .dll 供应商。感谢您的帮助!
  • 我一直在调试代码。当我使用它们来调用更简单的 .dll 时,似乎 .h 文件和 .m 文件可以工作。但是,我想我找到了这个错误,但我不知道如何解决这个问题,所以我希望你能帮助!在 .dll 中,FILEA 和 FILEB 是 .txt 路径。目前,我认为由于使用指针,chars 作为uint8 行向量传递给.dll。你能帮我把哪一行添加到 .dll 子例程中,以便能够在 FORTRAN 中将 uint8 行向量转换回 char 吗?提前致谢!
  • @SaravanHoogstraten:charuint8 在 MATLAB 支持的所有平台上都完全相同。您还应该考虑头文件中的签名与 Fortran 函数的签名不匹配的可能性。
猜你喜欢
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-29
  • 2015-03-29
  • 2011-02-17
  • 2011-03-12
相关资源
最近更新 更多