F# syntax for P/Invoke signature using MarshalAs

后端 未结 4 776
忘了有多久
忘了有多久 2020-12-10 16:41

I\'m unsure of the syntax for this. I\'m trying to translate this C# code into F#.

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


        
相关标签:
4条回答
  • 2020-12-10 16:51

    In addition to Brian's comment, it may be worth pointing out that F# extern signatures reflect C signatures fairly faithfully, so that rather than using the [<In>][<Out>] attributes on the reference you can probably just declare the parameter as LASTINPUTINFO* plii, and then pass a reference to a local instance using the && operator when calling the function.

    0 讨论(0)
  • 2020-12-10 16:59

    Honestly I haven't tried to run or use this, but this compiles, and hopefully will steer you in the right direction.

    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)
    
    0 讨论(0)
  • 2020-12-10 17:10

    In addition to kvb's comment, I found that declaring parameters as pointers messes up the current FSharp Power Tools refactor engine. You can use an IntPtr to get around that:

    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
    
    0 讨论(0)
  • 2020-12-10 17:10

    An updated answer:

    For the most part, when using P/Invoke, one may simply copy-and-paste the signatures from a C header file (sans-semi-colons, of course). However, there is at least one scenario where naïvely doing so produces code which is not verifiably type-safe. Let’s look at a specific example. Given the follow function prototype in C:

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

    One might use the following P/Invoke signature (and associated call) in 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
    

    However, that isn't quite right. Turns out, when dealing with the CLR, there are two types of pointers: unmanaged and managed. The latter are what you use when passing around CLR types by-reference (i.e. “byref<‘T>“ in F#, or “ref“ in C#, or “ByRef“ in VB). It also happens that you should use the managed variety if you want your F# code to be verifiably type-safe — and this includes P/Invoke calls. If you think about it, this makes sense. The runtime can only guarantee the bits it can control (i.e. the parts which are “managed”). So here’s what the F# code looks like using managed pointers instead:

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

    Handy table:

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

    In nearly all cases, a .NET developer should prefer the managed pointer. Leave the unmanaged risks with the C code.

    Edited Source : P/Invoke Gotcha in f#

    As an extra note, to be passed in as a byref the variable must be marked as mutable. Passing a non mutable object, even with mutable properties, is a readonly inref. Handy for passing read only value types by reference. F# ByRef and InRef

    0 讨论(0)
提交回复
热议问题