Structuren doorgeven
Veel niet-beheerde functies verwachten dat u, als parameter aan de functie, leden van structuren (door de gebruiker gedefinieerde typen in Visual Basic) of leden van klassen die zijn gedefinieerd in beheerde code, doorgeeft. Wanneer u structuren of klassen doorgeeft aan onbeheerde code met behulp van platformoproep, moet u aanvullende informatie opgeven om de oorspronkelijke indeling en uitlijning te behouden. In dit onderwerp wordt het StructLayoutAttribute kenmerk geïntroduceerd dat u gebruikt om opgemaakte typen te definiëren. Voor beheerde structuren en klassen kunt u kiezen uit verschillende voorspelbare indelingsgedragen die worden geleverd door de opsomming LayoutKind .
Centraal in de concepten die in dit onderwerp worden gepresenteerd, is een belangrijk verschil tussen structuur- en klassetypen. Structuren zijn waardetypen en klassen zijn referentietypen. Klassen bieden altijd ten minste één niveau van geheugen indirectie (een aanwijzer naar een waarde). Dit verschil is belangrijk omdat onbeheerde functies vaak indirectie vereisen, zoals wordt weergegeven door de handtekeningen in de eerste kolom van de volgende tabel. In de beheerde structuur en klassedeclaraties in de resterende kolommen ziet u de mate waarin u het niveau van indirectie in uw declaratie kunt aanpassen. Declaraties worden opgegeven voor Visual Basic en Visual C#.
Onbeheerde handtekening | Beheerde declaratie: geen indirectie Structure MyType struct MyType; |
Beheerde declaratie: één niveau van indirectie Class MyType class MyType; |
---|---|---|
DoWork(MyType x); Vereist nul niveaus van indirectie. |
DoWork(ByVal x As MyType) DoWork(MyType x) Hiermee worden nul niveaus van indirectie toegevoegd. |
Niet mogelijk omdat er al één niveau van indirectie is. |
DoWork(MyType* x); Vereist één niveau van indirectie. |
DoWork(ByRef x As MyType) DoWork(ref MyType x) Hiermee wordt één niveau van indirectie toegevoegd. |
DoWork(ByVal x As MyType) DoWork(MyType x) Hiermee worden nul niveaus van indirectie toegevoegd. |
DoWork(MyType** x); Vereist twee niveaus van indirectie. |
Niet mogelijk omdat ByRef ByRef of ref ref niet kan worden gebruikt. |
DoWork(ByRef x As MyType) DoWork(ref MyType x) Hiermee wordt één niveau van indirectie toegevoegd. |
In de tabel worden de volgende richtlijnen beschreven voor het aanroepen van platformdeclaraties:
Gebruik een structuur die door de waarde wordt doorgegeven wanneer de onbeheerde functie geen indirecte invloed vereist.
Gebruik een structuur die wordt doorgegeven door verwijzing of een klasse die door een waarde wordt doorgegeven wanneer de onbeheerde functie één niveau van indirectie vereist.
Gebruik een klasse die door een verwijzing wordt doorgegeven wanneer de onbeheerde functie twee niveaus van indirectie vereist.
Structuren declareren en doorgeven
In het volgende voorbeeld ziet u hoe u de Point
en Rect
structuren in beheerde code definieert en de typen als parameter doorgeeft aan de functie PtInRect in het bestand User32.dll. PtInRect heeft de volgende onbeheerde handtekening:
BOOL PtInRect(const RECT *lprc, POINT pt);
U moet de rect-structuur doorgeven aan een verwijzing, omdat de functie een aanwijzer naar een RECT-type verwacht.
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);
}
Klassen declareren en doorgeven
U kunt leden van een klasse doorgeven aan een niet-beheerde DLL-functie, zolang de klasse een vaste lidindeling heeft. In het volgende voorbeeld ziet u hoe u leden van de MySystemTime
klasse, die in opeenvolgende volgorde zijn gedefinieerd, doorgeeft aan GetSystemTime in het User32.dll-bestand. GetSystemTime heeft de volgende niet-beheerde handtekening:
void GetSystemTime(SYSTEMTIME* SystemTime);
In tegenstelling tot waardetypen hebben klassen altijd ten minste één niveau van indirectie.
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);
}
}