我可以让这个工作:
[<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)
它没有很好地记录,但在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)