592
Chương 15: Khả năng liên tác mã lệnh không-được-quản-lý
System.Runtime.Interop.Marshal
nếu muốn xác định kích thước của cấu trúc
theo byte.
Trong mã C# thuần túy, bạn không có khả năng trực tiếp kiểm soát việc cấp bộ nhớ. Thay vào
đó, CRL sẽ quyết định khi nào cần đưa dữ liệu vào bộ nhớ để tối ưu hóa hoạt động. Điều này
gây rắc rối khi làm việc với các hàm C, vì cấu trúc phải được trữ liên tục trong bộ nhớ. May
mắn là .NET đã giải quyết vấn đề này bằng đặc tính
StructLayoutAttribute
, cho phép bạn chỉ
định các thành viên của một lớp hay một cấu trúc cho trước sẽ được sắp xếp trong bộ nhớ như
thế nào.
Ví dụ, xét hàm
GetVersionEx
trong thư viện kernel32.dll. Hàm này nhận một con trỏ chỉ tới
cấu trúc
OSVERSIONINFO
và sử dụng nó để trả về thông tin phiên bản của hệ điều hành. Để sử
dụng cấu trúc
OSVERSIONINFO
trong mã C#, bạn phải định nghĩa nó với đặc tính
StructLayoutAttribute
như sau:
[StructLayout(LayoutKind.Sequential)]
public class OSVersionInfo {
public int dwOSVersionInfoSize;
public int dwMajorVersion;
public int dwMinorVersion;
public int dwBuildNumber;
public int dwPlatformId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)]
public String szCSDVersion;
}
Chú ý rằng, cấu trúc này cũng sử dụng đặc tính
System.Runtime.InteropServices.
MarshalAsAttribute
(cần cho các chuỗi có kích thước không đổi). Ở đây,
MarshalAsAttribute
chỉ định chuỗi sẽ được truyền bằng trị và sẽ chứa một bộ đệm gồm 128
ký tự được chỉ định trong cấu trúc
OSVersionInfo
. Trong ví dụ này,
LayoutKind.Sequential
được sử dụng, nghĩa là các kiểu dữ liệu trong cấu trúc được bố trí theo thứ tự mà chúng được
liệt kê trong cấu trúc hoặc lớp.
Ngoài
LayoutKind.Sequential
, bạn có thể sử dụng
LayoutKind.Explicit
. Trong trường hợp
này, bạn phải sử dụng
FieldOffsetAttribute
để định nghĩa độ dời của các trường. Cách này
hữu ích khi bạn muốn lưu trữ các trường một cách linh động hơn, hoặc bạn muốn bỏ qua
(không sử dụng) trường nào đó. Ví dụ sau định nghĩa lớp
OSVersionInfo
với
LayoutKind.Explicit
.
[StructLayout(LayoutKind.Explicit)]
public class OSVersionInfo {
[FieldOffset(0)] public int dwOSVersionInfoSize;