通过Type将字符数组从VBA传递到Fortran DLL会破坏其他Type成员

And*_*las 9 excel vba fortran gfortran fortran90

信不信由你,这个标题大约和我能做到的一样短,仍然描述我遇到的问题!

所以这就是场景:我从VBA调用Fortran DLL,DLL使用用户定义的类型或Fortran名称(结构?)作为参数,并将类型复制回调用者进行验证.

该类型具有一个固定长度字符数组和一些磨机整数运行.

我已经注意到在这个字符数组之后定义的任何属性中有一些有趣的行为,我将在下面描述,在我描述我的煮沸测试设置之后:


Fortran边:

这是主程序:

SUBROUTINE characterArrayTest (simpleTypeIn, simpleTypeOut)


           use simpleTypeDefinition


!GCC$ ATTRIBUTES STDCALL :: characterArrayTest


           type(simpleType),                INTENT(IN)     :: simpleTypeIn
           type(simpleType),                INTENT(OUT)    :: simpleTypeOut


           simpleTypeOut = simpleTypeIn


END SUBROUTINE characterArrayTest
Run Code Online (Sandbox Code Playgroud)

这是simpleTypeDefinition模块文件:

Module simpleTypeDefinition


  Type simpleType

     character (len=1)  :: CharacterArray(1) 
     !The length of the array is one here, but modified in tests

     integer   (kind=2) :: FirstInteger

     integer   (kind=2) :: SecondInteger

     integer   (kind=2) :: ThirdInteger

  End Type simpleType


End Module simpleTypeDefinition
Run Code Online (Sandbox Code Playgroud)

编译步骤:

 gfortran -c simpleTypeDefinition.f90 characterArrayTest.f90
 gfortran -shared -static -o characterArrayTest.dll characterArrayTest.o
Run Code Online (Sandbox Code Playgroud)

注意:这是gfortran的32位版本,因为我使用的是32位版本的Excel.


VBA方面:

首先,镜像的simpleType和declare语句:

Type simpleType

    CharacterArray(0) As String * 1  
    'The length of the array is one here, but modified in tests

    FirstInteger As Integer

    SecondInteger As Integer

    ThirdInteger As Integer

End Type

Declare Sub characterArrayTest Lib "characterArrayTest.dll" _
Alias "characterarraytest_@8" _
(simpleTypeIn As simpleType, simpleTypeOut As simpleType)
Run Code Online (Sandbox Code Playgroud)

接下来,调用代码:

Dim simpleTypeIn As simpleType
Dim simpleTypeOut As simpleType

simpleTypeIn.CharacterArray(0) = "A"
'simpleTypeIn.CharacterArray(1) = "B"
'simpleTypeIn.CharacterArray(1) = "C"
'simpleTypeIn.CharacterArray(3) = "D"

simpleTypeIn.FirstInteger = 1
simpleTypeIn.SecondInteger = 2
simpleTypeIn.ThirdInteger = 3

Call Module4.characterArrayTest(simpleTypeIn, simpleTypeOut)
Run Code Online (Sandbox Code Playgroud)

奇怪的,越野行为:

现在我们已经过了设置,我可以描述发生了什么:

(我正在玩字符数组的长度,同时将单个字符的长度设置为1.我在所有情况下都匹配两侧的字符数组参数.)


测试用例:CharacterArray长度= 1

对于第一种情况,一切都很好,我从VBA传入simpleTypeIn和simpleTypeOut,Fortran DLL接受它并将simpleTypeIn复制到simpleTypeOut,并且在调用之后VBA返回具有相同属性CharacterArray,FirstInteger等的simpleTypeOut.


测试用例:CharacterArray长度= 2

这是事情变得有趣的地方.

在调用之前,simpleTypeIn是定义的.在调用之后,simpleTypeIn.ThirdInteger已从3更改为65!甚至更奇怪的是,65是字符A的ASCII值,它是simpleTypeIn.CharacterArray(0).

我通过将"A"更改为"(",将ASCII值设置为40来测试此关系,果然,simpleTypeIn.ThirdInteger更改为40.很奇怪.

在任何情况下,人们都会期望simpleTypeOut将是simpleTypeIn已被变形的任何奇怪的东西的副本,但不是这样!simpleTypeOut是simpleTypeIn的副本,除了simpleTypeOut.ThirdInteger,它是16961!


测试用例:CharacterArray长度= 3

这种情况与案例2相同,奇怪的是.


测试用例:CharacterArray长度= 4

在这个奇怪的情况下,在调用simpleTypeIn.SecondInteger从2更改为65之后,simpleTypeIn.ThirdInteger从3更改为66,这是B的ASCII值.

不甘示弱,simpleTypeOut.SecondInteger出现为16961,simpleTypeOut.ThirdInteger为17475.其他值成功复制(我取消了B,C和D字符分配以匹配数组大小.)


观察:

这种奇怪的腐败似乎与字符数组中的字节成线性关系.我做了一些测试,如果有人想在星期一用长度为2而不是1的单个字符进行编目,当数组大小为1时发生损坏,而不是等到大小为2.它也没有当数组大小为3时,'t'跳过"额外的损坏,就像size = 1的情况一样.


这对我来说很容易成为一个名声大厅; 我相信你可以想象,在大型程序中使用大量的Type属性进行隔离是多么集中的乐趣.如果有人有任何想法,我将不胜感激!

如果我不立即回复你,那是因为我称之为一天,但我会尝试监控我的收件箱.

And*_*las 2

看起来我 今天编辑:不是)正在收集自己的赏金!

该问题的根源在于 VBA 每个字符占用 2 个字节,而 Fortran 要求每个字符占用 1 个字节。内存乱码是由于字符数组占用的内存空间超出 Fortran 预期而造成的。向 Fortran 发送 1 字节字符的方法如下:


类型定义:

Type simpleType

    CharacterArray(3) As Byte

    FirstInteger As Integer

    SecondInteger As Integer

    ThirdInteger As Integer

End Type
Run Code Online (Sandbox Code Playgroud)

从 VBA 字符到字节值的转换:

Dim tempByte() As Byte

tempByte = StrConv("A", vbFromUnicode)
simpleTypeIn.CharacterArray(0) = tempByte(0)

tempByte = StrConv("B", vbFromUnicode)
simpleTypeIn.CharacterArray(1) = tempByte(0)

tempByte = StrConv("C", vbFromUnicode)
simpleTypeIn.CharacterArray(2) = tempByte(0)

tempByte = StrConv("D", vbFromUnicode)
simpleTypeIn.CharacterArray(3) = tempByte(0)
Run Code Online (Sandbox Code Playgroud)

此代码成功地将作为参数传递的字符串传递给 StrConv 函数。我测试了它们在 Fortran DLL 中转换为正确的 ASCII 字符,结果确实如此!此外,整数不再被错误地传回!名人堂错误已被标记。