System.Runtime.InteropServices.ICustomMarshaler-Schnittstelle

Dieser Artikel enthält ergänzende Hinweise zur Referenzdokumentation für diese API.

Die ICustomMarshaler Schnittstelle stellt benutzerdefinierte Wrapper für die Behandlung von Methodenaufrufen bereit.

Ein Marshaller bietet eine Brücke zwischen der Funktionalität alter und neuer Schnittstellen. Benutzerdefinierte Marshaling bietet die folgenden Vorteile:

  • Es ermöglicht Clientanwendungen, die für die Arbeit mit einer alten Schnittstelle entwickelt wurden, auch mit Servern zu arbeiten, die eine neue Schnittstelle implementieren.
  • Es ermöglicht Clientanwendungen, die mit einer neuen Schnittstelle arbeiten, um mit Servern zu arbeiten, die eine alte Schnittstelle implementieren.

Wenn Sie über eine Schnittstelle verfügen, die ein anderes Marshallingverhalten einführt oder auf eine andere Weise für das Component Object Model (COM) verfügbar gemacht wird, können Sie einen benutzerdefinierten Marshaller entwerfen, anstatt den Interop-Marshaller zu verwenden. Mithilfe eines benutzerdefinierten Marshallers können Sie den Unterschied zwischen neuen .NET Framework-Komponenten und vorhandenen COM-Komponenten minimieren.

Angenommen, Sie entwickeln eine verwaltete Schnittstelle namens INew. Wenn diese Schnittstelle com über einen standardmäßigen COM-aufrufbaren Wrapper (CCW) verfügbar gemacht wird, verfügt sie über die gleichen Methoden wie die verwaltete Schnittstelle und verwendet die Marshallregeln, die in den Interop-Marshaller integriert sind. Angenommen, eine bekannte COM-Schnittstelle, die aufgerufen wird IOld , bietet bereits die gleiche Funktionalität wie die INew Schnittstelle. Durch das Entwerfen eines benutzerdefinierten Marshallers können Sie eine nicht verwaltete Implementierung IOld bereitstellen, die einfach die Aufrufe an die verwaltete Implementierung der INew Schnittstelle delegiert. Daher fungiert der benutzerdefinierte Marshaller als Brücke zwischen den verwalteten und nicht verwalteten Schnittstellen.

Hinweis

Benutzerdefinierte Marshaller werden nicht aufgerufen, wenn Sie von verwaltetem Code zu nicht verwaltetem Code auf einer Nur-Dispatch-Schnittstelle aufrufen.

Definieren des Marshaling-Typs

Bevor Sie einen benutzerdefinierten Marshaller erstellen können, müssen Sie die verwalteten und nicht verwalteten Schnittstellen definieren, die gemarstet werden. Diese Schnittstellen führen häufig dieselbe Funktion aus, werden jedoch unterschiedlich verwalteten und nicht verwalteten Objekten verfügbar gemacht.

Ein verwalteter Compiler erzeugt eine verwaltete Schnittstelle aus Metadaten, und die resultierende Schnittstelle sieht wie jede andere verwaltete Schnittstelle aus. Das folgende Beispiel zeigt eine typische Schnittstelle.

public interface INew
{
    void NewMethod();
}
Public Interface INew
    Sub NewMethod()
End Interface

Sie definieren den nicht verwalteten Typ in Interface Definition Language (IDL) und kompilieren ihn mit dem MIDL-Compiler (Microsoft Interface Definition Language). Sie definieren die Schnittstelle in einer Bibliotheksanweisung und weisen ihm eine Schnittstellen-ID mit dem UUID-Attribut (Universal Unique Identifier) zu, wie im folgenden Beispiel veranschaulicht.

 [uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library OldLib {
     [uuid(9B2BAADD-0705-11D3-A0CD-00C04FA35826)]
     interface IOld : IUnknown
         HRESULT OldMethod();
}

Der MIDL-Compiler erzeugt mehrere Ausgabedateien. Wenn die Schnittstelle in Old.idl definiert ist, definiert die Ausgabedatei Old_i.c eine const Variable mit dem Schnittstellenbezeichner (IID) der Schnittstelle, wie im folgenden Beispiel veranschaulicht.

const IID IID_IOld = {0x9B2BAADD,0x0705,0x11D3,{0xA0,0xCD,0x00,0xC0,0x4F,0xA3,0x58,0x26}};

Die Datei Old.h wird auch von MIDL produziert. Sie enthält eine C++-Definition der Schnittstelle, die im C++-Quellcode enthalten sein kann.

Implementieren der ICustomMarshaler-Schnittstelle

Ihr benutzerdefinierter Marshaller muss die ICustomMarshaler Schnittstelle implementieren, um die entsprechenden Wrapper für die Laufzeit bereitzustellen.

Der folgende C#-Code zeigt die Basisschnittstelle an, die von allen benutzerdefinierten Marshallern implementiert werden muss.

public interface ICustomMarshaler
{
    Object MarshalNativeToManaged(IntPtr pNativeData);
    IntPtr MarshalManagedToNative(Object ManagedObj);
    void CleanUpNativeData(IntPtr pNativeData);
    void CleanUpManagedData(Object ManagedObj);
    int GetNativeDataSize();
}
Public Interface ICustomMarshaler
     Function MarshalNativeToManaged( pNativeData As IntPtr ) As Object
     Function MarshalManagedToNative( ManagedObj As Object ) As IntPtr
     Sub CleanUpNativeData( pNativeData As IntPtr )
     Sub CleanUpManagedData( ManagedObj As Object )
     Function GetNativeDataSize() As Integer
End Interface

Die ICustomMarshaler Schnittstelle enthält Methoden, die Konvertierungsunterstützung, sauber up-Unterstützung und Informationen zu den zu marshallden Daten bereitstellen.

Typ der Operation ICustomMarshaler-Methode Beschreibung
Konvertierung (von systemeigenem in verwaltetem Code) MarshalNativeToManaged Marshallt einen Zeiger auf systemeigene Daten in einem verwalteten Objekt. Diese Methode gibt einen benutzerdefinierten runtime callable wrapper (RCW) zurück, der die nicht verwaltete Schnittstelle marshallen kann, die als Argument übergeben wird. Der Marshaller sollte eine Instanz des benutzerdefinierten RCW für diesen Typs zurückgeben.
Konvertierung (von verwaltetem in systemeigenem Code) MarshalManagedToNative Marshallt ein verwaltetes Objekt in einem Zeiger auf systemeigene Daten. Diese Methode gibt einen benutzerdefinierten COM-aufrufbaren Wrapper (CCW) zurück, der die verwaltete Schnittstelle marshallen kann, die als Argument übergeben wird. Der Marshaller sollte eine Instanz des benutzerdefinierten CCW für diesen Typ zurückgeben.
Bereinigung (systemeigener Code) CleanUpNativeData Ermöglicht es dem Marshaller, die systemeigenen Daten (CCW) zu sauber, die von der MarshalManagedToNative Methode zurückgegeben werden.
Bereinigung (von verwaltetem Code) CleanUpManagedData Ermöglicht dem Marshaller, die von der MarshalNativeToManaged Methode zurückgegebenen verwalteten Daten (RCW) zu sauber.
Informationen (über nativen Code) GetNativeDataSize Gibt die Größe der nicht verwalteten Daten zurück, die gemarstet werden sollen.

Konvertierung

ICustomMarshaler.MarshalNativeToManaged

Marshallt einen Zeiger auf systemeigene Daten in einem verwalteten Objekt. Diese Methode gibt einen benutzerdefinierten runtime callable wrapper (RCW) zurück, der die nicht verwaltete Schnittstelle marshallen kann, die als Argument übergeben wird. Der Marshaller sollte eine Instanz des benutzerdefinierten RCW für diesen Typs zurückgeben.

ICustomMarshaler.MarshalManagedToNative

Marshallt ein verwaltetes Objekt in einem Zeiger auf systemeigene Daten. Diese Methode gibt einen benutzerdefinierten COM-aufrufbaren Wrapper (CCW) zurück, der die verwaltete Schnittstelle marshallen kann, die als Argument übergeben wird. Der Marshaller sollte eine Instanz des benutzerdefinierten CCW für diesen Typ zurückgeben.

Bereinigung

ICustomMarshaler.CleanUpNativeData

Ermöglicht es dem Marshaller, die systemeigenen Daten (CCW) zu sauber, die von der MarshalManagedToNative Methode zurückgegeben werden.

ICustomMarshaler.CleanUpManagedData

Ermöglicht dem Marshaller, die von der MarshalNativeToManaged Methode zurückgegebenen verwalteten Daten (RCW) zu sauber.

Größeninformationen

ICustomMarshaler.GetNativeDataSize

Gibt die Größe der nicht verwalteten Daten zurück, die gemarstet werden sollen.

Hinweis

Wenn ein benutzerdefinierter Marshaller Methoden aufruft, die den letzten P/Invoke-Fehler festlegen, wenn er vom systemeigenen zum verwalteten oder beim sauber hochgesendet wird, stellt der von Marshal.GetLastWin32Error() ihm Marshal.GetLastPInvokeError() zurückgegebene Wert den Aufruf im Marshalling- oder sauber up-Aufruf dar. Dies kann dazu führen, dass Fehler bei Verwendung von benutzerdefinierten Marshallern mit P/Invokes mit DllImportAttribute.SetLastError "Set" trueverpasst werden. Verwenden Sie Marshal.GetLastPInvokeError() die Methoden und Marshal.SetLastPInvokeError(Int32) Methoden in der ICustomMarshaler Implementierung, um den letzten P/Invoke-Fehler beizubehalten.

Implementieren der GetInstance-Methode

Zusätzlich zur Implementierung der Schnittstelle müssen benutzerdefinierte Marshaller eine static Methode implementieren, die ICustomMarshaler einen String als Parameter akzeptiert und über einen Rückgabetyp verfügtICustomMarshalerGetInstance. Diese static Methode wird von der COM-Interoperabilitätsebene der Common Language Runtime aufgerufen, um eine Instanz des benutzerdefinierten Marshallers zu instanziieren. Die an das Cookie übergebene GetInstance Zeichenfolge ist ein Cookie, mit dem die Methode den zurückgegebenen benutzerdefinierten Marshaller anpassen kann. Das folgende Beispiel zeigt eine minimale, aber vollständige ICustomMarshaler Implementierung.

public class NewOldMarshaler : ICustomMarshaler
{
    public static ICustomMarshaler GetInstance(string pstrCookie)
        => new NewOldMarshaler();

    public Object MarshalNativeToManaged(IntPtr pNativeData) => throw new NotImplementedException();
    public IntPtr MarshalManagedToNative(Object ManagedObj) => throw new NotImplementedException();
    public void CleanUpNativeData(IntPtr pNativeData) => throw new NotImplementedException();
    public void CleanUpManagedData(Object ManagedObj) => throw new NotImplementedException();
    public int GetNativeDataSize() => throw new NotImplementedException();
}

MarshalAsAttribute anwenden

Um einen benutzerdefinierten Marshaller zu verwenden, müssen Sie das MarshalAsAttribute Attribut auf den Parameter oder das Feld anwenden, der gemarstet wird.

Sie müssen auch den UnmanagedType.CustomMarshaler Enumerationswert an den MarshalAsAttribute Konstruktor übergeben. Darüber hinaus müssen Sie das MarshalType Feld mit einem der folgenden benannten Parameter angeben:

  • MarshalType (erforderlich): Der assemblyqualifizierte Name des benutzerdefinierten Marshallers. Der Name sollte den Namespace und die Klasse des benutzerdefinierten Marshallers enthalten. Wenn der benutzerdefinierte Marshaller nicht in der Assembly definiert ist, in der er verwendet wird, müssen Sie den Namen der Assembly angeben, in der sie definiert ist.

    Hinweis

    Sie können das MarshalTypeRef Feld anstelle des MarshalType Felds verwenden. MarshalTypeRef verwendet einen Typ, der einfacher anzugeben ist.

  • MarshalCookie (optional): Ein Cookie, das an den benutzerdefinierten Marshaller übergeben wird. Sie können das Cookie verwenden, um dem Marshaller zusätzliche Informationen bereitzustellen. Wenn beispielsweise derselbe Marshaller verwendet wird, um eine Reihe von Wrappern bereitzustellen, identifiziert das Cookie einen bestimmten Wrapper. Das Cookie wird an die GetInstance Methode des Marshallers übergeben.

Das MarshalAsAttribute Attribut identifiziert den benutzerdefinierten Marshaller, damit er den entsprechenden Wrapper aktivieren kann. Der Interopdienst der Common Language Runtime untersucht dann das Attribut und erstellt den benutzerdefinierten Marshaller, wenn das Argument (Parameter oder Feld) zum ersten Mal gemarstet werden muss.

Die Laufzeit ruft dann die und MarshalManagedToNative methoden MarshalNativeToManaged für den benutzerdefinierten Marshaller auf, um den richtigen Wrapper zum Behandeln des Aufrufs zu aktivieren.

Verwenden eines benutzerdefinierten Marshallers

Wenn der benutzerdefinierte Marshaller abgeschlossen ist, können Sie ihn als benutzerdefinierter Wrapper für einen bestimmten Typ verwenden. Das folgende Beispiel zeigt die Definition der IUserData verwalteten Schnittstelle:

interface IUserData
{
    void DoSomeStuff(INew pINew);
}
Public Interface IUserData
    Sub DoSomeStuff(pINew As INew)
End Interface

Im folgenden Beispiel verwendet die IUserData Schnittstelle den NewOldMarshaler benutzerdefinierten Marshaller, um nicht verwaltete Clientanwendungen zu ermöglichen, eine IOld Schnittstelle an die DoSomeStuff Methode zu übergeben. Die verwaltete Beschreibung der DoSomeStuff Methode verwendet eine INew Schnittstelle, wie im vorherigen Beispiel gezeigt, während die nicht verwaltete Version eines DoSomeStuff Schnittstellenzeigers verwendet IOld wird, wie im folgenden Beispiel gezeigt.

[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library UserLib {
     [uuid(9B2BABCD-0705-11D3-A0CD-00C04FA35826)]
     interface IUserData : IUnknown
         HRESULT DoSomeStuff(IUnknown* pIOld);
}

Die Typbibliothek, die durch das Exportieren der verwalteten Definition von IUserData Erträgen generiert wird, enthält die in diesem Beispiel gezeigte nicht verwaltete Definition anstelle der Standarddefinition. Das MarshalAsAttribute Attribut, das auf das INew Argument in der verwalteten Definition der DoSomeStuff Methode angewendet wird, gibt an, dass das Argument einen benutzerdefinierten Marshaller verwendet, wie im folgenden Beispiel gezeigt.

using System.Runtime.InteropServices;
Imports System.Runtime.InteropServices
interface IUserData
{
    void DoSomeStuff(
        [MarshalAs(UnmanagedType.CustomMarshaler,
         MarshalType="NewOldMarshaler")]
    INew pINew
    );
}
Public Interface IUserData
    Sub DoSomeStuff( _
        <MarshalAs(UnmanagedType.CustomMarshaler, _
        MarshalType := "MyCompany.NewOldMarshaler")> pINew As INew)
End Interface

In den vorherigen Beispielen ist der erste Parameter, der für das MarshalAsAttribute Attribut bereitgestellt wird, der UnmanagedType.CustomMarshaler Enumerationswert UnmanagedType.CustomMarshaler.

Der zweite Parameter ist das MarshalType Feld, das den assemblyqualifizierten Namen des benutzerdefinierten Marshallers bereitstellt. Dieser Name besteht aus dem Namespace und der Klasse des benutzerdefinierten Marshallers (MarshalType="MyCompany.NewOldMarshaler").