f#p /在带有指针的GetClientRect上调用,但不能与OutAttribute一起调用

Rei*_*aka 2 pinvoke f#

我可以让这个工作:

[<DllImport("user32.dll")>]
extern bool GetClientRect(nativeint, RECT*)

let getClientRect hwnd =
    let mutable r = RECT()
    if GetClientRect(hwnd, &&r) = false then
        raise <| System.Exception("GetClientRect failed")
    r
Run Code Online (Sandbox Code Playgroud)

但无论出于何种原因,这只会留下r零,没有异常抛出:

[<DllImport("user32.dll")>]
extern bool GetClientRect(nativeint, [<Out>] RECT rect)

let getClientRect hwnd =
    let mutable r = RECT()
    if GetClientRect(hwnd, r) = false then
        raise <| System.Exception("GetClientRect failed")
    r
Run Code Online (Sandbox Code Playgroud)

当然,使用指针的问题在于我得到警告warning FS0051: The use of native pointers may result in unverifiable .NET IL code,这是可以理解的.

我能错过什么?

编辑:即使已经回答了记录,结构定义:

[<Struct>]
type RECT =
    val left:int
    val top:int
    val right:int
    val bottom:int
Run Code Online (Sandbox Code Playgroud)

Jac*_* P. 5

它没有很好地记录,但在F#P/Invoke签名中使用&符号是通过引用传递值的正确(和最佳)方式.它编译为与C#ref参数相同的类型签名; 添加[<Out>]到参数会给出与C#out参数相同的签名.

[<DllImport("user32.dll")>]
extern bool GetClientRect(nativeint hWnd, [<Out>] RECT& rect)

let getClientRect hwnd =
    let mutable r : RECT = Unchecked.defaultOf<_>
    if GetClientRect(hwnd, &r) = false then
        raise <| System.Exception("GetClientRect failed")
    r
Run Code Online (Sandbox Code Playgroud)

请注意,一旦您更改要使用的P/Invoke签名byref<RECT>而不是nativeptr<RECT>,您还需要将您使用的运算符地址(&&)r更改为单个&符号(&).

既然你只得到一个值出来GetClientRect,这是一个很好的做法,初始化可变的结果值Unchecked.defaultOf<_>那么很明显,它的意思被覆盖GetClientRect.

编辑:如果更改P/Invoke签名以使用byref(&)不起作用,您还可以尝试在返回类型上显式指定编组行为.GetClientRect的MSDN文档说它返回一个BOOL,它与.NET bool不完全相同(BOOL是一个4字节的整数值).

如果你想尝试:

[<DllImport("user32.dll")>]
extern [<return: MarshalAs(UnmanagedType.Bool)>] bool GetClientRect(
    nativeint hWnd, [<Out>] RECT& rect)
Run Code Online (Sandbox Code Playgroud)