使用MarshalAs进行P / Invoke签名的F#语法

时间:2009-11-06 18:35:33

标签: winapi f# pinvoke marshalling

我不确定这个的语法。我正在尝试将这个C#代码翻译成F#。

struct LASTINPUTINFO
{
    public uint cbSize;
    public uint dwTime;
}

public class IdleTimer
{
    [DllImport("User32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
}

这是我到目前为止所做的。

type LASTINPUTINFO = {
    cbSize : UInt32;
    dwTime : UInt32;
}

type IdleTimer =
    [<DllImport("User32.dll")>]
    [<return: MarshalAs(UnmanagedType.Bool)>]
    extern GetLastInputInfo(plii : LASTINPUTINFO ref)

4 个答案:

答案 0 :(得分:10)

除了Brian的评论之外,可能值得指出的是,F#extern签名相当忠实地反映了C签名,因此您可以将参数声明为{{[<In>][<Out>],而不是在引用上使用LASTINPUTINFO* plii属性。 1}},然后使用&amp;&amp ;;传递对本地实例的引用调用函数时的运算符。

答案 1 :(得分:8)

老实说,我没有尝试过运行或使用它,但这会编译,并希望能引导你朝正确的方向发展。

open System
open System.Runtime.InteropServices 

[<Struct>]
type LASTINPUTINFO = 
    val cbSize : UInt32
    val dwTime : UInt32

module IdleTimer =
    [<DllImport("User32.dll")>]
    extern [<MarshalAs(UnmanagedType.Bool)>] bool GetLastInputInfo([<In>][<Out>] LASTINPUTINFO plii)

答案 2 :(得分:3)

除了kvb的评论之外,我发现将参数声明为指针会混淆当前的FSharp Power Tools重构引擎。您可以使用IntPtr来解决这个问题:

open System
open System.Runtime.InteropServices 
open Microsoft.FSharp.NativeInterop

[<Struct>]
type LASTINPUTINFO = 
  val mutable cbSize : uint32
  val dwTime : uint32

[<DllImport("user32.dll")>]
extern bool GetLastInputInfo(IntPtr p)

let getLastInputTime() = 
  let mutable time = LASTINPUTINFO(cbSize = 8u)
  GetLastInputInfo(NativePtr.toNativeInt &&time) |> ignore
  time.dwTime

答案 3 :(得分:0)

最新答案:

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

__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 <'T>”或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