Übergeben von Strukturen

Bei vielen nicht verwalteten Funktionen wird erwartet, dass Member von Strukturen (benutzerdefinierte Typen in Visual Basic) oder Member von Klassen, die in verwaltetem Code definiert werden, als Parameter an die Funktion übergeben werden. Wenn Sie Strukturen oder Klassen an nicht verwalteten Code mithilfe von Plattformaufruf übergeben, müssen Sie zusätzliche Informationen angeben, um das ursprüngliche Layout und die ursprüngliche Ausrichtung beizubehalten. Dieses Thema enthält eine Einführung in das StructLayoutAttribute-Attribut, das Sie zum Definieren formatierter Typen verwenden. Sie können für verwaltete Strukturen und Klassen verschiedene vorhersagbare Layoutverhalten auswählen, die von der LayoutKind-Enumeration bereitgestellt werden.

Es gibt einen entscheidenden Unterschied zwischen Struktur- und Klassentypen, der für die im vorliegenden Thema dargestellten Konzepte eine Schlüsselposition einnimmt. Strukturen sind Werttypen und Klassen sind Referenztypen, wobei Klassen immer mindestens eine Speicherdereferenzierungsebene (einen Zeiger auf einen Wert) bereitstellen. Dieser Unterschied ist relevant, da nicht verwaltete Funktionen oftmals Dereferenzierung erfordern, was durch die Signaturen in der ersten Spalte der folgenden Tabelle dargestellt wird. Die verwalteten Struktur- und Klassendeklarationen in den anderen Spalten geben an, in welchem Ausmaß die Dereferenzierungsebene in der Deklaration angepasst werden kann.

Nicht verwaltete Signatur

Verwaltete Deklaration:

keine Dereferenzierung

struct MyStruct(…);

Verwaltete Deklaration:

eine Dereferenzierungsebene

class MyStruct(…);

DoWork(MyStruct x);

Erfordert 0 Dereferenzierungsebenen.

DoWork(ByVal x As MyStruct)

Fügt 0 Dereferenzierungsebenen hinzu.

Nicht möglich, da bereits eine Dereferenzierungsebene besteht.

DoWork(MyStruct* x);

Erfordert eine Dereferenzierungsebene.

DoWork(ByRef x As MyStruct)

Fügt eine Dereferenzierungsebene hinzu.

DoWork(ByVal x As MyStruct)

Fügt 0 Dereferenzierungsebenen hinzu.

DoWork(MyStruct** x);

Erfordert zwei Dereferenzierungsebenen.

Nicht möglich, da ByRef ByRef nicht verwendet werden kann.

DoWork(ByRef x As MyStruct)

Fügt eine Dereferenzierungsebene hinzu.

Die Tabelle beschreibt die folgenden Richtlinien für Plattformaufrufdeklarationen:

  • Verwenden Sie eine Struktur, die durch einen Wert übergeben wird, wenn die nicht verwaltete Funktion keine Dereferenzierung erfordert.

  • Verwenden Sie eine Struktur, die durch einen Verweis übergeben wird, oder verwenden Sie eine Klasse, die durch einen Wert übergeben wird, wenn die nicht verwaltete Funktion eine Dereferenzierungsebene erfordert.

  • Verwenden Sie eine Klasse, die durch einen Verweis übergeben wird, wenn die nicht verwaltete Funktion zwei Dereferenzierungsebenen erfordert.

Deklarieren und Übergeben von Strukturen

Im folgenden Beispiel wird gezeigt, wie Sie Point-Strukturen und Rect-Strukturen in verwaltetem Code definieren und die Typen als Parameter an die PtInRect-Funktion in der Datei User32.dll übergeben können. PtInRect hat folgende nicht verwaltete Signatur:

BOOL PtInRect(const RECT *lprc, POINT pt);

Hinweis: Sie müssen die Rect-Struktur durch Verweis übergeben, da von der Funktion ein Zeiger auf einen RECT-Typ erwartet wird.

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);
}

Deklarieren und Übergeben von Klassen

Sie können Member einer Klasse an nicht verwaltete DLL-Funktionen übergeben, solange die Klasse ein festes Layout für Member hat. Das folgende Beispiel veranschaulicht, wie Sie Member der MySystemTime-Klasse, die sequenziell definiert sind, an GetSystemTime in der Datei User32.dll übergeben können. GetSystemTime hat folgende nicht verwaltete Signatur:

void GetSystemTime(SYSTEMTIME* SystemTime);

Im Unterschied zu Werttypen besitzen Klassen immer mindestens eine Dereferenzierungsebene.

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);
    }
}

Siehe auch

Referenz

StructLayoutAttribute-Klasse

StructLayoutAttribute-Klasse

FieldOffsetAttribute-Klasse

Weitere Ressourcen

Aufrufen einer DLL-Funktion