arrays - 从 C# 到 F# : Pinned Array and Stringbuilder in external function called from F#

标签 arrays f# pinvoke external pass-by-reference

我正在尝试从 F# 访问外部 DLL 函数。
这个真的让我汗流浃背。

C头文件是:

ext_def(int32) swe_calc_ut(double tjd_ut, int32 ipl, int32 iflag, 
double *xx, char *serr);

我相应地在 F# 中导入了它:
extern int32 ext_swe_calc_ut(double tjd_ut, int32 ipl, int32 iflag, double *xx, StringBuilder serr);

问题是数组部分。我尝试了 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:
(*

“FSharp.Core,版本=2.0.0.0,文化=中性,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)

程序此时崩溃(控制台窗口消失,在调试中,它只是跳回第一行。

我知道我可以在“let argx = PinnedArray.of_array ar”中使用“use”而不是“let”,但是由于顶部的模块声明,编译器不会让我拥有它。

在 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;
    }

也许整个问题可以追溯到 FileLoad 异常(即使它没有显示在 dll 调用中) - 可能是因为 FSharp Powerpack?

非常感谢您的帮助。

最佳答案

这是我的猜测,基于 C 类型签名和 function documentation我发现。它可以编译,但你必须告诉我它是否真的有效,因为我没有 swedll32.dll部件。此外,我的代码不需要 F# Powerpack 程序集。

编辑:只需阅读@kvb 答案下的评论——他提出了我在代码中包含的相同内容,即 [<MarshalAs(...)>] SizeConst 的属性属性集。我还添加了 [<Out>] , 因为最后两个参数被用作返回值; IIRC,它也会影响编码行为。

编辑 2:删除了 ExactSpelling = true来自 MarshalAs属性,根据@wolfgang 的报告。

[<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

关于arrays - 从 C# 到 F# : Pinned Array and Stringbuilder in external function called from F#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12622160/

相关文章:

java - 读入文件以创建二维数组单词搜索难题并解决

javascript - 在 Javascript 中查找最接近所需值的数组条目?

f# - F# lambda vs Func 的重载解析

f# - 没有分配给领域

c# - Wininet 缓存 API 在 Windows 8 中挂起

c# - 另一个 PInvoke - 错误消息显示未找到源?

c++ - 理解 char array[] 和 string

c - 如何在 C 中创建字符串数组?

f# - F#标准库中是否有生成常量函数的函数?

c# - PInvoke WPF HWND 和 const void*