StructLayout特性
StructLayout特性
公共語言運行庫利用StructLayoutAttribute控制類或結構的數據字段在託管內存中的物理佈局,即類或結構需要按某種方式排列。如果要將類傳遞給需要指定佈局的非託管代碼,則顯式控制類佈局是重要的。它的構造函數中用 LayoutKind值初始化 StructLayoutAttribute 類的新實例。 LayoutKind.Sequential 用於強制將成員按其出現的順序進行順序佈局。
StructLayout特性允許我們控制Structure語句塊的元素在內存中的排列方式,以及當這些元素被傳遞給外部DLL時,運行庫排列這些元素的方式。Visual Basic結構的成員在內存中的順序是按照它們出現在源代碼中的順序排列的,儘管編譯器可以自由的插入填充字節來安排這些成員,以便使得16位數值用子邊界對齊,32位數值用雙字邊界對齊。
使用這種排列(未壓縮佈局)提供的性能最佳。
在Visual Basic 6的用戶自定義結構是未壓縮的,而且我們不可以改變這一默認設置。在VB.NET中可以改變這種設置,並且可以通過System.Runtime.InteropServices.StructLayout 特性精確的控制每一個結構成員的位置。
System.Runtime.InteropServices.StructLayout 允許的值有StructLayout.Auto StructLayout.Sequential StructLayout.Explicit.
1.Sequential,順序佈局,比如
struct S1
{
int a;
int b;
}
那麼默認情況下在內存裏是先排a,再排b
也就是如果能取到a的地址,和b的地址,則相差一個int類型的長度,4字節
[StructLayout(LayoutKind.Sequential)]
struct S1
{
int a;
int b;
}
這樣和上一個是一樣的.因爲默認的內存排列就是Sequential,也就是按成員的先後順序排列.
2.Explicit,精確佈局
需要用FieldOffset()設置每個成員的位置
這樣就可以實現類似c的公用體的功能
[StructLayout(LayoutKind.Explicit)]
struct S1
{
[FieldOffset(0)]
int a;
[FieldOffset(0)]
int b;
}
這樣a和b在內存中地址相同
StructLayout特性支持三種附加字段:CharSet、Pack、Size。
· CharSet定義在結構中的字符串成員在結構被傳給DLL時的排列方式。可以是Unicode、Ansi或Auto。
默認爲Auto,在WIN NT/2000/XP中表示字符串按照Unicode字符串進行排列,在WIN 95/98/Me中則表示按照ANSI字符串進行排列。
· Pack定義了結構的封裝大小。可以是1、2、4、8、16、32、64、128或特殊值0。特殊值0表示當前操作平臺默認的壓縮大小。
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct LIST_OPEN
{
public int dwServerId;
public int dwListId;
public System.UInt16 wRecordSize;
public System.UInt16 wDummy;
public int dwFileSize;
public int dwTotalRecs;
public NS_PREFETCHLIST sPrefetch;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
public string szSrcMach;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 24)]
public string szSrcComp;
}
此例中用到MashalAs特性,它用於描述字段、方法或參數的封送處理格式。用它作爲參數前綴並指定目標需要的數據類型。
例如,以下代碼將兩個參數作爲數據類型長指針封送給 Windows API 函數的字符串 (LPStr):
[MarshalAs(UnmanagedType.LPStr)]
String existingfile;
[MarshalAs(UnmanagedType.LPStr)]
String newfile;
注意結構作爲參數時候,一般前面要加上ref修飾符,否則會出現錯誤:對象的引用沒有指定對象的實例。
[ DllImport( "kernel32", EntryPoint="GetVersionEx" )]
public static extern bool GetVersionEx2( ref OSVersionInfo2 osvi );