奇怪的是 excel 的 2011 VBA 在 mac 上找不到 c++ dylib

c++ dylib curiously not found by excel's 2011 VBA on mac

我主要尝试在更简单的设置中模仿 this。运气不好。

我写了一个简单的 fortran 代码:

      subroutine POWERTWO (n, nsquared)
      !DEC$ ATTRIBUTES DLLEXPORT :: POWERTWO
      integer, intent(in) :: n
      integer, intent(out) :: nsquared
      nsquared = n*n
      return
      end subroutine POWERTWO

我用 gfortran 编译如下:

gfortran -m32 -dynamiclib ./tmp.f90 -o ./tmp.dylib

注意我的gfortran配置如下:

COLLECT_LTO_WRAPPER=/usr/local/lvm/gcc-5.2.0/libexec/gcc/x86_64-apple-darwin14.4.0/5.2.0/lto-wrapper
Target: x86_64-apple-darwin14.4.0
Configured with: ../configure --prefix=/usr/local/lvm/gcc-5.2.0 --enable-checking=release --with-gmp=/usr/local/lvm/gmp-6.0.0 --with-mpfr=/usr/local/lvm/mpfr-3.1.2 --with-mpc=/usr/local/lvm/mpc-1.0.3 --enable-languages=c,c++,fortran,objc,obj-c++ --with-isl=/usr/local/lvm/isl-0.14 --with-cloog=/usr/local/lvm/cloog-0.18.4
Thread model: posix
gcc version 5.2.0 (GCC)

在 dylib 所在的同一个文件夹中,我有一个 excel 2011 xslm 电子表格,在 VBA 中我输入了以下代码:

Declare Function powertwo CDecl Lib "tmp.dylib" (n As Long, ByVal nsquared As Long)

Function VBA_UDFPowerTwo(n As Long) As Long

    Dim res As Long
    res = 0
    Call powertwo(n, res)
    VBAUDFPowerTwo = res

End Function

现在,在单元格 A2 中执行公式 =VBA_UDFPowerTwo(A1) 得到 #VALUE!。如果我将 dylib 的整个路径放在 VBA 中,结果相同:

Declare Function powertwo CDecl Lib "/Users/XXXXXX/Documents/GITHUBRepos/DYLIBS/MyFirstDylib/tmp.dylib" (n As Long, ByVal nsquared As Long)

Declare Function powertwo CDecl Lib "Mackintosh HD:Users:XXXXXX:Documents:GITHUBRepos:DYLIBS:MyFirstDylib:tmp.dylib" (n As Long, ByVal nsquared As Long)

如果我将 ByVal 替换为 ByRef,则相同。甚至一个

Declare Function powertwo CDecl Lib "Mackintosh HD:Users:ludwigvonmises:Documents:GITHUBRepos:DYLIBS:MyFirstDylibt:tmp.dylib" Alias "POWERTWO" (n As Long, ByRef nsquared As Long)

没有让我开心。我是不是遗漏了什么或做错了什么?

我在 dylib 上做了一个 nm 并得到以下结果:

00000fa4 t ___x86.get_pc_thunk.ax
00000f87 T _powertwo_
         U dyld_stub_binder

nm -gU 给我:

00000f87 T _powertwo_

Console.app 在打开 excel 并触发 A2 单元格中的计算时告诉我以下内容:

22/08/15 19:37:32,892 Microsoft Excel[2971]: WARNING: The Gestalt selector gestaltSystemVersion is returning 10.9.5 instead of 10.10.5. Use NSProcessInfo's operatingSystemVersion property to get correct system version number.
Call location:
22/08/15 19:37:32,892 Microsoft Excel[2971]: 0   CarbonCore                          0x96a01291 ___Gestalt_SystemVersion_block_invoke + 135
22/08/15 19:37:32,893 Microsoft Excel[2971]: 1   libdispatch.dylib                   0x92c4f0b5 dispatch_once_f + 251
22/08/15 19:37:32,893 Microsoft Excel[2971]: 2   libdispatch.dylib                   0x92c500d8 dispatch_once + 31
22/08/15 19:37:32,893 Microsoft Excel[2971]: 3   CarbonCore                          0x9697a69d _Gestalt_SystemVersion + 1050
22/08/15 19:37:32,893 Microsoft Excel[2971]: 4   CarbonCore                          0x969797c0 Gestalt + 150
22/08/15 19:37:32,893 Microsoft Excel[2971]: 5   MicrosoftComponentPlugin            0x01bdb27e McpInitLibrary_ + 505
22/08/15 19:37:32,893 Microsoft Excel[2971]: 6   MicrosoftComponentPlugin            0x01bdb0ae McpInitLibrary_ + 41

otool -L tmp.dylib 显示如下:

./tmp.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/local/lvm/gcc-5.2.0/lib/i386/libgfortran.3.dylib (compatibility version 4.0.0, current version 4.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1213.0.0)
    /usr/local/lvm/gcc-5.2.0/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/local/lvm/gcc-5.2.0/lib/i386/libquadmath.0.dylib (compatibility version 1.0.0, current version 1.0.0)

编辑

求助于:

Declare Function powertwo_ CDecl Lib "Macintosh HD:Users:XXXXXX:Documents:GITHUBRepos:DYLIBS:MyFirstDylib:tmp.dylib" (n As Long, ByRef nsquared As Long)

Ken Thomases所建议,在单元格A2中触发计算时导致excel崩溃,这至少表明导出的函数名称不是powertwo而是powertwo_, excel 的 VBA 确实加载了 tmp.dylib。

您的尝试存在一些问题:

  • 过程参数不匹配

    Fortran 默认按引用传递,VBA 也是如此。在您多次尝试使界面正确时,您并没有完全匹配它们。

  • 程序不匹配return值

    您的 Fortran 过程是一个子例程,return没有价值。您需要将 VBA 过程声明为 Sub,而不是 Function

  • 使用 Intel Fortran 指令

    !DEC$ 指令 (afaik) 只是对 gfortran 的注释,未被解释。


这是我所做的,并测试了它是否有效。 Fortran 已使用 Fortran 2003 的 iso_c_binding C-interop 功能重新编写,以提供对导出过程接口的更多控制并确保您获得所需内容。您使用的是现代 Fortran 编译器,因此这对您来说不是问题。

Fortran:

subroutine POWERTWO (n, nsquared) bind(C, name='powertwo')
  use iso_c_binding, only: c_long
  implicit none
  integer(kind=c_long), intent(in) :: n
  integer(kind=c_long), intent(out) :: nsquared
  nsquared = n*n
  return
end subroutine POWERTWO 

这将生成一个带有 C 接口的过程

void powertwo(long int* n, long int* squared)

我用 gfortran 5.2(来自 macports)编译了这个:

gfortran-mp-5 -m32 -dynamiclib -o test32.dylib test.f90

在 Excel VBA 中,我已将程序声明为:

Declare Sub powertwo CDecl Lib "/Users/casey/code/so/xlstest/test32.dylib" (ByRef n As Long, ByRef nsquared As Long)

Function VBA_UDFPowerTwo(n As Long) As Long

    Dim res As Long
    res = 0
    Call powertwo(n, res)
    VBA_UDFPowerTwo = res

End Function

请注意,以上内容还修复了 VBAUDFPowerTwo = res 行中的错字,即 VBA 后缺少下划线(与函数名称不匹配)。我还将导入的函数更改为 Sub 并将其参数明确声明为 ByRef (这应该是默认值)。

这会产生所需的行为: