Skicka strukturer
Många ohanterade funktioner förväntar sig att du skickar medlemmar i strukturer (användardefinierade typer i Visual Basic) som en parameter till funktionen eller medlemmar i klasser som definieras i hanterad kod. När du skickar strukturer eller klasser till ohanterad kod med plattformsanrop måste du ange ytterligare information för att bevara den ursprungliga layouten och justeringen. Det här avsnittet introducerar attributet StructLayoutAttribute som du använder för att definiera formaterade typer. För hanterade strukturer och klasser kan du välja bland flera förutsägbara layoutbeteenden som tillhandahålls av LayoutKind-uppräkningen .
Centralt för de begrepp som presenteras i det här avsnittet är en viktig skillnad mellan struktur- och klasstyper. Strukturer är värdetyper och klasser är referenstyper – klasser ger alltid minst en nivå av indirekt minne (en pekare till ett värde). Den här skillnaden är viktig eftersom ohanterade funktioner ofta kräver indirektion, vilket visas av signaturerna i den första kolumnen i följande tabell. Den hanterade strukturen och klassdeklarationerna i de återstående kolumnerna visar i vilken grad du kan justera indirektionsnivån i deklarationen. Deklarationer tillhandahålls för både Visual Basic och Visual C#.
Ohanterad signatur | Hanterad deklaration: ingen indirektion Structure MyType struct MyType; |
Hanterad deklaration: en indirekt nivå Class MyType class MyType; |
---|---|---|
DoWork(MyType x); Kräver noll indirekta nivåer. |
DoWork(ByVal x As MyType) DoWork(MyType x) Lägger till noll nivåer av indirektion. |
Inte möjligt eftersom det redan finns en indirekt nivå. |
DoWork(MyType* x); Kräver en indirekt nivå. |
DoWork(ByRef x As MyType) DoWork(ref MyType x) Lägger till en indirekt nivå. |
DoWork(ByVal x As MyType) DoWork(MyType x) Lägger till noll nivåer av indirektion. |
DoWork(MyType** x); Kräver två indirekta nivåer. |
Inte möjligt eftersom ByRef ByRef eller ref ref inte kan användas. |
DoWork(ByRef x As MyType) DoWork(ref MyType x) Lägger till en indirekt nivå. |
Tabellen beskriver följande riktlinjer för plattformsanropsdeklarationer:
Använd en struktur som skickas av ett värde när den ohanterade funktionen inte kräver någon indirektion.
Använd antingen en struktur som skickas av en referens eller en klass som skickas av ett värde när den ohanterade funktionen kräver en indirekt nivå.
Använd en klass som skickas med referens när den ohanterade funktionen kräver två indirekta nivåer.
Deklarera och skicka strukturer
I följande exempel visas hur du definierar strukturerna Point
och Rect
i den hanterade koden och skickar typerna som parameter till funktionen PtInRect i User32.dll-filen. PtInRect har följande ohanterade signatur:
BOOL PtInRect(const RECT *lprc, POINT pt);
Observera att du måste skicka Rect-strukturen efter referens, eftersom funktionen förväntar sig en pekare till en RECT-typ.
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);
}
Deklarera och skicka klasser
Du kan skicka medlemmar i en klass till en ohanterad DLL-funktion, så länge klassen har en fast medlemslayout. I följande exempel visas hur du skickar medlemmar i MySystemTime
klassen, som definieras i sekventiell ordning, till GetSystemTime i filen User32.dll. GetSystemTime har följande ohanterade signatur:
void GetSystemTime(SYSTEMTIME* SystemTime);
Till skillnad från värdetyper har klasser alltid minst en indirekt nivå.
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);
}
}