我正在进行一个来自F#的平台调用调用,我收到一个编译器错误,我真的无法理解这一点。首先,让我展示我正在做的事情的C签名:
int Foo(
ULONG_PTR *phHandle,
DWORD flags
);
在F#中,我认为本地调用它的正确方法是:
[<DllImport("somedll.dll")>]
static extern int APlatformInvokeCall
(
[<Out>]nativeint& phHandle,
uint32 flags
)
如果我尝试在类中调用此,则在调用它时会出现编译错误:
type Class1() =
[<DllImport("somedll.dll")>]
static extern int APlatformInvokeCall
(
nativeint& phHandle,
uint32 flags
)
member this.Foo() =
let mutable thing = nativeint 0
APlatformInvokeCall(&thing, 0u) |> ignore
thing
错误是:
类型实例化涉及byref类型。 Common IL的规则不允许这样做。
奇怪的是,当我在一个模块中完成所有操作时,编译错误就会消失:
module Module1 =
[<DllImport("somedll.dll")>]
extern int APlatformInvokeCall
(
nativeint& phHandle,
uint32 flags
)
let Foo() =
let mutable thing = nativeint 0
APlatformInvokeCall(&thing, 0u) |> ignore
thing
为什么要编译为模块,而不是作为类编译?
答案 0 :(得分:2)
我不认为在F#中的类中定义extern
方法是有效的。
如果您调高F# 3.0 language specification并搜索DllImport
,则底部附近会列出一些特殊属性以及如何使用它们。 [<DllImport>]
的文字说:
当应用于模块中的函数定义时,会导致F#编译器忽略定义的实现,而是将其编译为CLI P / Invoke存根声明。
这似乎表明它只对在模块中定义的函数声明extern
方法(使用[<DllImport>]
)有效;但它并没有对班级成员说些什么。
我认为您正在遇到编译器错误。请将此代码提交给fsbugs@microsoft.com
,以便他们可以修复编译器发出的错误消息 - 它确实会给您一个关于在类中定义extern
方法的错误,因为它不是语言规范允许。
答案 1 :(得分:2)
这是否是一个不能承受的错误,也许这就是正在发生的事情:如果APlatformInvokeCall
被认为是静态成员函数,那么该成员只有一个元组类型的参数。元组被编译为泛型类型的对象(请参阅底部的here或the spec中的5.1.3)。在这种情况下,元组是
System.Tuple<nativeint&, uint32>
但是ECMA 335 II.9.4说你不能在byref
类型实例化泛型类型。这解释了报告的错误。
这个解释符合上面提到的事实Class1
如果你修改extern声明并调用取而代之的是一个参数,那么(好吧,编译)。它也符合模块版本的工作原理,因为在该版本中没有考虑APlatFormInvokeCall
成员函数。
答案 2 :(得分:1)
简单的解决方案是检查规范,这里是类定义语法:
type type-name pat_opt as-defn)opt =
class
class-inherits-decl_opt
class-function-or-value-defns_opt
type-defn-elements
end
然后我们
class-function-or-value-defn :
attributes_opt staticopt let rec_opt function-or-value-defns
attributes_opt staticopt do expr
不允许使用extern。
和
type-defn-element :
member-defn
interface-impl
interface-signature
这也不是你想要的。
因此,我们可以看到在您尝试使用它时使用extern
无法在类中完成。