StructLayout特性
公共語(yǔ)言運(yùn)行庫(kù)利用StructLayoutAttribute控制類或結(jié)構(gòu)的數(shù)據(jù)字段在托管內(nèi)存中的物理布局,即類或結(jié)構(gòu)需要按某種方式排列。如果要將類傳遞給需要指定布局的非托管代碼,則顯式控制類布局是重要的。它的構(gòu)造函數(shù)中用 LayoutKind值初始化 StructLayoutAttribute 類的新實(shí)例。 LayoutKind.Sequential 用于強(qiáng)制將成員按其出現(xiàn)的順序進(jìn)行順序布局。
StructLayout特性允許我們控制Structure語(yǔ)句塊的元素在內(nèi)存中的排列方式,以及當(dāng)這些元素被傳遞給外部DLL時(shí),運(yùn)行庫(kù)排列這些元素的方式。Visual Basic結(jié)構(gòu)的成員在內(nèi)存中的順序是按照它們出現(xiàn)在源代碼中的順序排列的,盡管編譯器可以自由的插入填充字節(jié)來(lái)安排這些成員,以便使得16位數(shù)值用子邊界對(duì)齊,32位數(shù)值用雙字邊界對(duì)齊。
使用這種排列(未壓縮布局)提供的性能最佳。
通過(guò)System.Runtime.InteropServices.StructLayout 特性精確的控制每一個(gè)結(jié)構(gòu)成員的位置。
System.Runtime.InteropServices.StructLayout 允許的值有:
StructLayout.Auto
StructLayout.Sequential
StructLayout.Explicit
1.Sequential,順序布局,比如
struct S1
{
int a;
int b;
}
那么默認(rèn)情況下在內(nèi)存里是先排a,再排b
也就是如果能取到a的地址,和b的地址,則相差一個(gè)int類型的長(zhǎng)度,4字節(jié)
[StructLayout(LayoutKind.Sequential)]
struct S1
{
int a;
int b;
}
這樣和上一個(gè)是一樣的.因?yàn)槟J(rèn)的內(nèi)存排列就是Sequential,也就是按成員的先后順序排列.
2.Explicit,精確布局
需要用FieldOffset()設(shè)置每個(gè)成員的位置
這樣就可以實(shí)現(xiàn)類似c的公用體(union)的功能
[StructLayout(LayoutKind.Explicit)]
struct S1
{
[FieldOffset(0)] int a;
[FieldOffset(0)] int b;
}
這樣a和b在內(nèi)存中地址相同
StructLayout特性支持三種附加字段:CharSet、Pack、Size。
· CharSet定義在結(jié)構(gòu)中的字符串成員在結(jié)構(gòu)被傳給DLL時(shí)的排列方式。可以是Unicode、Ansi或Auto。
默認(rèn)為Auto,在WIN NT/2000/XP中表示字符串按照Unicode字符串進(jìn)行排列,在WIN 95/98/Me中則表示按照ANSI字符串進(jìn)行排列。
· Pack定義了結(jié)構(gòu)的封裝大小。可以是1、2、4、8、16、32、64、128或特殊值0。特殊值0表示當(dāng)前操作平臺(tái)默認(rèn)的壓縮大小。
[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特性,它用于描述字段、方法或參數(shù)的封送處理格式。用它作為參數(shù)前綴并指定目標(biāo)需要的數(shù)據(jù)類型。
例如,以下代碼將兩個(gè)參數(shù)作為數(shù)據(jù)類型長(zhǎng)指針?lè)馑徒o Windows API 函數(shù)的字符串 (LPStr):
[MarshalAs(UnmanagedType.LPStr)] String existingfile;
[MarshalAs(UnmanagedType.LPStr)] String newfile;
注意結(jié)構(gòu)作為參數(shù)時(shí)候,一般前面要加上ref修飾符,否則會(huì)出現(xiàn)錯(cuò)誤:對(duì)象的引用沒(méi)有指定對(duì)象的實(shí)例。
[ DllImport( "kernel32", EntryPoint="GetVersionEx" )]
public static extern bool GetVersionEx2( ref OSVersionInfo2 osvi );
string result;
IntPtr strPtr = new IntPtr() ;// unmanaged api TestStringAsResultIntPrt(1);
result = Marshal.PtrToStringUni(strPtr);
result = Marshal.PtrToStringAnsi(strPtr);
Marshal.FreeCoTaskMem(strPtr);