I want to compile and link a dynamic link library (dll) from a Fortran code using gfortran (mingw - 64bit) in windows. I test generated dlls in Excel 64-bit. I noticed, Excel does not return any result (it returns: #VALUE!) for the below Fortran code:
Function RANDGF() bind(c)
use ISO_C_BINDING
implicit none
!GCC$ ATTRIBUTES DLLEXPORT :: RANDGF
Double Precision :: RANDGF
Double Precision :: A
Call RANDOM_NUMBER(A)
RANDGF = 2.0
END
However, if I comment the Call RANDOM_NUMBER(A) line, the DLL returns the expected result.
The command that I use for the compilation is:
gfortran -shared -fpic -o randgf.dll randgf.f90
Here is my VBA Code in Excel:
Private Declare PtrSafe Function randgf Lib "randgf.dll" () As Double
Public Function gfort_res() As Double
gfort_res = randgf()
End Function
What should I do in order to call functions like RANDOM_NUMBER in my code, if I want to use them in a DLL?
There are two main issues that leads to Excel to not execute the function:
One of them, is that Excel can't find the DLL file, and this is because,
even if the dll file is at the same folder of the excel worksheet, the Excel's
process current dir is not the same.
For this issue, there are two possible solutions:
First: At the function declaration, you can put the full path to the DLL file:
Private Declare PtrSafe Function randgf Lib "C:\myexcellib\randfunc\randgf.dll" () As Double
Second: Before calling the function, you can change the current dir to the dll folder:
Private Declare PtrSafe Function randgf Lib "randgf.dll" () As Double
Public Function gfort_res() As Double
ChDrive "C"
ChDir "\myexcellib\randfunc"
gfort_res = randgf()
End Function
The second issue is that Excel can't find the function inside the DLL file.
And for this issue, there are two possible solutions, as well:
First: You can indicate the ordinal number of the funcition as an "alias" at the declare
clause (e.g. if the ordinal number is 1):
Private Declare PtrSafe Function RANDFG Lib "randfg.dll" Alias "#1" () As Double
Second: If you are using fortran 2003 or later, you can indicate the binding name that will
be exported:
function RANDGF() bind(c, name="RANDGF")
use ISO_C_BINDING
implicit none
!GCC$ ATTRIBUTES DLLEXPORT :: RANDGF
Double Precision :: RANDGF
Double Precision :: A
call RANDOM_NUMBER(A)
end function RANDGF
Important: All of the VBA code should be inside a Module, otherwise it won't work.
I found the reason of this behaviour. Before running my DLL, I had to copy gfortran libraries to my target computer. I simply copied all the lib*.dll files form mingw64\bin directory to windows\system32 folder of my target computer and now the dll works fine and Excel returns the desired result.
Is there any way to automatically load required DLLs from mingw64\bin into my own DLL (randgf.dll) during compilation and linking at my source computer? I want to avoid copying all the lib*.dll files from mingw64\bin.
Related
Is there a way to control the naming of symbols in shared libraries? Specifically, I've been using GCC for a project where we access shared library through C-Types in Python. This works great, however recently I've been working with a system where the Intel compiler is suggested. I can build the shared object just fine but I find the symbols have a slightly different naming convention as compared to intel. Specifically when I compile with gcc the symbol names look like:
__test_function_MOD_read_a_file
the Intel compiled shared object has the symbol names like:
test_function_mp_read_a_file__
Is there a way to force consistency of the naming or at least change the name of the symbols after the fact?
For example consider the following bit of code test_function.f90
MODULE test_function
CONTAINS
SUBROUTINE read_a_file
PRINT *,'I did a thing!'
END SUBROUTINE
END
The compile line should look something like
gfortran -fPIC -c test_function.f90
gfortran -shared -o libtest_function.so test_function.o
Both compilers are mangling the names of the subprograms contained in the module. The Fortran standard does not mandate any naming convention. You can prevent the name mangling by using the ISO C Binding feature of Fortran to give the subprogram a specific name. For example,
module bar
contains
function fun(x) bind(c, name='foo')
real fun, x
fun = x
end function fun
function goo(x)
real goo, x
goo = x
end function goo
end module bar
When compiled with gfortran the resulting object file contains
gfortran -c a.f90
nm a.o
00000000 T __bar_MOD_goo
00000013 T foo
Thus, you can reference functionfun as foo in the library. You'll probably also want to use the types defined by the iso_c_binding module.
The following code compiles to 64 bit but the use of Fortran Generic Interfaces seems to confuse the gcc attribute in 32 bit (required to call the 32 bit STDCALL API). The code is a stripped down version of the f03gl project which I am attempting to build for Windows (I couldn't create a smaller repro).
A rework of the code (shown at the bottom) without the generic interface and passing arguments direct does compile and work. I'm hoping I won't have to rewrite all of the calls to work around this issue.
The error is;
callglutbad.o:callglutbad.f90:(.text+0x27): undefined reference to `glutInit'
callglut.f90 (fails 32 bit compilation)
MODULE GlutModule
USE ISO_C_BINDING
IMPLICIT NONE
PUBLIC glutInit
INTERFACE glutInit
MODULE PROCEDURE glutInit_f03
END INTERFACE glutInit
INTERFACE
SUBROUTINE glutInit_gl(pargc, argv) BIND(C,NAME="glutInit")
IMPORT
#ifdef x86
!GCC$ ATTRIBUTES stdcall :: glutInit
#endif
INTEGER(C_INT) :: pargc
TYPE(C_PTR), INTENT(IN) :: argv
END SUBROUTINE glutInit_gl
END INTERFACE
CONTAINS
SUBROUTINE glutInit_f03()
INTEGER(C_INT) :: argcp=1
TYPE(C_PTR), DIMENSION(1), TARGET :: argv=C_NULL_PTR
CHARACTER(C_CHAR), DIMENSION(1), TARGET :: empty_string=C_NULL_CHAR
argv(1)=C_LOC(empty_string)
CALL glutInit_gl(argcp, C_LOC(argv))
END SUBROUTINE
END MODULE GlutModule
program main
USE GlutModule
PRINT *,"Calling glutInit"
call glutInit()
PRINT *,"Called glutInit"
end program main
build.bat
REM 32 bit
#setlocal
#SET PATH=%PATH%;C:\msys64\mingw32\bin\
gfortran -Dx86 -cpp -c callglut.f90 -o callglut.o
gcc callglut.o -o callglut32.exe ..\x86\lib\freeglut.lib -lgfortran
#endlocal
REM 64 bit
#setlocal
#SET PATH=%PATH%;C:\msys64\mingw64\bin\
gfortran -cpp -c callglut.f90 -o callglut.o
gcc callglut.o -o callglut64.exe ..\x64\lib\freeglut.lib -lgfortran
#endlocal
The generic interface is used to call glutInit without parameters from Fortran (the parameters are filled in by the proxy subroutine).
callglut.f90 (compiles and runs on both platforms)
MODULE GlutModule
USE ISO_C_BINDING
IMPLICIT NONE
PUBLIC glutInit
INTERFACE
SUBROUTINE glutInit(pargc, argv) BIND(C,NAME="glutInit")
IMPORT
#ifdef x86
!GCC$ ATTRIBUTES stdcall :: glutInit
#endif
INTEGER(C_INT) :: pargc
TYPE(C_PTR), INTENT(IN) :: argv
END SUBROUTINE glutInit
END INTERFACE
END MODULE GlutModule
program main
USE GlutModule
PRINT *,"Calling glutInit"
call glutInit(0,C_NULL_PTR)
PRINT *,"Called glutInit"
end program main
Am I seeing a compiler bug with the attribute statement not being applied to a generic interface or is it something that I am doing? I suspect this and the 32 bit STDCALL name mangling from glutInit to _glutInit#8 is not being performed.
I am using gfortran under msys2 (GNU Fortran (Rev2, Built by MSYS2 project) 7.1.0) on 64 bit Windows with both 32 and 64 bit compilers.
It doesn't make sense to apply the STDCALL attribute to an identifier that is only a generic name. From the compiler's perspective, a generic name is a common label for a number of procedures. While the individual specific procedures may have different calling conventions, the label for them does not.
If you want to tell the compiler that a specific procedure has a certain calling convention, use the name that the compiler knows the specific procedure by, in the relevant declaration.
!GCC$ ATTRIBUTES stdcall :: glutInit_gl
I wrote a simple C++ program
Source.cpp:
double __stdcall square(double& x)
{
return x*x;
}
Define.def:
LIBRARY "square"
EXPORTS
square
Then in my .xlsm file Modules, added module1 importing:
Declare PtrSafe Function square _
Lib "C:\Users\user\Documents\AthosCode\Marek Kolman Square\Square\Debug\square.dll" _
(ByRef x As Double) As Double
and in Sheet1, put 10 in A1, and =square(A1) in B1.
The error says: "A value used in the formula is of the wrong data type".
What's the problem and how could I fix it?
My environment is
Windows 7 64-bit
Visual Studio 2016 Community (so it's 32-bit)
Excel 2016 64-bit
Replying the comment, if I change passing by reference to passing by value, the error is the same.
I changed the Source.cpp to
double __stdcall square(double x)
{
return x*x;
}
and changed the module1 as
Declare PtrSafe Function square _
Lib "C:\Users\user\Documents\AthosCode\Marek Kolman Square\Square\Debug\square.dll" _
(ByVal x As Double) As Double
Same error.
Due to the differences in architecture, it's impossible to load a 32-bit DLL in a 64-bit process and vice versa
It's possible to access 32-bit DLLs from 64-bit code but unless you have legacy DLLs without a 64-bit version, you should compile as a 64-bit project. And 64-bit Office doesn't support 32-bit plugin at all so you'll need a 64-bit DLL.
Can I load a 32 bit DLL into a 64 bit process on Windows?
Process Interoperability
System: Windows-7-64-bit/Visual-Studio-2010/Intel-Visual-Fortran-11.
I am creating 32-bit executables.
Fortran routine declaration
SUBROUTINE LA01BD(N,M,L,A,B,C,X,F,IA,IPRINT,IND,WK,IER)
!DEC$ ATTRIBUTES DLLEXPORT::LA01BD
!DEC$ ATTRIBUTES STDCALL,REFERENCE,ALIAS:"LA01BD"::LA01BD
use, intrinsic :: ISO_C_BINDING
C++ function signature declaration
extern "C" {void __stdcall LA01BD(int *N, int *M, int *L, double *A, double *B, double *C, double *X, double *F, int *IA, int *IPRINT, int *IND, double *WK, int *IER); }
I created the dll from fortran code using Visual Studio 2010 and Intel Visual Fortran compiler 11. I checked the exported symobol in dependency walker and the Function is "LA01BD".
When using the same dll (the .lib file during linking) in my C++ project, I get the following linker error.
lpwrap.obj : error LNK2001: unresolved external symbol _LA01BD#52
I am unable to resolve this issue. What does suffix "#52" does? How to fix the linking issue?
Thanks.
The C++ compiler applies name decoration to the identifier. The __stdcall decoration is a leading _underscore and a trailing #n where n is the size of the activation frame.
The ALIAS directive in your Fortran code caused this problem, you forced it to be exported as "LA01BD" instead of "_LA01BD#52". You should first try to remove it so the normal name decoration is applied. If that's not an option then you'll need to either create an import library with lib.exe /def from a properly crafted .def file or fallback to late binding with GetProcAddress().
If you must use the STDCALL calling convention, then add the DECORATE attribute to the Fortran side to instruct the compiler to decorate the specified alias.
SUBROUTINE LA01BD(N,M,L,A,B,C,X,F,IA,IPRINT,IND,WK,IER)
!DEC$ ATTRIBUTES DLLEXPORT::LA01BD
!DEC$ ATTRIBUTES STDCALL,REFERENCE,ALIAS:"LA01BD"::LA01BD
!DEC$ ATTRIBUTES DECORATE :: LA01BD
it is caused by name mangling, please ref this for details. I guess you missed the extern "c" in your C++ project.
I'm trying to learn how to call a function in a fortran dll from a fortran executable on windows. I'm working with gfortran 4.7 and photran in eclipse.
My test dll has a single function in hello.f90:
hello.f90
subroutine hello
implicit none
print *, "Hello World!"
end subroutine hello
with the following makefile:
all:
gfortran -Wall -c hello.f90
gfortran -shared -o hello.dll hello.o
Dependency Walker confirms that the function "hello_" is exported.
Now I'm trying to build a program that calls it dynamically. I've built the following based on examples I've found online, but it doesn't compile:
main.f90
program main
implicit none
integer :: p
pointer (q, hello)
p = loadlibrary("hello.dll")
q = getprocaddress(p, "hello_")
call hello
end program main
makefile
all:
gfortran -Wall -pedantic -fcray-pointer main.f90
The error message is that function LoadLibrary (and getprocaddress) has no IMPLICIT type. I suspect that means those functions aren't defined and I need to include their headers somehow. Is that right? I've found a declaration for loadlibrary in c:\mingw\include\winbase.h
cheers,
Marc
LoadLibrary and GetProcAddress are Windows API routines. Like any non-intrinsic function, you need to declare the type of those functions and, given the nature of those API's, you also need to go further and provide the full interface.
If you are compiling for 32 bit Windows then those API's use the stdcall calling convention. You need to use gfortran source directive extensions to designate that.
(On 64 bit Windows stdcall is defined to be the same as the C calling convention - the source directive has no effect.)
For calling convention control, if I change your DLL code to:
SUBROUTINE hello() BIND(C, NAME='hello')
IMPLICIT NONE
PRINT *, 'Hello'
END SUBROUTINE hello
then the following main program will load the resulting DLL and execute that procedure.
PROGRAM Main
USE, INTRINSIC :: ISO_C_BINDING, ONLY: &
C_F_PROCPOINTER, C_FUNPTR, C_INTPTR_T, &
C_NULL_CHAR, C_CHAR, C_ASSOCIATED
IMPLICIT NONE
INTERFACE
FUNCTION LoadLibrary(lpFileName) BIND(C,NAME='LoadLibraryA')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INTPTR_T, C_CHAR
IMPLICIT NONE
CHARACTER(KIND=C_CHAR) :: lpFileName(*)
!GCC$ ATTRIBUTES STDCALL :: LoadLibrary
INTEGER(C_INTPTR_T) :: LoadLibrary
END FUNCTION LoadLibrary
FUNCTION GetProcAddress(hModule, lpProcName) &
BIND(C, NAME='GetProcAddress')
USE, INTRINSIC :: ISO_C_BINDING, ONLY: &
C_FUNPTR, C_INTPTR_T, C_CHAR
IMPLICIT NONE
!GCC$ ATTRIBUTES STDCALL :: GetProcAddress
TYPE(C_FUNPTR) :: GetProcAddress
INTEGER(C_INTPTR_T), VALUE :: hModule
CHARACTER(KIND=C_CHAR) :: lpProcName(*)
END FUNCTION GetProcAddress
END INTERFACE
ABSTRACT INTERFACE
SUBROUTINE hello_intf() BIND(C)
IMPLICIT NONE
END SUBROUTINE hello_intf
END INTERFACE
INTEGER(C_INTPTR_T) :: module_handle
TYPE(C_FUNPTR) :: proc_address
PROCEDURE(hello_intf), BIND(C), POINTER :: my_proc
!****
module_handle = LoadLibrary(C_CHAR_'hello.dll' // C_NULL_CHAR)
IF (module_handle == 0) STOP 'Unable to load DLL'
proc_address = GetProcAddress( module_handle, &
C_CHAR_'hello' // C_NULL_CHAR )
IF (.NOT. C_ASSOCIATED(proc_address)) &
STOP 'Unable to obtain procedure address'
CALL C_F_PROCPOINTER(proc_address, my_proc)
CALL my_proc
END PROGRAM Main