mar*_*est 5 arrays pinvoke f# external pass-by-reference
我正在尝试从F#访问外部DLL函数.这个让我真的很汗.
C头是:
ext_def(int32) swe_calc_ut(double tjd_ut, int32 ipl, int32 iflag,
double *xx, char *serr);
Run Code Online (Sandbox Code Playgroud)
我相应地在F#中导入了它:
extern int32 ext_swe_calc_ut(double tjd_ut, int32 ipl, int32 iflag, double *xx, StringBuilder serr);
Run Code Online (Sandbox Code Playgroud)
问题是阵列部分.我尝试了来自F#Powerpack的PinnedArray,但是呼叫仍然失败.char数组可能没问题,即使我无法检查,因为调用失败了.
到目前为止它是:
open System
open System.Runtime.InteropServices
open System.Text
open Microsoft.FSharp.NativeInterop
#r "FSharp.PowerPack.dll"
#nowarn "51"
module Sweph =
[<DllImport(@"swedll32.dll", CharSet = CharSet.Ansi, EntryPoint = "swe_calc_ut")>]
extern int32 ext_swe_calc_ut(double tjd_ut, int32 ipl, int32 iflag, double *xx, StringBuilder serr);
/// <param name="jdnr">Julian day</param>
/// <returns>Array with 6 doubles: 0:longitude, 1:latitude, 2:distance,3:speel in longitude,
/// 4: speed in latitude, 5: speed in distance </returns>
let ar: double array = Array.zeroCreate 6
let argx = PinnedArray.of_array ar
printfn " ar: %A" ar
// This fails with a "FileLoadException"
printfn "ar: %A" argx
// Details of FileLoadException:
(*
Run Code Online (Sandbox Code Playgroud)
"FSharp.Core,Version = 2.0.0.0,Culture = neutral,PublicKeyToken = b03f5f7f11d50a3a"不匹配程序集(Ausnahme von HRESULT:0x80131040)*)
// However, if I leave the printfn for argx out, code continues and does not display this error again.
let sb = new StringBuilder(50) // Vgl. Expert F#, S. 515
Console.ReadKey(true)
// Application crashes here:
let ret_calc = Sweph.ext_swe_calc_ut(4700.0,1,1,argx.Ptr,sb)
Run Code Online (Sandbox Code Playgroud)
程序在此时崩溃(控制台窗口消失,在调试中,它只是跳回到第一行.
我知道我可以使用"use"代替"let"和"let argx = PinnedArray.of_array ar",但编译器不会因为顶部的模块声明而让我拥有它.
C#中有一个实现,如下所示:
public static double[] getPlanet(int ipl, double jdnr) {
// String ephePath = "Q:\\sweph\\";
// Sweph.setEphePath(ephePath);
double[] xx2 = new double[8];
double[] xx = new double[6];
String serr = "";
int iflag = Constants.SEFLG_SPEED;
long iflgret = ext_swe_calc_ut(jdnr, ipl, iflag, xx, serr);
for (int i = 0; i < 6; i++) {
xx2[i] = xx[i];
}
iflag = Constants.SEFLG_SWIEPH | Constants.SEFLG_SPEED | Constants.SEFLG_EQUATORIAL;
iflgret = ext_swe_calc_ut(jdnr, ipl, iflag, xx, serr);
xx2[6] = xx[0];
xx2[7] = xx[1];
return xx2;
}
Run Code Online (Sandbox Code Playgroud)
也许整个问题都会回到FileLoad异常(即使它没有在dll调用中显示) - 可能是因为FSharp Powerpack?
非常感谢你的帮助.
这是我的猜测,基于 C 类型签名和我找到的函数文档。它可以编译,但你必须告诉我它是否真的有效,因为我没有swedll32.dll程序集。另外,我的代码不需要 F# Powerpack 程序集。
编辑:只需阅读 @kvb 的答案下的评论 - 他建议我在代码中包含相同的内容,即[<MarshalAs(...)>]具有SizeConst属性集的属性。我还加上了[<Out>],因为最后两个参数是作为返回值的;IIRC,它也会影响编组行为。
编辑 2:根据 @wolfgang 的报告,ExactSpelling = true从属性中删除了。MarshalAs
[<DllImport(@"swedll32.dll",
CharSet = CharSet.Ansi,
EntryPoint = "swe_calc_ut")>]
extern int32 swe_calc_ut (
float tjd_ut,
int32 ipl,
int32 flag,
[<Out; MarshalAs(UnmanagedType.LPArray, SizeConst = 6)>]
float[] xx,
[<Out; MarshalAs(UnmanagedType.LPStr, SizeConst = 256)>]
System.Text.StringBuilder serr)
// Wrapper function for swe_calc_ut
let swe_calc_ut_wrapper (tjd_ut, ipl, flag) =
let positions = Array.zeroCreate 6
let errorSB = System.Text.StringBuilder (256)
let result = swe_calc_ut (tjd_ut, ipl, flag, positions, errorSB)
if result < 0 then
// Error
Choice2Of2 (result, errorSB.ToString ())
else
Choice1Of2 positions
Run Code Online (Sandbox Code Playgroud)