好吧,这可能为时已晚,但需要考虑一些问题
1)“调用约定”必须正确匹配。这有几个不同的方面,其中一些是:
a) s/r 和 Arg 名称是否大写或混合。
b) Srting 调用约定。在您的代码中,您使用了一些“混合”的东西,并且似乎缺少一些位。
例如,试试这个
subroutine FORTRANCALL (R1, NUM) ! notice capitalisation
!DEC$ ATTRIBUTES DLLEXPORT :: FORTRANCALL ! notice capitalisation and default "calling convention" (this varies between compilers)
! but older CVF and Intel compilers default to CDECL, which you can set in your "properties" etc.
! Its been a while but I think newer IVF have changed to a different default convention
! e.g. if you were doing this, say, in GCC/gFortran, there would be a much longer discussion
integer, intent(in) :: r1
Character(Len=10), intent(out) :: num ! you might be able to use Character(Len=*) so long as its fixed on the VBA side
! remove this => !DEC$ ATTRIBUTES REFERENCE :: num
num = ''
write (num,'(i0)') r1 * 2
return
end subroutine FortranCall
在 VBA 方面,声明是:
Declare Sub FortranCall_XX Lib "C:\ ... your path ...\Fcall.dll.dll" _
Alias "FORTRANCALL" (R1 as Long, ByVal NUM As String, ByVal NumLen as Long)
注意 String len 的额外 Arg,这仅出现在 VBA 端,并且仅在通过 ByVal 传递 String 时出现(任何其他字符串传递,尤其是字符串数组都是一个巨大的问题......可行,但要为一些功课做好准备) .
此外,这里的字符串 len 只是在 Arg 位置之后跟随字符串。但是,如果 num 位于更早的位置,则 NumLen 的位置将位于 Arg num 之后,或者位于 Arg 列表的末尾,具体取决于调用约定。
此外,当您创建 DLL 时,编译器通常还会创建一个“Def”文件。使用 VBA/Fortran 时无需直接访问 Def 文件。但是,查看它的内部会向您显示编译器认为您的 s/r 应该被调用的确切“命名风格”。例如,使用某些调用约定,Def 文件可能会将您的 s/r 的名称显示为类似 __fortrancall@12
...无论 Def 文件说什么,您必须在 VBA 声明中使用别名“__fortrancall@12”
...这些事情需要对具有不同调用约定/编译器的一般实现进行冗长的讨论。
顺便说一句:我添加“_XX”纯粹是为了让实际的 VBA UDF 具有“明显的名称”,比如 FortranCall,或者其他什么......如果你会做很多这样的事情,尤其是使用 Functions 等,这是一个合理的习惯。 ,但在这里不太重要。
并且 VBA 子变成:
Private Sub CommandButton1_Click()
Dim r1 As Long
Dim num As String * 10
Dim numlen as Long
numlen = 10 ' required string len, can automate via intrinsics etc
r1 = 123
Call FortranCall_XX(r1, num, numlen) ' notice the extra Arg
TextBox1.Text = "Answer is " & num ' you may wish to add Trim() or something in case returned num does not require 10 chars
End Sub
2) 你真的需要将 num 作为字符串传递吗?在 VBA 和 DLL 之间传递字符串是一大堆蠕虫。为什么不将 num 作为 Double 或其他东西传递?
如果它必须是字符串并且如果它是 ByVal,那么您还必须在 VBA 端将字符串长度包括为 ByVal Long(因为在 Fortran 中,字符串 len 是隐藏值),如上所示。
如果您知道如何在 Fortran 端使用(Cray)指针或变体将 VBString 转换为 Fortran 字符串等,则可以通过 Ref 和带有/不带有额外的 StringLen 等... 一个很长的讨论。
3)分发/连接到dll的更通用和“确定”的方法是将XL表/模块转换为XLA ...即加载项。然后(通常)将 XLA 和 DLL 放入同一个目录中,并通过工具/插件等将插件添加到 Excel 中......它具有浏览以确保正确的路径。
然后,您还可以从任何工作簿/工作表等中调用您的 s/r,而不仅仅是一个工作簿。