【问题标题】:Using a gFortran dll with Excel 365 / VBA 7在 Excel 365 / VBA 7 中使用 gFortran dll
【发布时间】:2021-11-27 15:56:00
【问题描述】:

多年来,我使用了用 Fortran PowerStation 和 Excel 02/03 编写的 dll 与 VBA 6 的组合。

但进步在继续,我得到了一台新机器,它装有 Windows 64 位、Excel 365(64 位),但没有 Fortran。所以我从 SourceForge 下载了 gFortran 作为 MinGW-w64-for 32 和 64 位 Windows 的一部分。 现在我必须让它工作。 这是 Fortran 源代码,位于名为 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

(第二行指定 Microsoft 属性。稍后会详细介绍。)

在添加到添加到 %PATH% 的 MinGW 箱的路径后,我从目录 . 编译如下

gfortran -Wextra -Wall -pedantic -shared -fPIC -o .\Output\gTest.dll .\Source\gTest.F90

这没有产生任何输出,但确实写入了文件 gTest.dll。

等等到 Excel / VBA。我设置了一个小电子表格 gTest.xlsm,它试图调用 AddIt。它声明 AddIt 如下:

Declare PtrSafe Function AddIt Lib "C:\A\Projects\gTest\Output\gTest.dll"(iVal1 As Integer, iVal2 As Integer) As Integer

没有运气。于是我输入了以下 VBA 代码并单步执行:

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

正如预期的那样,它在 Sum = 上爆炸了,出现了 MS 更无用的错误消息之一"Error in loading DLL (Error 48)"

现在,我怀疑问题在于我没有告诉 dll 必须导出其中的内容 - 上面的 MS 属性语句的功能。我看到在 C++ 环境中,您可以使用关键字 __declspec(dllexport) 或模块定义 (.def) 文件从 dll 导出。但是我看不出您如何将其中任何一个与 gFortran 一起使用(我已经尝试过)。 或者,可能,我没有为 64 位编译和链接?

有人可以帮我吗?将不胜感激。

【问题讨论】:

  • 最好明确地将ByRef 放在函数参数上,这样可以清楚地看出值是通过引用而不是值传递的。然后检查用作编译器选项的调用约定,以查看参数何时应作为引用以及何时作为值。

标签: excel vba fortran gfortran dllexport


【解决方案1】:

这因编译器而异,下面显示的只是为了说明。以下某些信息可能不正确,请查阅具体的编译器文档以了解要使用的调用约定。

考虑以下Fortran 过程

subroutine test(n,x,a)
!gcc$attributes dllexport :: test
  integer :: n
  real :: x
  real, dimension(n) :: a
end subroutine

以及对应的VBA声明

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)

(*) 过程名称仅在 x86 上进行修饰。 @n 表示函数返回前需要从堆栈中清除的字节数。

请注意,对于数组参数,仅传递对第一个元素的引用,因为 VBA 使用 Fortran 不支持的 SafeArray 结构。

Dim n as Long
Dim x as Single
Dim a() as Single

Call TEST(n, x, a(1))

考虑为标量类型指定特定的值类型,覆盖C,Default 调用约定的默认行为

subroutine test(n,x,a)
!gcc$attributes dllexport :: test
  integer, value :: n
  real, value :: x
  real, dimension(n) :: a
end subroutine

在 VBA 中声明为

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))

【讨论】:

  • !dec$attributes 也将被 gfortran 忽略
  • @VladimirF 是的,无论 gfortran 的属性声明是什么。
  • 感谢弗拉基米尔和约翰。不幸的是,仍然没有运气。我已经尽可能多地尝试了您的建议组合,但我不断收到错误消息“加载 DLL 时出错(错误 48)”。
  • 我什至尝试删除所有参数,以消除参数传递约定。我如何才能确定 gFortran 实际上正在生成 64 位 dll?
  • 我使用了dumpbin.exe 实用程序或depends.exe
【解决方案2】:

!MS$ATTRIBUTES 被 gfortran 忽略,试试

!GCC$ ATTRIBUTES DLLEXPORT

https://gcc.gnu.org/onlinedocs/gfortran/ATTRIBUTES-directive.html

STDCALL 仅适用于 32 位。

也可以使用bind(C)设置别名:

integer(2) function AddIt(iVal1,iVal2) bind(C, name='AddIt')

请检查 MS Powerstation 中的种类编号是否与 gfortran 中的种类编号相对应。很有可能您实际上想要 integer(8) 或使用更便携的语法 Fortran: integer*4 vs integer(4) vs integer(kind=4) 的等价物

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-10
    • 2011-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多