MSDN文档的错误:关于UnmanagedType.LPStruct和UnmanagedType.Struct

MarshalAsAttribute中,第一个参数类型是UnmanagedType枚举,用以指定对应的非托管数据类型。在这个枚举类型中,有两个非常诡异的值,Unmanaged.LPStructUnmanagedType.Struct。对于这两个枚举值,在MSDN文档中是这么讲的:

Struct

A VARIANT, which is used to marshal managed formatted classes and value types.

LPStruct

A pointer to a C-style structure that you use to marshal managed formatted classes. Valid for platform invoke methods only.

先说StructMarshalAs(UnmanagedType.Struct)实际上并不是指定MarshalVariantMarshalAs(UnmanagedType.Struct)适用于大部分值类型和object类型(不包括其他引用类型),两种情况下,写和不写都没有区别。当用于值类型的情况下,CLR将对应的值类型转换成对应的非托管类型。而当用于object类型时,CLR则是将object转换成VARIANT。文档中指的当是后一种情况,显然不够准确。其实写不写MarshalAs(UnmanagedType.Struct)基本上没有区别,对于可读性也没有任何的帮助,反而由于文档的错误和两种不同的情况增加了复杂性,因此最好建议不要使用这个枚举值。

比较诡异的是 LpStruct ,从字面上理解是传 Struct 的指针。实际上,这个枚举也只能用在两种情况下:

1. GuidDecimal

这里的作用是告诉CLR多加一层引用。比如Guid对应的非托管类型是GUID,而[MarshalAs(UnmanagedType.LpStruct)] Guid的对应的非托管类型则是Guid*,要多一级指针。这个的作用是可以让你避免使用ref Guid(对应的非托管类型也是GUID*),这样可以让函数原型看起来更加自然一些,特别是GUID*只是用来传入的时候。还有就是在特殊的情况下,可以使用[MarshalAs(UnmanagedType.LpStruct)] ref Guid这样的写法来传递GUID**

2. 一般引用类型,也就是class。和系统关系比较紧密的一些引用类型如StringStringBuilder、数组、委托等除外。

Guid/Decimal不一样的是,这种情况下写和不写没有区别,,因为class本身就是按引用传递,是有一级指针的。如:

class MyClass
{
int b;
}

对应的非托管的类型为MyClass*

由此可见,文档本身并不没有错,但是基本上对编写者没有任何帮助,说了等于没说。

因为LpStruct这个枚举值并不影响可读性,而且在不同情况下行为不一样(不过可以认为是一致的,只是稍微难以理解),文档本身也没有写清楚用途。由于这些原因,以后很有可能CLR Interop小组会慢慢的停止对LpStruct的支持。我的建议是在程序中尽量不要使用LpStruct。

作者:张羿 (ATField) Blog: http://blog.csdn.net/atfield EMail: [email protected] 转载请注明出处

你可能感兴趣的:(.net,Blog)