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; //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 }

 

運行結果也是正常的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM