由于调用 lib (C++ .h 文件和 FORTRAN .dll),MATLAB 崩溃了。

3
我正在尝试将一个外部的基于FORTRAN的DLL(使用Intel Fortran编译器编译)包含到MATLAB中。由于它是外部的,我不能对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);

Thanks in advance for your help!


当使用calllib时,MATLAB崩溃并没有提供错误信息。在这里看到错误输出会很好。 - Duck Dodgers
2
你可以连接你的调试器,例如从Visual Studio,使用“附加到进程”功能,连接到Matlab进程。在Matlab进入你的calllib函数之前设置断点。然后,如果dll具有调试符号,即它是在调试模式下编译而不是发布模式下编译的,你就可以步进代码了。我记得5~6年前我为一个从Matlab调用的C语言mex文件做过这个操作。我不确定是否对dll文件也适用。 - Duck Dodgers
3
如果使用调试器似乎很麻烦(或者对于dll文件无效),那么您可以做的另一件事是,如果您有Fortran代码的源代码,就在Matlab进入该dll文件的开头处放置调试代码行。这样,您至少会知道对dll的调用正常工作,而错误发生在dll文件的某个地方。然后,您可以使用跟踪线walk the code来定位代码工作良好的位置,跟踪线可以输出到文本文件中。我知道这看起来很原始,但当其他方法都不起作用时,这就是唯一的方法。 - Duck Dodgers
1
你能在Fortran程序中链接和调用Fortran库的成员函数而不出现问题吗? - newkid
1
@newkid 不,我没有。我会联系.dll供应商解决这个问题(也许在另一个问题中提到它...) - Sara van Hoogstraten
显示剩余7条评论
1个回答

2

我认为您的问题在于您向FORTRAN函数传递了空指针,这将尝试写入非法地址。您需要先为输出分配内存,然后将指针传递给此内存以供函数使用。类似这样:

我想您的问题是您将空指针传递给了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会关闭,而不提供任何信息(??) - Sara van Hoogstraten
1
@SaravanHoogstraten:你需要使用调试器,或者至少在你的FORTRAN函数中添加一些控制台或文件输出,以找出发生了什么以及为什么(正如Joey昨天建议的)。当MATLAB关闭时,很可能是因为你将数据写入了不应该写入的内存部分。确保为此函数调用分配的数组足够大。 - Cris Luengo
是的,我确实想到了这样的事情。不幸的是,外部的.dll文件是以发布模式编译的,而我没有访问源代码的权限。我会联系.dll文件的供应商解决这个问题。感谢您的帮助! - Sara van Hoogstraten
我一直在调试代码。看起来.h文件和.m文件能够工作,在使用它们调用一个更简单的.dll时。但是,我认为我已经找到了错误,但是不知道如何解决,所以希望你能帮忙!在.dll中,FILEA和FILEB是.txt路径。目前,我认为由于使用指针,这些“char”作为“uint8”行向量传递到.dll。你能帮我确定应该添加到.dll子程序的哪一行,以便能够将“uint8”行向量转换回FORTRAN中的“char”吗?谢谢提前! - Sara van Hoogstraten
@SaravanHoogstraten:在 MATLAB 支持的所有平台上,charuint8 是完全相同的东西。您还应考虑头文件中的签名与 Fortran 函数的签名不匹配的可能性。 - Cris Luengo
嗨Cris,谢谢!问题已经通过在Fortran中硬编码.txt文件的路径来解决,因此它们不再需要通过指针传递。 - Sara van Hoogstraten

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接