问题描述
我正在尝试将此c#代码转换为f#:
[DllImport("psapi.dll",SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetPerformanceInfo([Out] out Performanceinformation Performanceinformation,[In] int Size);
这是我到目前为止所拥有的:
[<DllImport("psapi.dll",SetLastError = true)>]
extern [<return: MarshalAs(UnmanagedType.Bool)>] bool GetPerformanceInfo(PerfInfo Performanceinformation,int Size)
传递必要参数的正确方法是什么?指针,byrefs或其他东西。 另外,[Out]和[In]属性在做什么?
编辑:我已经回答了一些问题,但还有几个问题。 我是否需要为Size参数指定一个[],一个inref,还是简单地推断出来?
解决方法
这是我研究的部分答案。
在大多数情况下,使用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* patch)
let mutable major,minor,patch = 0,0
getVersion(&&major,&&minor,&&patch)
printfn "Version: %i.%i.%i" major minor patch
但是,这不太正确。事实证明,在处理CLR时,有两种类型的指针:非托管和托管。后者是在按引用传递CLR类型时使用的内容(即F#中的“ byref ”或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,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代码。
作为额外的注释,必须将变量标记为可变的,以作为byref传递。传递具有可变属性的非可变对象是只读的inref。方便通过引用传递只读值类型。 F# ByRef and InRef