For years I used a combination of dlls written in Fortran PowerStation and Excel 02/03, with VBA 6.
But progress marches on, and I got a new machine with Windows 64 bit, Excel 365 (64 bit), and no Fortran. So I downloaded gFortran as part of MinGW-w64-for 32 and 64 bit Windows from SourceForge. Now I have to get it working. Here is the Fortran source code, in a file called gTest.F90:
integer(2) function AddIt(iVal1,iVal2)
!MS$ATTRIBUTES dllexport, stdcall, alias:'AddIt' :: ADDIT
Integer(2) iVal1,iVal2
AddIt=iVal1 iVal2
end function AddIt
(The second line specifies Microsoft attributes. More about this later.)
I compiled it as follows, from directory ., after adding the path to the MinGW bins added to %PATH%
gfortran -Wextra -Wall -pedantic -shared -fPIC -o .\Output\gTest.dll .\Source\gTest.F90
This produced no output, but did write the file gTest.dll.
And so onto Excel / VBA. I set up a little spread sheet, gTest.xlsm, which tried to invoke AddIt. It declared AddIt as follows:
Declare PtrSafe Function AddIt Lib "C:\A\Projects\gTest\Output\gTest.dll"(iVal1 As Integer, iVal2 As Integer) As Integer
No luck. So I entered the following VBA code and stepped through it:
Sub RunIt()
Dim Val1 As Integer, Val2 As Integer, Sum As Integer
Val1 = 1
Val2 = 10
Sum = AddIt(Val1, Val2)
Debug.Print Val1, Val2, Sum
End Sub
As expected, it blew up on Sum =, with one of MS's more useless error messages "Error in loading DLL (Error 48)"
.
Now, I suspect that the problem is that I am not telling the dll what within it must be exported - the function of the MS attribute statement above. I see that in the C environment, you can export from a dll with either the keyword __declspec(dllexport)
, or with a module definition (.def) file. But I cannot see how you could utilise either of these with gFortran (and I have tried).
Or, possibly, did I not compile and link for 64 bits?
Can someone please help me? It would be gratefully appreciated.
CodePudding user response:
!MS$ATTRIBUTES
is ignored by gfortran, try
!GCC$ ATTRIBUTES DLLEXPORT
See https://gcc.gnu.org/onlinedocs/gfortran/ATTRIBUTES-directive.html
STDCALL
is only relevant for 32 bit.
The alias can also be set using bind(C)
:
integer(2) function AddIt(iVal1,iVal2) bind(C, name='AddIt')
Please check that the kind numbers in MS Powerstation actually correspond to those in gfortran. Chances are that you actually want integer(8)
or an equivalent using some more portable syntax Fortran: integer*4 vs integer(4) vs integer(kind=4)
CodePudding user response:
This varies by the compiler and it is shown below just for illustration. Some of the below info might be incorrect, please consult the specific compiler documentation for what calling convention to use.
Consider the following Fortran
procedure
subroutine test(n,x,a)
!gcc$attributes dllexport :: test
integer :: n
real :: x
real, dimension(n) :: a
end subroutine
And the corresponding VBA
declaration
Calling Convention | Procedure Names(*) | Scalar Arg. | Array Arg. | VBA Spec |
---|---|---|---|---|
C, Default | _UPPERCASE | Reference | Reference | (ByRef N As Long, ByRef X As Single, ByRef A As Single) |
C, REF | _lowercase | Value | Reference | (ByVal N As Long, ByVal X As Single, ByRef A As Single) |
STD, CALL | UPPERCASE@n | Value | Reference | (ByVal N As Long, ByVal X As Single, ByRef A As Single) |
STD, REF | UPPERCASE@n | Reference | Reference | (ByRef N As Long, ByRef X As Single, ByRef A As Single) |
(*) Procedure names are only decorated on x86. @n denotes the number of bytes needed to be cleaned from the stack before the function returns.
Note that for array arguments, only pass the reference to the first element because VBA uses SafeArray
structures that are not supported by Fortran.
Dim n as Long
Dim x as Single
Dim a() as Single
Call TEST(n, x, a(1))
Consider specifying the specific value types for scalar types overriding the default behavior for C,Default
calling convention
subroutine test(n,x,a)
!gcc$attributes dllexport :: test
integer, value :: n
real, value :: x
real, dimension(n) :: a
end subroutine
to be declared in VBA as
Declare PtrSafe Sub Test Lib "Fortran.dll" (ByVal N As Long, ByVal X As Single, ByRef A As Single)
Call Test(100&, x, a(1))