Przekazywanie struktur
Wiele niezarządzanych funkcji oczekuje przekazania funkcji jako parametru do funkcji składowych struktur (typów zdefiniowanych przez użytkownika w Visual Basic) lub składowych klas zdefiniowanych w kodzie zarządzanym. Podczas przekazywania struktur lub klas do niezarządzanych kodu przy użyciu wywołania platformy należy podać dodatkowe informacje, aby zachować oryginalny układ i wyrównanie. W tym temacie StructLayoutAttribute przedstawiono atrybut używany do definiowania sformatowanych typów. W przypadku zarządzanych struktur i klas można wybrać spośród kilku przewidywalnych zachowań układu dostarczonych przez wyliczenie LayoutKind .
Kluczową kwestią dla pojęć przedstawionych w tym temacie jest ważna różnica między typami struktury i klas. Struktury to typy wartości, a klasy są typami referencyjnymi — klasy zawsze zapewniają co najmniej jeden poziom pośredniej pamięci (wskaźnik do wartości). Ta różnica jest ważna, ponieważ funkcje niezarządzane często wymagają pośredniości, jak pokazano w podpisach w pierwszej kolumnie poniższej tabeli. Struktura zarządzana i deklaracje klas w pozostałych kolumnach pokazują stopień, w jakim można dostosować poziom pośredni w deklaracji. Deklaracje są dostarczane dla języka Visual Basic i Visual C#.
Podpis niezarządzany | Deklaracja zarządzana: brak pośredni Structure MyType struct MyType; |
Deklaracja zarządzana: jeden poziom pośredni Class MyType class MyType; |
---|---|---|
DoWork(MyType x); Wymaga zerowych poziomów pośredniego. |
DoWork(ByVal x As MyType) DoWork(MyType x) Dodaje zero poziomów pośredniego. |
Nie można, ponieważ istnieje już jeden poziom pośredni. |
DoWork(MyType* x); Wymaga jednego poziomu pośredniego. |
DoWork(ByRef x As MyType) DoWork(ref MyType x) Dodaje jeden poziom pośredni. |
DoWork(ByVal x As MyType) DoWork(MyType x) Dodaje zero poziomów pośredniego. |
DoWork(MyType** x); Wymaga dwóch poziomów pośredniego. |
Nie można użyć polecenia ByRef ByRef lub ref ref nie można go użyć. |
DoWork(ByRef x As MyType) DoWork(ref MyType x) Dodaje jeden poziom pośredni. |
W tabeli opisano następujące wytyczne dotyczące deklaracji wywoływania platformy:
Użyj struktury przekazywanej przez wartość, gdy niezarządzana funkcja nie wymaga żadnej pośredniości.
Użyj struktury przekazanej przez odwołanie lub klasy przekazanej przez wartość, gdy niezarządzana funkcja wymaga jednego poziomu pośredniego.
Użyj klasy przekazanej przez odwołanie, gdy niezarządzana funkcja wymaga dwóch poziomów pośredniego.
Deklarowanie i przekazywanie struktur
W poniższym przykładzie pokazano, jak zdefiniować Point
struktury i Rect
w kodzie zarządzanym oraz przekazać typy jako parametr do funkcji PtInRect w pliku User32.dll. Element PtInRect ma następujący podpis niezarządzany:
BOOL PtInRect(const RECT *lprc, POINT pt);
Zwróć uwagę, że należy przekazać strukturę Rect według odwołania, ponieważ funkcja oczekuje wskaźnika do typu 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);
}
Deklarowanie i przekazywanie klas
Można przekazać elementy członkowskie klasy do niezarządzanej funkcji DLL, o ile klasa ma stały układ składowy. W poniższym przykładzie pokazano, jak przekazać składowe MySystemTime
klasy zdefiniowanej w kolejności sekwencyjnej do metody GetSystemTime w pliku User32.dll. Polecenie GetSystemTime ma następujący podpis niezarządzany:
void GetSystemTime(SYSTEMTIME* SystemTime);
W przeciwieństwie do typów wartości klasy zawsze mają co najmniej jeden poziom pośredni.
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);
}
}