【问题标题】:Proper use of P/invoke with pointers in F#在 F# 中正确使用带有指针的 P/invoke
【发布时间】:2020-09-11 04:57:41
【问题描述】:

我正在尝试将此 c# 代码转换为 f#:

[DllImport("psapi.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetPerformanceInfo([Out] out PerformanceInformation PerformanceInformation, [In] int Size);

这是我目前所拥有的:

[<DllImport("psapi.dll", SetLastError = true)>]
extern [<return: MarshalAs(UnmanagedType.Bool)>] bool GetPerformanceInfo(PerfInfo PerformanceInformation, int Size)

传递必要参数的正确方法是什么?指针、byrefs 或其他东西。 另外,[Out] 和 [In] 属性是做什么的?

编辑:我已经回答了一些问题,但仍有几个问题未解决。 我需要为 Size 参数指定一个 []、一个 inref 还是只是推断出来的?

【问题讨论】:

    标签: .net .net-core f# c#-to-f#


    【解决方案1】:

    这是我研究的部分答案。

    在大多数情况下,使用 P/Invoke 时,可以简单地从 C 头文件中复制并粘贴签名(当然,没有分号)。但是,至少在一种情况下,天真地这样做会产生无法验证类型安全的代码。我们来看一个具体的例子。给定 C 中的以下函数原型:

    __declspec(dllexport) void getVersion (int* major, int* minor, int* patch);
    

    可以在 F# 中使用以下 P/Invoke 签名(和相关调用):

    [<DllImport("somelib",CallingConvention=CallingConvention.Cdecl)>]
    extern void getVersion (int* major, int* minor, int* patch)
    
    let mutable major,minor,patch = 0,0,0
    getVersion(&&major,&&minor,&&patch)
    printfn "Version: %i.%i.%i" major minor patch
    

    但是,这并不完全正确。事实证明,在处理 CLR 时,有两种类型的指针:非托管和托管。后者是您在通过引用传递 CLR 类型时使用的(即 F# 中的“byref”,C# 中的“ref”,或 VB 中的“ByRef”)。如果您希望您的 F# 代码是可验证的类型安全的,那么您也应该使用托管类型——这包括 P/Invoke 调用。如果你仔细想想,这是有道理的。运行时只能保证它可以控制的位(即“管理”的部分)。下面是使用托管指针代替 F# 代码的样子:

    [<DllImport("somelib",CallingConvention=CallingConvention.Cdecl)>]
    extern void getVersion (int& major, int& minor, int& patch)
    
    let mutable major,minor,patch = 0,0,0
    getVersion(&major,&minor,&patch)
    printfn "Version: %i.%i.%i" major minor patch
    

    方便的桌子:

    Pointer    F#             Type Declaration      Invocation
    Unmanaged  nativeint      <type>*               &&<type>
    Managed    byref <type>   <type>&               &type
    

    在几乎所有情况下,.NET 开发人员都应该更喜欢托管指针。将无法管理的风险留给 C 代码。

    编辑来源:P/Invoke Gotcha in f#

    作为额外说明,要作为 byref 传入,变量必须标记为可变。传递一个非可变对象,即使具有可变属性,也是一个只读的 inref。通过引用传递只读值类型很方便。 F# ByRef and InRef

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-07
    • 2013-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多