C#在调用WInAPI函数时,可能会看到如下的声明
1 [StructLayout(LayoutKind.Sequential)]
2 public struct RECT
3 {
4 public int Left;
5 public int Top;
6 public int Right;
7 public int Bottom;
8 }
在类或者结构体前面带上了
[StructLayout(LayoutKind.Sequential)]
StructLayoutAttribute特性的作用是允许你控制内存中类或结构的数据字段的物理布局。
平常我们在C#代码中使用类或者结构体时,不需要使用此特性。但在与非托管代码时交互,需要使用StructLayoutAttribute特性来控制类型的非托管布局。
StructLayoutAttribute常用构造函数是:
StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind)
System.Runtime.InteropServices.LayoutKind是一个枚举类型,有三个取值。
LayoutKind.Sequential
强制按成员的显示顺序对其进行排列。对于blittable 类型,在托管和非托管内存中控制布局。对于non -blittable类型,它会在将类或者结构体封送到非托管代码时控制布局(换言之,如果仅在C#中进行调用,不会做任何操作,在与非托管代码交互时,仅控制送入非托管代码的布局)
LayoutKind.Explicit
控制每个数据成员的精确位置,这会影响托管和非托管代码中的布局,不管是blittable 类型还是**non** -blittable 类型。(Blittable and Non-Blittable Types介绍),使用LayoutKind.Explicit时,需要使用FieldOffsetAttribute特性指示类型中每个字段的位置。
默认情况下,编译器会将LayoutKind.Sequential 应用到结构体,对于类,需要显式应用LayoutKind.Sequential值。
到这里也就明白了,以后在调用API函数时,如果使用的是结构体,就不再需要下面这句代码了。
[StructLayout(LayoutKind.Sequential)]
下面用示例代码说明一下
这里以获取桌面窗体的宽高为例,需要用到的API函数是
1 HWND GetDesktopWindow();
3 BOOL GetWindowRect(HWND hWnd, LPRECT lpRect );
其中LPRECT是指向RECT结构体的指针,RECT结构声明如下:
1 typedef struct tagRECT {
2 LONG left;
3 LONG top;
4 LONG right;
5 LONG bottom;
6 } RECT, *PRECT, *NPRECT, *LPRECT;
使用结构体时,调用代码如下:
1 using System;
2 using System.Runtime.InteropServices;
3
4 namespace ConsoleApp10
5 {
6 public struct RECT
7 {
8 public int Left;
9 public int Top;
10 public int Right;
11 public int Bottom;
12 }
13
14 class Program
15 {
16 [DllImport("user32.dll")]
17 private static extern IntPtr GetDesktopWindow();
18
19 [DllImport("user32.dll", SetLastError = true)]
20 private static extern int GetWindowRect(IntPtr hwnd, out RECT rc);
21
22 static void Main(string[] args)
23 {
24 IntPtr hwnd = GetDesktopWindow();
25 RECT rect;
26 GetWindowRect(hwnd, out rect);
27 Console.WriteLine($"Left:{rect.Left},Top:{rect.Top},Right:{rect.Right},Bottom:{rect.Bottom}");
28 }
29 }
30 }
运行结果如下:
下面我们来看一下,把结构体换成类的情况
1 using System;
2 using System.Runtime.InteropServices;
3
4 namespace ConsoleApp10
5 {
6 public class RECT
7 {
8 public int Left;
9 public int Top;
10 public int Right;
11 public int Bottom;
12 }
13
14 class Program
15 {
16 [DllImport("user32.dll")]
17 private static extern IntPtr GetDesktopWindow();
18
19 [DllImport("user32.dll", SetLastError = true)]
20 private static extern int GetWindowRect(IntPtr hwnd,RECT rc);
21
22 static void Main(string[] args)
23 {
24 IntPtr hwnd = GetDesktopWindow();
25 RECT rect = new RECT();
26 GetWindowRect(hwnd, rect);
27 Console.WriteLine($"Left:{rect.Left},Top:{rect.Top},Right:{rect.Right},Bottom:{rect.Bottom}");
28 }
29 }
30 }
运行结果如下:
运行结果并不正常
把类修改一下,带上[StructLayout(LayoutKind.Sequential)]
1 [StructLayout(LayoutKind.Sequential)]
2 public class RECT
3 {
4 public int Left;
5 public int Top;
6 public int Right;
7 public int Bottom;
8 }
再次运行,发现结果正常了
最后再看看LayoutKind.Explicit的情况,调用代码如下
1 using System;
2 using System.Runtime.InteropServices;
3
4 namespace ConsoleApp10
5 {
6 [StructLayout(LayoutKind.Explicit)]
7 public class RECT
8 {
9 [FieldOffset(0)]public int Left; //FieldOffset用于指示类或结构的非托管表示形式中字段的物理位置
10 [FieldOffset(4)]public int Top;
11 [FieldOffset(8)]public int Right;
12 [FieldOffset(12)]public int Bottom;
13 }
14
15 class Program
16 {
17 [DllImport("user32.dll")]
18 private static extern IntPtr GetDesktopWindow();
19
20 [DllImport("user32.dll", SetLastError = true)]
21 private static extern int GetWindowRect(IntPtr hwnd,[MarshalAs(UnmanagedType.LPStruct)]RECT rc);
22
23 static void Main(string[] args)
24 {
25 IntPtr hwnd = GetDesktopWindow();
26 RECT rect = new RECT();
27 GetWindowRect(hwnd, rect);
28 Console.WriteLine($"Left:{rect.Left},Top:{rect.Top},Right:{rect.Right},Bottom:{rect.Bottom}");
29 }
30 }
31 }
运行结果也是正常的。