2

我可以让它工作:

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

但无论出于何种原因,这只会留下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

当然,使用指针的问题是我得到了警告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
4

1 回答 1

5

它没有很好的文档记录,但是在 F# P/Invoke 签名中使用与号 (&) 符号是通过引用传递值的正确(也是最佳)方式。ref它编译为与 C#参数相同的类型签名;添加[<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

请注意,一旦您将 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)
于 2012-06-22T02:40:17.417 回答