从VBA打电话给fortran dll

时间:2013-08-05 20:06:02

标签: vba dll fortran

我正在使用gfortran编译器(在Simply Fortran下)用于Windows 64,当创建一个基本的fortran dll进行测试时,我无法在VBA下运行它,然后得到运行时错误48:找不到dll。

这是我的fortran子程序代码:

subroutine multiply(x, y, z)

!DEC$ ATTRIBUTES DLLEXPORT :: multiply
!DEC$ ATTRIBUTES ALIAS : "multiply" :: multiply

real, intent(in):: x, y
real, intent(out):: z

z = x * y

end subroutine multiply

我创建了一个类型库:gfortran -shared -omultiply.dll multiply.f90 该库位于“C:\ Users \ Olivier \ Documents \ Fortran \”

和我的VBA代码(我正在使用VBA 7.0):

Declare Sub multiply Lib "C:\Users\Olivier\Documents\Fortran\multiply.dll" (x As Single, y As Single, ByRef z As Single)

Sub test()

Dim x As Single
Dim y As Single
Dim z As Single

x = 2
y = 3

Call multiply(x, y, z)

Cells(1, 1) = z

End Sub

运行此VBA代码时,它表示无法找到multiply.dll,而此文件位于声明中提到的正确文件中,如果有人可以提供帮助的话!

提前谢谢

4 个答案:

答案 0 :(得分:0)

您是否尝试使用LoadLibrary-API?

Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" ( _
    ByVal lpLibFileName As String) As Long

Declare Sub multiply Lib "multiply.dll" ( _
    x As Single, _
    y As Single, _
    ByRef z As Single)

'Call LoadLibrary ONCE!!!! before calling 'multiply', e.g. on opening the workbook
LoadLibrary "C:\Users\Olivier\Documents\Fortran\multiply.dll"

此致

AKDA

答案 1 :(得分:0)

我的Excel 2010确实是32位版本,因此我构建了32位DLL与STDCALL相乘:

subroutine multiply(x, y, z)

    !GCC$ ATTRIBUTES STDCALL :: multiply

    real , intent (in) :: x, y  
    real , intent (inout) :: z  

    z = x * y

end subroutine multiply

当我用Dependency Walker打开这个DLL时,它说找不到LIBGCC_S_SJLJ-1.DLL,我不知道如何使用ALIAS解决这个问题,当在VBA下运行时我现在得到运行时错误53,如果有人请可能会有所帮助,这远远超出了我的能力,在R下连接fortran dll是如此容易,我无法想象在VBA下做同样的事情是如此复杂:(

答案 2 :(得分:0)

我遇到了同样的问题,直到找到解决方案才困扰我。如果查看依赖性walker,则缺少引用的dll,很可能来自调试环境。我是DFORTD.DLL

解决方案是

  1. 编译为发布版并确保所有相关dll都可用
  2. 或使用静态库/libs:static参数进行编译,或者在设置Fortran/Library/Debug Single-threaded中指定。
  3. 最后,请确保您的dll中没有遗漏的依赖项

    Depends

答案 3 :(得分:0)

我已成功从玩具DLL函数填充Long的2D数组,使用以下方法从Excel VBA调用。

Fortran代码:

    Subroutine FortranDLL(Array1, dim1, dim2)
    Implicit None
    Integer :: dim1, dim2
    Integer :: Array1(1:dim1, 1:dim2)
    Integer :: i, j
    do i=1,dim1
        do j=1,dim2
            Array1(i,j)=(10*i) + j
        end do
    end do
    End Subroutine FortranDLL

使用Code :: Blocks编译:

    mingw32-gfortran.exe -Jobj\Release\ -mrtd -fno-underscoring  -Wall -DBUILD_DLL -O2     -c D:\Test2\main.f95 -o obj\Release\main.o
    mingw32-gfortran.exe -shared -Wl,--output-def=bin\Release\libTest2.def -Wl,--out-implib=bin\Release\libTest2.a -Wl,--dll  obj\Release\main.o  -o bin\Release\Test2.dll -s  

特别注意编译时使用-mrtd-fno-underscoring

然后是Excel VBA:

    Option Explicit
    Option Base 1

    Declare Sub fortrandll Lib "D:\Test2\bin\Release\Test2.dll" _
        (ByRef Array1 As Long, ByRef rows As Long, ByRef cols As Long)

    Sub TestDLL()

        Const rows As Long = 7
        Const cols As Long = 5

        Dim Arr(1 To rows, 1 To cols) As Long

        Call fortrandll(Arr(1, 1), rows, cols)

        ActiveSheet.Range("A1").Resize(rows, cols).Value = Arr

    End Sub

请注意,数组的第一个元素作为普通的Long传递给DLL ByRef。

我非常感谢作者here,感谢我对工作解决方案的初步建议。一个非常类似的解决方案也出现在this comp.lang.fortran线程中。