Передача структур
Для многих неуправляемых функций в качестве параметра должны быть переданы члены структур (в Visual Basic это определяемые пользователем типы) или члены классов, определяемые в управляемом коде программы. Для сохранения исходного размещения и выравнивания при передаче структур или классов в неуправляемый код необходимо предоставить дополнительные сведения при вызове неуправляемого кода. В этом разделе описывается атрибут StructLayoutAttribute, который можно использовать для определения форматированных типов. Для управляемых структур и классов можно выбрать одно из стандартных размещений, предоставленных перечислением LayoutKind.
Важнейшим из понятий, описанных в этом разделе, является существенное различие между структурами и классами как типами. Структуры представляют собой типы значений, а классы —ссылочные типы, которые всегда обеспечивают, по крайней мере, один уровень косвенного обращения к памяти (указатель на значение). Это различие очень важно, так как неуправляемые функции часто требуют косвенного обращения, что видно из примеров сигнатур в первом столбце следующей таблицы. Объявления управляемых структур и классов в остальных столбцах показывают, насколько возможна настройка уровня косвенного обращения в объявлении.
Неуправляемая сигнатура |
Управляемое объявление: без косвенного обращения struct MyStruct(…); |
Управляемое объявление: один уровень косвенного обращения class MyStruct(…); |
---|---|---|
DoWork(MyStruct x); Требует отсутствия уровней косвенного обращения. |
DoWork(ByVal x As MyStruct) Добавляет ноль уровней косвенного обращения. |
Невозможно, так как уже существует один уровень косвенного обращения. |
DoWork(MyStruct* x); Требует одного уровня косвенного обращения. |
DoWork(ByRef x As MyStruct) Добавляет один уровень косвенного обращения. |
DoWork(ByVal x As MyStruct) Добавляет ноль уровней косвенного обращения. |
DoWork(MyStruct** x); Требует двух уровней косвенного обращения. |
Невозможно, так как объявление ByRef ByRef использовать нельзя. |
DoWork(ByRef x As MyStruct) Добавляет один уровень косвенного обращения. |
Эта таблица содержит следующие указания для объявлений вызовов неуправляемого кода:
Если неуправляемая функция не требует косвенного обращения, структуру следует передавать по значению.
Если для неуправляемой функции требуется один уровень косвенного обращения, используется либо структура, передаваемая по ссылке, либо класс, передаваемый по значению.
Если для неуправляемой функции требуется два уровня косвенного обращения, используется класс, передаваемый по ссылке.
Объявление и передача структур
В следующем примере показано определение структур 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
Class Win32API
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;
}
class Win32API {
[DllImport("User32.dll")]
public static extern bool PtInRect(ref Rect r, Point p);
}
Объявление и передача классов
Если для класса используется фиксированное размещение членов, их можно передавать в неуправляемую функцию DLL,. В следующем примере демонстрируется передача последовательно упорядоченных в определении членов класса MySystemTime в функцию GetSystemTime в файле User32.dll. Для функции GetSystemTime используется следующая неуправляемая сигнатура:
void GetSystemTime(SYSTEMTIME* SystemTime);
В отличие от типов значений для классов всегда используется, по крайней мере, один уровень косвенного обращения.
Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.VisualBasic
<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
Public Class Win32
Declare Auto Sub GetSystemTime Lib "Kernel32.dll"(sysTime _
As MySystemTime)
Declare Auto Function MessageBox Lib "User32.dll"(hWnd As IntPtr, _
txt As String, caption As String, Typ As Integer) As Integer
End Class
Public Class TestPlatformInvoke
Public Shared Sub Main()
Dim sysTime As New MySystemTime()
Win32.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
Win32.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;
}
class Win32API {
[DllImport("Kernel32.dll")]
public static extern void GetSystemTime(MySystemTime st);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern int MessageBox(IntPtr hWnd,
string text, string caption, int options);
}
public class TestPlatformInvoke
{
public static void Main()
{
MySystemTime sysTime = new MySystemTime();
Win32API.GetSystemTime(sysTime);
string dt;
dt = "System time is: \n" +
"Year: " + sysTime.wYear + "\n" +
"Month: " + sysTime.wMonth + "\n" +
"DayOfWeek: " + sysTime.wDayOfWeek + "\n" +
"Day: " + sysTime.wDay;
Win32API.MessageBox(IntPtr.Zero, dt, "Platform Invoke Sample", 0);
}
}