在MarshalAsAttribute中,第一個參數類型是UnmanagedType枚舉,用以指定對應的非管理的資料類型。在這個枚舉類型中,有兩個非常詭異的值,Unmanaged.LPStruct和UnmanagedType.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. |
先說Struct,MarshalAs(UnmanagedType.Struct)實際上並不是指定Marshal為Variant。MarshalAs(UnmanagedType.Struct)適用於大部分實值型別和object類型(不包括其他參考型別),兩種情況下,寫和不寫都沒有區別。當用於實值型別的情況下,CLR將對應的實值型別轉換成對應的非託管類型。而當用於object類型時,CLR則是將object轉換成VARIANT。文檔中指的當是後一種情況,顯然不夠準確。其實寫不寫MarshalAs(UnmanagedType.Struct)基本上沒有區別,對於可讀性也沒有任何的協助,反而由於文檔的錯誤和兩種不同的情況增加了複雜性,因此最好建議不要使用這個枚舉值。
比較詭異的是LpStruct,從字面上理解是傳Struct的指標。實際上,這個枚舉也只能用在兩種情況下:
1. Guid和Decimal
這裡的作用是告訴CLR多加一層引用。比如Guid對應的非託管類型是GUID,而[MarshalAs(UnmanagedType.LpStruct)] Guid的對應的非託管類型則是Guid*,要多一級指標。這個的作用是可以讓你避免使用ref Guid(對應的非託管類型也是GUID*),這樣可以讓函數原型看起來更加自然一些,特別是GUID*只是用來傳入的時候。還有就是在特殊的情況下,可以使用[MarshalAs(UnmanagedType.LpStruct)] ref Guid這樣的寫法來傳遞GUID**。
2. 一般參考型別,也就是class。和系統關係比較緊密的一些參考型別如String、StringBuilder、數組、委託等除外。
和Guid/Decimal不一樣的是,這種情況下寫和不寫沒有區別,,因為class本身就是按引用傳遞,是有一級指標的。如:
對應的非託管的類型為MyClass*。
由此可見,文檔本身並不沒有錯,但是基本上對編寫者沒有任何協助,說了等於沒說。
因為LpStruct這個枚舉值並不影響可讀性,而且在不同情況下行為不一樣(不過可以認為是一致的,只是稍微難以理解),文檔本身也沒有寫清楚用途。由於這些原因,以後很有可能CLR Interop小組會慢慢的停止對LpStruct的支援。我的建議是在程式中盡量不要使用LpStruct。
| 作者:張羿 (ATField) |
Blog: http://blog.csdn.net/atfield |
EMail: atfield_zhang@hotmail.com |
轉載請註明出處 |