• P/Invoke各种总结(一、使用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;
    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 }

    运行结果也是正常的。

  • 相关阅读:
    django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
    Error fetching command 'collectstatic': You're using the staticfiles app without having set the STATIC_ROOT setting to a filesystem path. Command 'collectstatic' skipped
    windows 虚拟环境下 安装 mysql 引擎一系列错误处理
    项目概念流程
    pip 使用
    HTTPserver v3.0 版本项目
    GitHub 使用
    git 操作命令详解
    git 忽略部分文件类型的同步
    Python 正则处理_re模块
  • 原文地址:https://www.cnblogs.com/zhaotianff/p/12510286.html
Copyright © 2020-2023  润新知