0

I have some C code that I compile to a dll object.

I have a separate project, written in Fortran, which I too compile to a single dll object.

I need to call a function inside the Fortran dll from the C dll, without using LoadLibrary. I also need the 2 dll to remain separate, so I can replace one or the other without re-compiling them together.

I have read some information on the topic, but I can't get it to work. The compiler does not throw errors or warnings, but when I start my main program it fails. Here's what I tried:

  • I have declared all functions I wish to call from the C code with BIND ("C", NAME="FUNCNAME")

  • I have declared the USE, INTRINSIC :: ISO_C_Binding inside the functions and subroutines I intend to call from the C code

  • I included !DEC$ ATTRIBUTES DLLEXPORT :: NAME inside the Fortran code (I am using ifort)

  • I use c type variables in my Fortran code

  • I pass variables by reference in the C code

  • I included the Fortran dll .lib file as a source file to the C project

According to most sources and docs I consulted, I should be able to simply do the following:

In Fortran:

SUBROUTINE FORTRANDLLFUNCTION(arg1, arg2, arg3, char1) BIND (C, NAME='FORTRANDLLFUNCTION')
USE, INTRINSIC  :: ISO_C_Binding

IMPLICIT NONE

!DEC$ ATTRIBUTES DLLEXPORT :: FORTRANDLLFUNCTION

REAL(C_FLOAT),                  INTENT(INOUT)   :: arg1
REAL(C_FLOAT),                  INTENT(INOUT)   :: arg2
REAL(C_FLOAT),                  INTENT(INOUT)   :: arg3
CHARACTER(KIND=C_CHAR),         INTENT(IN   )   :: char1

<do stuff>
END SUBROUTINE FORTRANDLLFUNCTION

In C:

extern void FORTRANDLLFUNCTION(float**, float**, float**, char*, int);

float a, b, c;
char abc;

FORTRANDLLFUNCTION(&a, &b, &c, abc, sizeof(abc));

However, I get an error when running the main program. If I remove the call to the Fortran function and run the main program with the compiled C dll, it works. What could be the source of my problem?

This is the output of dumpbin /exports on the compiled Fortran dll: enter image description here

And this is the result of dumpbin /imports on the compiled C dll: enter image description here

ghylander
  • 136
  • 8
  • Please show the error you got. – albert Mar 11 '22 at 13:13
  • "The dynamic library C:/PATH/TO/DLL.dll could not be loaded. Check that the file exists in the specified location and that it is compiled for 64-bit applications." The file exists and is compiled for 64 bit applications. As I said in the comment, removing the call to the Fortran dll subroutine removes the error. The Fortran dll is placed in the same directory as the C dll – ghylander Mar 11 '22 at 13:23
  • @ghylander when you run `dumpbin /exports` on the fortran dll using the VC++ console what do you see? – Mgetz Mar 11 '22 at 13:29
  • Added as an edit to the OP. I would like to clarify my comment above, the error is related to reading the C dll, not the Fortran one – ghylander Mar 11 '22 at 13:36
  • 1
    @ghylander ok so it's been exported. Is there a `.lib` file generated? You'll need that to link to it. If not [You'll need to make one](https://stackoverflow.com/a/16127548). You'll also need to mark the declaration in C as [`dllimport`](https://learn.microsoft.com/en-us/cpp/cpp/dllexport-dllimport?view=msvc-170) – Mgetz Mar 11 '22 at 13:38
  • When I compile the Fortran project, a .dll, a .lib and a .exp object are generated. I added the .lib file to the "Source files" property of the project (via "Add>existing file"). I then place the Fortran dll in the same dir as the C dll. You are saying that in addition to that, I should use "__declspec( dllimport ) void myfunc()" instead of "extern void myfunc()"? Or to use "__declspec( dllimport ) ", then "extern void myfunc()" – ghylander Mar 11 '22 at 13:48
  • 1
    `__declspec( dllimport ) void myfunc()` is what you need. Add the lib file to the linker inputs not to the source files. – Mgetz Mar 11 '22 at 13:50
  • I tried this, by adding the dir where the lib object is to the "Linker>General>Additional library directories", then adding "fortrandllname.lib" to the "Linker>Input>Additional dependencies" field, but it's still gibing me the same error. I also tried to declare the fortran dll function with `extern __declspec(dllimport) void myfunc()` (moving the "extern" to the beginning). I'll keep working on this on monday. Thanks for the help so far though! Learned quite a bit by investigating the stuff you posted – ghylander Mar 11 '22 at 14:37
  • @ghylander Just an FYI.... there is one other major issue. Your declared parameter types `float**` isn't reference but pointer to pointer. Passing in `&a` is incompatible with that because you're using the wrong type. [gcc complains about this](https://godbolt.org/z/c3W69ac9z) when you turn on warnings. – Mgetz Mar 11 '22 at 14:56
  • Which Fortran coimpiler are you using? Do you have the runtime libraries for the Fortran compiler installed and accessible on PATH in the environment that you are trying to run whatever calls the C DLL/Fortran DLL from? Likely unrelated to the inability to load the DLL, but there is a misunderstanding how BIND(C) character arguments are passed in your call. – IanH Mar 11 '22 at 22:37
  • The two screenshots make me suspect that the C DLL is a 32-bit file and the Fortran DLL is a 64-bit file. Although x86-64 CPUs allow mixing 32- and 64-bit code, neither Windows nor most other widespread operating systems (MacOS, Linux ...) support mixing 32- and 64-bit code. – Martin Rosenau Mar 12 '22 at 14:33
  • Both projects are compiled for 64 bits. I can call the standalone fortran dll from the main program. I am using the ifort compiler (latest intel oneAPI). Regarding the use of float**, ifort complains if I use float* instead, and in all docs I have found of calling a Fortran dll from C, they use pointer to pointer when declaring the extern function in C – ghylander Mar 14 '22 at 08:12

1 Answers1

0

So I finally found the cause of my problems...

I abandoned this and focused on a different part of the project, which led me to finding out that the reason why I was getting this error is that the Fortran dll didn't have to be in the same dir as the C dll, but the same dir as the exe calling the C dll (the C dll being in a different location).

It really surprises me as the error I was getting was "Can't load C.dll", instead of "Could not find/load fortran.dll", the correct traceback or message could have saved me hours...

Either ways, I believe it wouldn't have worked anyways had it not been for the comments made by @Mgetz, so thanks for that, really!

Some more clarifications to some of the comments on the question:

Before I finally realized what the solution was, and on top of creating my VS projects using cmake .. -G "Visual Studio 17 2022" -A x64, I also performed a dumpbin /headers on both the Fortran dll and the C dll, which both revealed that the machine was 8664 (64 bits architecture).

I use float** and int** in the Fortran dll function prototype in the C code because I call the Fortran function from within a C function which already takes pointers as arguments, so if I don't use "double pointers" when calling the Fortran function I get warnings from the compiler.

And yes, the Fortran runtime libraries were in the environment, I regularly work with Fortran, I knew that was not an issue.

ghylander
  • 136
  • 8