C#使用StructLayout特性来控制内存结构

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 }

运行结果也是正常的。

相关推荐
逐·風17 分钟前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
m0_656974743 小时前
C#中的集合类及其使用
开发语言·c#
九鼎科技-Leo3 小时前
了解 .NET 运行时与 .NET 框架:基础概念与相互关系
windows·c#·.net
九鼎科技-Leo6 小时前
什么是 ASP.NET Core?与 ASP.NET MVC 有什么区别?
windows·后端·c#·asp.net·mvc·.net
.net开发6 小时前
WPF怎么通过RestSharp向后端发请求
前端·c#·.net·wpf
小乖兽技术6 小时前
C#与C++交互开发系列(二十):跨进程通信之共享内存(Shared Memory)
c++·c#·交互·ipc
幼儿园园霸柒柒7 小时前
第七章: 7.3求一个3*3的整型矩阵对角线元素之和
c语言·c++·算法·矩阵·c#·1024程序员节
平凡シンプル9 小时前
C# EF 使用
c#
丁德双9 小时前
winform 加载 office excel 插入QRCode图片如何设定位置
c#·excel
九鼎科技-Leo10 小时前
在 C# 中,ICollection 和 IList 接口有什么区别?
windows·c#·.net