【问题标题】:c++ dylib curiously not found by excel's 2011 VBA on macc++ dylib 奇怪地没有在 mac 上的 excel 的 2011 VBA 中找到
【发布时间】:2015-08-22 17:18:56
【问题描述】:

我主要尝试在更简单的设置中模仿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!。如果我将整个路径放在 VBA 中的 dylib 中,结果相同:

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_

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

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。

【问题讨论】:

  • 当 Excel 尝试加载您的插件时,它是否将任何内容写入控制台日志?您可以查看 /Applications/Utilities/Console.app。 otool -L tmp.dylib 显示什么?我怀疑它依赖于不在标准搜索路径中的库(请参阅dyld(1) man page)。
  • @KenThomases 感谢您对这样一个微本地化主题的兴趣!我在问题的末尾添加了 Console.app 的输出(以前从未使用过,所以我可能做错了)和otool -L tmp.dylib 的输出,供您查看。顺便说一句,我没有加载任何插件,我只是编写了一个在 .dylib 中编译的 .f90 文件,正如我在问题中所描述的那样。
  • 嗯。控制台输出可能是良性的,并且与显然无法正常工作的插件无关。您可以在尝试使用您的插件后使用vmmap -w -interleaved <pid of Excel> 检查Excel 进程,以查看tmp.dylib 是否已成功加载到进程中。我注意到nm 输出有一个下划线前缀和后缀_powertwo_。前缀对于 C 符号是正常的,例如,dlsym() 会处理它。后缀似乎不寻常。对Declare Function powertwo_ …有效吗?
  • 这里的大多数 Fortran 人员不会检查所有版本和编译器标记(fortran90gfortran 等...) - 您应该考虑标记 fortran 而不是 gcc5gfortran。有关更多信息,请参阅此内容:meta.stackoverflow.com/questions/261912/…
  • @Ross 完成。即使我不完全确定问题与 fortran 相关。无论如何。

标签: excel vba macos fortran dylib


【解决方案1】:

您尝试这样做时遇到了一些问题:

  • 过程参数不匹配

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

  • 过程返回值不匹配

    您的 Fortran 过程是一个子例程,它不返回任何值。您需要将 VBA 过程声明为 Sub,而不是 Function

  • 英特尔 Fortran 指令的使用

    !DEC$ 指令 (afaik) 只是 gfortran 的 cmets,而不是解释。


这是我所做的,并测试了它是否有效。 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(这应该是默认值)。

这会产生所需的行为:

【讨论】:

  • 我用哪些标志编译这个?我猜-m32应该被替换为-arch i386,这也适用于ifort,但是-dynamiclib呢?
  • 好的,结果一样,导致excel崩溃。
  • 使用你建议我使用的 fortran 代码,是的。并使用此声明:Declare Function powertwo CDecl Lib "Macintosh HD:Users:XXXXXX:Documents:GITHUBRepos:DYLIBS:MyFirstDylib:tmp.dylib" (n As Long, ByRef nsquared As Long) 删除 CDecl 会得到相同的结果。函数调用如下:Function VBA_UDFPowerTwo(n As Long) As LongDim res As Longres = 0Call powertwo(n, res)VBAUDFPowerTwo = resEnd Function
  • 出于好奇,你有没有在你的 mac 上试过这个?
  • @user10000100_u 我已经编辑了一个完整的、经过测试的示例来解决您的问题。请看一下。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-29
相关资源
最近更新 更多