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 }
運行結果也是正常的。