3

I am trying to include an external FORTRAN-based (compiled with the Intel Fortran compiler) DLL in MATLAB. As it is external, I cannot make any adjustments to the runtime library of the DLL. Currently, I wrote an accompanying header file in C++ to be able to call the DLL. Using loadlibrary the library is loaded into MATLAB (no errors - one warning), however, when using calllib MATLAB crashes and does not provide an error.

I think one of the following might be the reason for this, but as I am an inexperienced in using DLL's (especially coding in C++) I did not yet find the error myself.

  • There is also a .lib file that I got from the supplier, but I did not incorporate this yet in the MATLAB file or C++ header file.
  • The FILEA and FILEB variables are paths to two text files that are input to the DLL, I think I might not have correctly incorporated these in C++.
  • In the mHeader file (MATLAB header file) stdcall is only mentioned in the commented section and not in the coding section.

The code for the header file in C++ and my MATLAB script is shown below:

#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!

Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
  • `when using calllib MATLAB crashes and does provide an error.` -> would be nice to see that error output here also. – Duck Dodgers Jan 22 '19 at 08:41
  • Thanks for your quick reply. Sorry that was a typo, it does not provide an error. The program states: MATLAB has encountered an internal problem and needs to close. – Sara van Hoogstraten Jan 22 '19 at 08:45
  • 2
    What you can do is, connect your debugger, for example, from Visual Studio, using `Attach to the Process`, to the Matlab process. And put a breakpoint before Matlab enters your `calllib` function. Then you can step inside the code, provided the `dll` has debug symbols, i.e. provided it was compiled in debug-mode and not release mode. I remember doing this 5~6 years ago for a mex-file written in C, that was called from Matlab. I am not 100% sure, if the same is possible for the `dll` files. – Duck Dodgers Jan 22 '19 at 09:40
  • 3
    If working with the debugger seems cumbersome (or does not work for the dll file), then also what else you can do is, **IF** you have the source code to that Fortran code, to put debug lines at the start of that fortran code where matlab enters that dll file. In this way, you would know at least the call to the dll works fine and the error happens somewhere inside the dll file. you could then `walk the code` with trace lines to locate until what point the code works fine. The tracelines can go into a text file. This seems pretty primitive, I know, but when nothing else works then this is it. – Duck Dodgers Jan 22 '19 at 09:46
  • Here are two links [debug mex-file called from matlab using visual studio](https://stackoverflow.com/questions/23653347/debug-a-mex-function-in-visual-studio) and [how to debug dll files with debug information using visual studio](https://stackoverflow.com/questions/1130417/how-to-debug-a-referenced-dll-having-pdb). – Duck Dodgers Jan 22 '19 at 09:49
  • Thanks! I will give it a try. – Sara van Hoogstraten Jan 22 '19 at 09:56
  • Also, look in your `C:\Users\$YourName$\AppData\Local\Temp` (if you are on windows). Matlab should have generated dump files. They would be helpful also. – Duck Dodgers Jan 22 '19 at 09:59
  • 1
    Can you link and call member functions of the fortran library from a fortran program without problems? – newkid Jan 22 '19 at 15:25
  • The [documentation](https://www.mathworks.com/help/matlab/matlab_external/working-with-pointers.html) says to use `[int8(str) 0]` when passing data to a `char*`. Also, `libpointer` creates a NULL pointer if you don't give it data, your FORTRAN function is likely writing to a NULL pointer here. – Cris Luengo Jan 22 '19 at 16:47
  • @newkid when I try to compile a program (that will call the function in the .dll) in Fortran, it states that filename.obj cannot be opened for write and compilation with ifort is aborted. – Sara van Hoogstraten Jan 23 '19 at 16:11
  • @SaravanHoogstraten do you have access to the source of the fortran library? If you have problems compiling and linking the fortran library with your test fortran program. It may have to be addressed in another question – newkid Jan 23 '19 at 16:14
  • 1
    @newkid No, I don't. I will contact the .dll supplier about this, so I can solve this issue (and maybe address it in another question...) – Sara van Hoogstraten Jan 23 '19 at 16:20

1 Answers1

2

I think your problem is that you are passing NULL pointers to your FORTRAN function, which will then attempt to write to an illegal address. You need to allocate memory for the outputs first, and pass the pointers to this memory to your function. Something like this:

% 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.

For each of these outputs, I created data using the zeros function. The first two are scalar values (one data element), the others are arrays of various sizes. I have no idea what your FORTRAN function expects there, so I just made up some sizes. Please check your function to see what memory sizes each pointer should point to.

Note that I also changed how you pass the input data to your function. MATLAB should automatically convert the data to the right types. [uint8(FILEA),0] creates a zero-terminated c-style string from the MATLAB char array FILEA. In C strings must be terminated with a zero. I don't know how FORTRAN determines the length of a string, I presume it is the same, since the function uses a "C" interface.

Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
  • Thanks for your help! Something definitely changed, as MATLAB seems to run much faster. However, while running calllib MATLAB closes, without giving any information (??) – Sara van Hoogstraten Jan 23 '19 at 10:30
  • 1
    @SaravanHoogstraten: You will have to use a debugger, or at least add some console or file output to your FORTRAN function, to figure out what is happening and why (as [Joey suggested yesterday](https://stackoverflow.com/questions/54304095/matlab-crashes-due-to-calllib-c-h-file-and-fortran-dll/54313169?noredirect=1#comment95430533_54304095). When MATLAB closes, it is most likely because you've written data into parts of the memory that you shouldn't have. Make sure that the arrays you allocate for this function call are big enough. – Cris Luengo Jan 23 '19 at 15:23
  • Yes, I figured something like this indeed. Unfortunately the external .dll is compiled in release mode and I do not have access to the source code. I will contact the .dll supplier about this. Thanks for helping! – Sara van Hoogstraten Jan 23 '19 at 16:04
  • I have been working on debugging the code. It seems the .h file and .m file work, when I use these to call a more simple .dll. However, I think I found the bug, but I don't know how to solve this, so I hope you can help! In the .dll the FILEA and FILEB are .txt paths. Currently, I think that because of using pointers, the `char`s are passed as `uint8` row vectors to the .dll. Can you help me with which line I should add to the .dll subroutine, to be able to convert the `uint8` row vector back to `char` in FORTRAN? Thanks in advance! – Sara van Hoogstraten Jan 28 '19 at 10:29
  • @SaravanHoogstraten: a `char` and a `uint8` are exactly the same thing on all platforms supported by MATLAB. You should consider also the possibility that the signature in the header file doesn’t match that of the Fortran function. – Cris Luengo Jan 28 '19 at 14:23
  • Hi Cris, thanks! The problem is solved by hard-coding the path for the .txt files in Fortran, so they do not have to be passed with pointers anymore. – Sara van Hoogstraten Jan 28 '19 at 15:31