Share via


Standardmäßiges Marshalling für Werttypen

Die meisten Werttypen, z. B. ganze Zahlen und Gleitkommazahlen, sind blitfähig und müssen daher nicht gemarshallt werden. Andere, nicht blitfähige Typen sind im verwalteten und im nicht verwalteten Speicher unterschiedlich dargestellt und müssen gemarshallt werden. Wieder andere Typen müssen über die Grenze für die Interoperation hinweg explizit formatiert werden.

In diesem Thema erhalten Sie folgende Informationen über formatierte Werttypen:

  • Im Plattformaufruf verwendete Werttypen

  • In COM-Interop verwendete Werttypen

Neben der Beschreibung formatierter Typen identifiziert dieses Thema Systemwerttypen, die ein ungewöhnliches Verhalten beim Marshallen aufweisen.

Formatierte Typen sind komplexe Typen mit Informationen, durch die das Layout der Member im Speicher explizit gesteuert wird. Die Memberlayoutdaten werden durch Verwendung des StructLayoutAttribute-Attributs bereitgestellt. Eines der folgenden Layouts aus der LayoutKind-Enumeration kann verwendet werden:

  • LayoutKind.Automatic

    Weist darauf hin, dass die Common Language Runtime die Member des Typs aus Effizienzgründen neu anordnen kann. Wenn ein Werttyp an nicht verwalteten Code übergeben wird, ist das Layout der Member jedoch vorhersehbar. Wenn der Versuch unternommen wird, eine solche Struktur zu marshallen, wird automatisch eine Ausnahme ausgelöst.

  • LayoutKind.Sequential

    Weist darauf hin, dass die Member des Typs im nicht verwalteten Speicher in derselben Reihenfolge angeordnet werden sollen, in der sie in der Definition des verwalteten Typs erscheinen sind.

  • LayoutKind.Explicit

    Weist darauf hin, dass die Member entsprechend dem für die einzelnen Felder bereitgestellten FieldOffsetAttribute angeordnet sind.

Im Plattformaufruf verwendete Werttypen

Im folgenden Beispiel stellen die Typen Point und Rect durch Verwendung des StructLayoutAttribute Informationen zum Layout der Member bereit.

Imports System.Runtime.InteropServices
<StructLayout(LayoutKind.Sequential)> Public Structure Point
   Public x As Integer
   Public y As Integer
End Structure
<StructLayout(LayoutKind.Explicit)> Public Structure 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
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;
}

Wenn diese formatierten Typen an nicht verwalteten Code gemarshallt werden, so geschieht dies in Form von Strukturen im C-Format. Dadurch kann eine nicht verwaltete API mit Strukturargumenten problemlos aufgerufen werden. Die POINT-Struktur und die RECT-Struktur können beispielsweise folgendermaßen an die PtInRect-Funktion der Microsoft Win32-API übergeben werden:

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

Strukturen können mit der folgenden Plattformaufrufdefinition übergeben werden:

Class Win32API    
   Declare Auto Function PtInRect Lib "User32.dll" _
    (ByRef r As Rect, p As Point) As Boolean
End Class
class Win32API {
   [DllImport("User32.dll")]
   public static extern Bool PtInRect(ref Rect r, Point p);
}

Der Rect-Werttyp muss durch einen Verweis übergeben werden, da die nicht verwaltete API erwartet, dass ein Zeiger auf RECT an die Funktion übergeben wird. Der Point-Werttyp wird durch einen Wert übergeben, da die nicht verwaltete API erwartet, dass POINT auf dem Stapel übergeben wird. Dieser feine Unterschied ist von großer Bedeutung. Verweise werden als Zeiger an nicht verwalteten Code übergeben. Werte werden auf dem Stapel an nicht verwalteten Code übergeben.

Hinweis

Wenn ein formatierter Typ als Struktur gemarshallt wird, kann nur auf die Felder innerhalb des Typs zugegriffen werden.Wenn der Typ über Methoden, Eigenschaften oder Ereignisse verfügt, kann über nicht verwalteten Code nicht auf diese zugegriffen werden.

Klassen können ebenfalls als Strukturen im C-Format an nicht verwalteten Code gemarshallt werden, sofern das Memberlayout feststehend ist. Die Memberlayoutdaten einer Klasse werden durch Verwendung des StructLayoutAttribute-Attributs bereitgestellt. Der wichtigste Unterschied zwischen Werttypen und Klassen mit feststehendem Layout besteht in der Art und Weise, in der sie an nicht verwalteten Code gemarshallt werden. Werttypen werden durch einen Wert (auf dem Stapel) übergeben, weshalb der Aufrufer die durch den Aufgerufenen an den Membern des Typs vorgenommenen Änderungen nicht sehen kann. Verweistypen werden durch einen Verweis übergeben. (Ein Verweis auf den Typ wird auf dem Stapel übergeben.) Infolgedessen können alle durch den Aufgerufenen an den Membern des blitfähigen Typs vorgenommenen Änderungen vom Aufrufer gesehen werden.

Hinweis

Wenn ein Verweistyp über Member nicht blitfähiger Typen verfügt, muss die Konvertierung doppelt ausgeführt werden: zunächst bei der Übergabe eines Arguments an die nicht verwaltete Seite und dann bei Rückgabe aus dem Aufruf.Aufgrund dieses zusätzlichen Aufwands müssen In/Out-Parameter explizit auf ein Argument angewendet werden, wenn der Aufrufer die durch den Aufgerufenen vorgenommenen Änderungen sehen möchte.

Im folgenden Beispiel verfügt die SystemTime-Klasse über ein sequenzielles Memberlayout und kann an die GetSystemTime-Funktion der Win32-API übergeben werden.

<StructLayout(LayoutKind.Sequential)> Public Class SystemTime
   Public wYear As System.UInt16
   Public wMonth As System.UInt16
   Public wDayOfWeek As System.UInt16
   Public wDay As System.UInt16
   Public wHour As System.UInt16
   Public wMinute As System.UInt16
   Public wSecond As System.UInt16
   Public wMilliseconds As System.UInt16
End Class
[StructLayout(LayoutKind.Sequential)]
   public class SystemTime {
   public ushort wYear; 
   public ushort wMonth;
   public ushort wDayOfWeek; 
   public ushort wDay; 
   public ushort wHour; 
   public ushort wMinute; 
   public ushort wSecond; 
   public ushort wMilliseconds; 
}

Die GetSystemTime-Funktion ist folgendermaßen definiert:

void GetSystemTime(SYSTEMTIME* SystemTime);

Die entsprechende Plattformaufrufdefinition für GetSystemTime lautet folgendermaßen:

Public Class Win32
   Declare Auto Sub GetSystemTime Lib "Kernel32.dll" (ByVal sysTime _
   As SystemTime)
End Class
class Win32API {
   [DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
   public static extern void GetSystemTime(SystemTime st);
}

Beachten Sie, dass das SystemTime-Argument nicht als Verweisargument typisiert ist, da es sich bei SystemTime nicht um einen Werttyp, sondern um eine Klasse handelt. Anders als Werttypen werden Klassen immer durch einen Verweis übergeben.

Im folgenden Codebeispiel ist eine andere Point-Klasse mit einer Methode mit der Bezeichnung SetXY dargestellt. Da der Typ über ein sequenzielles Layout verfügt, kann er an nicht verwalteten Code übergeben und als Struktur gemarshallt werden. Der SetXY-Member kann jedoch nicht über nicht verwalteten Code aufgerufen werden, obwohl das Objekt durch einen Verweis übergeben wird.

<StructLayout(LayoutKind.Sequential)> Public Class Point
   Private x, y As Integer
   Public Sub SetXY(x As Integer, y As Integer)
      Me.x = x
      Me.y = y
   End Sub
End Class
[StructLayout(LayoutKind.Sequential)]
public class Point {
   int x, y;
   public void SetXY(int x, int y){ 
      this.x = x;
      this.y = y;
   }
}

In COM-Interop verwendete Werttypen

An COM-Interop-Methodenaufrufe können auch formatierte Typen übergeben werden. Werttypen werden sogar automatisch in Strukturen umgewandelt, wenn sie in eine Typbibliothek exportiert werden. Wie das folgende Beispiel zeigt, wird der Point-Werttyp in eine Typdefinition (typedef) mit der Bezeichnung Point umgewandelt. Alle Verweise auf den Point -Werttyp an anderer Stelle in der Typbibliothek werden durch die Point typedef ersetzt.

Typbibliothekdarstellung

typedef struct tagPoint {
   int x;
   int y;
} Point;
interface _Graphics {
   …
   HRESULT SetPoint ([in] Point p)
   HRESULT SetPointRef ([in,out] Point *p)
   HRESULT GetPoint ([out,retval] Point *p)
}

Beim Marshallen über COM-Schnittstellen werden dieselben Regeln verwendet wie beim Marshallen von Werten und Verweisen an Plattformaufrufe. Wenn beispielsweise eine Instanz des Point-Werttyps von .NET Framework an COM übergeben wird, wird Point durch einen Wert übergeben. Der Point-Werttyp wird durch einen Verweis übergeben, und ein Zeiger auf einen Point wird auf dem Stapel übergeben. Der Interop-Marshaller unterstützt in keiner Richtung höhere Indirection-Levels (Point **).

Hinweis

Strukturen, für deren LayoutKind-Enumerationswert Explicit festgelegt ist, können in COM-Interop nicht verwendet werden, da die exportierte Typbibliothek kein explizites Layout ausdrücken kann.

Systemwerttypen

Der System-Namespace verfügt über mehrere Werttypen, die das mittels Boxing gepackte Format primitiver Laufzeittypen darstellen. So repräsentiert der Werttyp der System.Int32-Struktur beispielsweise das mittels Boxing gepackte Format von ELEMENT_TYPE_I4 . Diese Typen werden nicht wie andere formatierte Typen als Strukturen gemarshallt, sondern das Marshalling erfolgt auf dieselbe Weise, wie das Marshalling für die von ihnen mittels Boxing gepackten primitiven Typen. System.Int32 wird demzufolge nicht als Struktur mit einem einzigen Member des Typs long, sondern als ELEMENT_TYPE_I4 gemarshallt. In der folgenden Tabelle sind die Werttypen im System-Namespace aufgelistet, die mittels boxing gepackte Darstellungen primitiver Typen sind.

Systemwerttyp

Elementtyp

System.Boolean

ELEMENT_TYPE_BOOLEAN

System.SByte

ELEMENT_TYPE_I1

System.Byte

ELEMENT_TYPE_UI1

System.Char

ELEMENT_TYPE_CHAR

System.Int16

ELEMENT_TYPE_I2

System.UInt16

ELEMENT_TYPE_U2

System.Int32

ELEMENT_TYPE_I4

System.UInt32

ELEMENT_TYPE_U4

System.Int64

ELEMENT_TYPE_I8

System.UInt64

ELEMENT_TYPE_U8

System.Single

ELEMENT_TYPE_R4

System.Double

ELEMENT_TYPE_R8

System.String

ELEMENT_TYPE_STRING

System.IntPtr

ELEMENT_TYPE_I

System.UIntPtr

ELEMENT_TYPE_U

Einige andere Werttypen im System-Namespace werden anders behandelt. Da der nicht verwaltete Code bereits über bestehende Formate für diese Typen verfügt, verwendet der Marshaller zum Marshallen dieser Formate besondere Regeln. In der folgenden Tabelle sind die besonderen Werttypen im System-Namespace und die nicht verwalteten Typen aufgelistet, an die sie gemarshallt werden.

Systemwerttyp

IDL-Typ

System.DateTime

DATE

System.Decimal

DEZIMAL

System.Guid

GUID

System.Drawing.Color

OLE_COLOR

Der folgende Code stellt die Definition der nicht verwalteten Typen DATE, GUID, DECIMAL und OLE_COLOR in der Stdole2-Typbibliothek dar.

0t2cwe11.collapse_all(de-de,VS.110).gifTypbibliothekdarstellung

typedef double DATE;
typedef DWORD OLE_COLOR;

typedef struct tagDEC {
    USHORT    wReserved;
    BYTE      scale;
    BYTE      sign;
    ULONG     Hi32;
    ULONGLONG Lo64;
} DECIMAL;

typedef struct tagGUID {
    DWORD Data1;
    WORD  Data2;
    WORD  Data3;
    BYTE  Data4[ 8 ];
} GUID;

Im folgenden Code sind die entsprechenden Definitionen in der verwalteten IValueTypes-Schnittstelle dargestellt.

Public Interface IValueTypes
   Sub M1(d As System.DateTime)
   Sub M2(d As System.Guid)
   Sub M3(d As System.Decimal)
   Sub M4(d As System.Drawing.Color)
End Interface
public interface IValueTypes {
   void M1(System.DateTime d);
   void M2(System.Guid d);
   void M3(System.Decimal d);
   void M4(System.Drawing.Color d);
}

0t2cwe11.collapse_all(de-de,VS.110).gifTypbibliothekdarstellung

[…]
interface IValueTypes : IDispatch {
   HRESULT M1([in] DATE d);
   HRESULT M2([in] GUID d);
   HRESULT M3([in] DECIMAL d);
   HRESULT M4([in] OLE_COLOR d);
};

Siehe auch

Konzepte

Blitfähige und nicht blitfähige Typen

Direktionale Attribute

Kopieren und Fixieren

Weitere Ressourcen

Standardmarshallingverhalten