问题
I mainly tried to mimic this in an even simpler setting. No luck.
I wrote a simple fortran code :
subroutine POWERTWO (n, nsquared)
!DEC$ ATTRIBUTES DLLEXPORT :: POWERTWO
integer, intent(in) :: n
integer, intent(out) :: nsquared
nsquared = n*n
return
end subroutine POWERTWO
that I compiled with gfortran as follows :
gfortran -m32 -dynamiclib ./tmp.f90 -o ./tmp.dylib
Note that my gfortran
is configured as follows :
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)
In the same folder where the dylib is, I have an excel 2011 xslm spreadsheet, and in the VBA I put the following code :
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
Now, doing in cell A2 the formula =VBA_UDFPowerTwo(A1)
gives me a #VALUE!
. Same result if I put the whole path to the dylib in the VBA as :
Declare Function powertwo CDecl Lib "/Users/XXXXXX/Documents/GITHUBRepos/DYLIBS/MyFirstDylib/tmp.dylib" (n As Long, ByVal nsquared As Long)
or as
Declare Function powertwo CDecl Lib "Mackintosh HD:Users:XXXXXX:Documents:GITHUBRepos:DYLIBS:MyFirstDylib:tmp.dylib" (n As Long, ByVal nsquared As Long)
and same if I replace ByVal
by ByRef
. Even a
Declare Function powertwo CDecl Lib "Mackintosh HD:Users:ludwigvonmises:Documents:GITHUBRepos:DYLIBS:MyFirstDylibt:tmp.dylib" Alias "POWERTWO" (n As Long, ByRef nsquared As Long)
did not make my day. Am I missing something or doing something wrong ?
I did a nm
on the dylib an get the following :
00000fa4 t ___x86.get_pc_thunk.ax
00000f87 T _powertwo_
U dyld_stub_binder
whereas nm -gU
gives me :
00000f87 T _powertwo_
Console.app told me the following while excel was opened and calculation in the A2 cell triggered :
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
and otool -L tmp.dylib
showed the following :
./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)
EDIT
Resorting to :
Declare Function powertwo_ CDecl Lib "Macintosh HD:Users:XXXXXX:Documents:GITHUBRepos:DYLIBS:MyFirstDylib:tmp.dylib" (n As Long, ByRef nsquared As Long)
as Ken Thomases suggested, made excel crash when calculation triggered in cell A2, which shows at least that the exported function name is not powertwo
but powertwo_
, and that excel's VBA loads tmp.dylib indeed.
回答1:
Your attempt at doing this had a few issues:
mismatch in the procedure arguments
Fortran passes by reference by default, as does VBA. In your multiple attempts at getting the interface right you didn't quite match them up.
mismatch in the procedure return value
Your Fortran procedure is a subroutine, which returns no value. You need to declare the VBA procedure as a
Sub
, not aFunction
.Use of Intel Fortran directives
The
!DEC$
directives (afaik) are just comments to gfortran and not interpreted.
Here is what I've done, and tested that it works. The Fortran has been re-written using the iso_c_binding
C-interop features of Fortran 2003 to give more control over the exported procedure interface and ensure that you get what you want. You are using a modern Fortran compiler, so this is not an issue for you.
The 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
This will produce an procedure with the C interface
void powertwo(long int* n, long int* squared)
I compiled this with gfortran 5.2 (from macports):
gfortran-mp-5 -m32 -dynamiclib -o test32.dylib test.f90
In Excel VBA, I have declared the procedure as:
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
Note the above also fixes a typo in your line VBAUDFPowerTwo = res
, which is missing an underscore after VBA (doesn't match the Function name). I have also changed the imported function to be a Sub
and explicitely declared its arguments as ByRef
(which should be the default).
This produces the desired behavior:
来源:https://stackoverflow.com/questions/32158814/c-dylib-curiously-not-found-by-excels-2011-vba-on-mac