[转]用StructLayout特性限定声明结构或类

一、用StructLayout特性限定声明结构或类
  公共语言运行库利用StructLayoutAttribute控制类或结构的数据字段在托 管内存中的物理布局,即类或结构需要按某种方式排列。如果要将类传递给需要指定布局的非托管代码,则显式控制类布局是重要的。它的构造函数中用LayoutKind值初始化 StructLayoutAttribute 类的新实例。 LayoutKind.Sequential 用于强制将成 员按其出现的顺序进行顺序布局。
  LayoutKind.Explicit 用于控制每个数据成员的精确位置。利用 Explicit, 每个成员必须使用 FieldOffsetAttribute 指示此字段在类型中的位置。如:

  1. [StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)]
  2. public class MySystemTime
  3. {
  4. [FieldOffset(0)]public ushort wYear;
  5. [FieldOffset(2)]public ushort wMonth;
  6. [FieldOffset(4)]public ushort wDayOfWeek;
  7. [FieldOffset(6)]public ushort wDay;
  8. [FieldOffset(8)]public ushort wHour;
  9. [FieldOffset(10)]public ushort wMinute;
  10. [FieldOffset(12)]public ushort wSecond;
  11. [FieldOffset(14)]public ushort wMilliseconds;
  12. }


  下面是针对API中OSVERSIONINFO结构,在.net中定义对应类或结构的例子:
/**********************************************
* API中定义原结构声明
* OSVERSIONINFOA STRUCT
* dwOSVersionInfoSize DWORD ?
* dwMajorVersion DWORD ?
* dwMinorVersion DWORD ?
* dwBuildNumber DWORD ?
* dwPlatformId DWORD ?
* szCSDVersion BYTE 128 dup (?)
* OSVERSIONINFOA ENDS
*
* OSVERSIONINFO equ <OSVERSIONINFOA>
*********************************************/
//.net中声明为类

  1. [ StructLayout( LayoutKind.Sequential )]
  2. public class OSVersionInfo
  3. {
  4. public int OSVersionInfoSize;
  5. public int majorVersion;
  6. public int minorVersion;
  7. public int buildNumber;
  8. public int platformId;
  9. [ MarshalAs( UnmanagedType.ByValTStr, SizeConst=128 )]
  10. public String versionString;
  11. }


//或者
//.net中声明为结构

  1. [ StructLayout( LayoutKind.Sequential )]
  2. public struct OSVersionInfo2
  3. {
  4. public int OSVersionInfoSize;
  5. public int majorVersion;
  6. public int minorVersion;
  7. public int buildNumber;
  8. public int platformId;
  9. [ MarshalAs( UnmanagedType.ByValTStr, SizeConst=128 )]
  10. public String versionString;
  11. }


  此例中用到MashalAs特性,它用于描述字段、方法或参数的封送处理格式。用它作为参数前缀并指定目标需要的数据类型。例如,以下代码将两个参数作为数据类型长指针封送给 Windows API 函数的字符串 (LPStr):
[MarshalAs(UnmanagedType.LPStr)]
String existingfile;
[MarshalAs(UnmanagedType.LPStr)]
String newfile;
  注意结构作为参数时候,一般前面要加上ref修饰符,否则会出现错误:对象的引用没有指定对象的实例。

  1. [ DllImport( "kernel32", EntryPoint="GetVersionEx" )]
  2. public static extern bool GetVersionEx2( ref OSVersionInfo2 osvi ); 

 二、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 );

 原文:http://www.cnblogs.com/JessieDong/archive/2009/07/21/1527553.html


 

原文地址:https://www.cnblogs.com/linlf03/p/2202204.html