Передача структур
В качестве параметров во многие неуправляемые функции должны передаваться члены, структуры (определяемые пользователем типы в Visual Basic) или члены классов, которые определяются в управляемом коде. При передаче структур или классов в неуправляемый код посредством вызовов неуправляемого кода необходимо указать дополнительную информацию для сохранения исходного размещения и выравнивания. В этом разделе описывается атрибут StructLayoutAttribute, который используется для определения форматированных типов. Для управляемых структур и классов можно выбрать любое из нескольких предсказуемых поведений размещения в перечислении LayoutKind.
Представленные в этом разделе понятия приводятся с учетом важного различия между структурами и типами классов. Структуры представляют собой типы значений, а классы — ссылочные типы. В классах всегда реализуется как минимум один уровень косвенного обращения к памяти (указатель на значение). Это важное отличие, поскольку неуправляемые функции часто используют косвенное обращение, как показано в сигнатурах в первом столбце в следующей таблице. Управляемые структуры и объявления классов в оставшихся столбцах демонстрируют, в какой степени можно изменить уровень косвенного обращения в объявлении. Объявления предоставляются как для Visual Basic, так и для Visual C#.
Неуправляемая сигнатура | Управляемое объявление: без косвенного обращения Structure MyType struct MyType; |
Управляемое объявление: один уровень косвенного обращения Class MyType class MyType; |
---|---|---|
DoWork(MyType x); Требуется ноль уровней косвенного обращения. |
DoWork(ByVal x As MyType) DoWork(MyType x) Добавляет ноль уровней косвенного обращения. |
Невозможно, поскольку один уровень косвенного обращения уже существует. |
DoWork(MyType* x); Требуется один уровень косвенного обращения. |
DoWork(ByRef x As MyType) DoWork(ref MyType x) Добавляет один уровень косвенного обращения. |
DoWork(ByVal x As MyType) DoWork(MyType x) Добавляет ноль уровней косвенного обращения. |
DoWork(MyType** x); Требуется два уровня косвенного обращения. |
Невозможно, поскольку нельзя использовать ByRef ByRef или ref ref . |
DoWork(ByRef x As MyType) DoWork(ref MyType x) Добавляет один уровень косвенного обращения. |
В этой таблице описываются следующие рекомендации по объявлению вызовов неуправляемого кода:
Если неуправляемая функция не требует косвенного обращения, используйте структуру, передаваемую по значению.
Если неуправляемая функция требует один уровень косвенного обращения, используйте передаваемую по ссылке структуру или передаваемый по значению класс.
Если неуправляемая функция требует два уровня косвенного обращения, используйте класс, передаваемый по ссылке.
Объявление и передача структур
В следующем примере демонстрируется, как определить структуры Point
и Rect
в управляемом коде и передать типы в качестве параметров в функцию PtInRect в файле User32.dll. Функция PtInRect имеет следующую неуправляемую сигнатуру:
BOOL PtInRect(const RECT *lprc, POINT pt);
Обратите внимание, что структуру Rect необходимо передавать по ссылке, поскольку функция принимает указатель на тип RECT.
Imports System.Runtime.InteropServices
<StructLayout(LayoutKind.Sequential)> Public Structure Point
Public x As Integer
Public y As Integer
End Structure
Public Structure <StructLayout(LayoutKind.Explicit)> Rect
<FieldOffset(0)> Public left As Integer
<FieldOffset(4)> Public top As Integer
<FieldOffset(8)> Public right As Integer
<FieldOffset(12)> Public bottom As Integer
End Structure
Friend Class NativeMethods
Friend Declare Auto Function PtInRect Lib "user32.dll" (
ByRef r As Rect, p As Point) As Boolean
End Class
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct Point {
public int x;
public int y;
}
[StructLayout(LayoutKind.Explicit)]
public struct Rect {
[FieldOffset(0)] public int left;
[FieldOffset(4)] public int top;
[FieldOffset(8)] public int right;
[FieldOffset(12)] public int bottom;
}
internal static class NativeMethods
{
[DllImport("User32.dll")]
internal static extern bool PtInRect(ref Rect r, Point p);
}
Объявление и передача классов
Члены класса можно передавать в неуправляемую функцию DLL при условии, что класс имеет фиксированное размещение членов. В следующем примере демонстрируется, как передать члены класса MySystemTime
, которые определяются последовательно, в функцию GetSystemTime в файле User32.dll. Функция GetSystemTime имеет следующую неуправляемую сигнатуру:
void GetSystemTime(SYSTEMTIME* SystemTime);
В отличие от типов значений, классы всегда имеют как минимум один уровень косвенного обращения.
Imports System.Runtime.InteropServices
<StructLayout(LayoutKind.Sequential)> Public Class MySystemTime
Public wYear As Short
Public wMonth As Short
Public wDayOfWeek As Short
Public wDay As Short
Public wHour As Short
Public wMinute As Short
Public wSecond As Short
Public wMiliseconds As Short
End Class
Friend Class NativeMethods
Friend Declare Auto Sub GetSystemTime Lib "Kernel32.dll" (
sysTime As MySystemTime)
Friend Declare Auto Function MessageBox Lib "User32.dll" (
hWnd As IntPtr, lpText As String, lpCaption As String, uType As UInteger) As Integer
End Class
Public Class TestPlatformInvoke
Public Shared Sub Main()
Dim sysTime As New MySystemTime()
NativeMethods.GetSystemTime(sysTime)
Dim dt As String
dt = "System time is:" & ControlChars.CrLf & _
"Year: " & sysTime.wYear & _
ControlChars.CrLf & "Month: " & sysTime.wMonth & _
ControlChars.CrLf & "DayOfWeek: " & sysTime.wDayOfWeek & _
ControlChars.CrLf & "Day: " & sysTime.wDay
NativeMethods.MessageBox(IntPtr.Zero, dt, "Platform Invoke Sample", 0)
End Sub
End Class
[StructLayout(LayoutKind.Sequential)]
public class MySystemTime {
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
internal static class NativeMethods
{
[DllImport("Kernel32.dll")]
internal static extern void GetSystemTime(MySystemTime st);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern int MessageBox(
IntPtr hWnd, string lpText, string lpCaption, uint uType);
}
public class TestPlatformInvoke
{
public static void Main()
{
MySystemTime sysTime = new MySystemTime();
NativeMethods.GetSystemTime(sysTime);
string dt;
dt = "System time is: \n" +
"Year: " + sysTime.wYear + "\n" +
"Month: " + sysTime.wMonth + "\n" +
"DayOfWeek: " + sysTime.wDayOfWeek + "\n" +
"Day: " + sysTime.wDay;
NativeMethods.MessageBox(IntPtr.Zero, dt, "Platform Invoke Sample", 0);
}
}