System.Runtime.InteropServices.ICustomMarshaler, interfejs

Ten artykuł zawiera dodatkowe uwagi dotyczące dokumentacji referencyjnej dla tego interfejsu API.

Interfejs ICustomMarshaler udostępnia niestandardowe otoki do obsługi wywołań metod.

Marshaller zapewnia most między funkcjonalnością starych i nowych interfejsów. Przeprowadzanie marshalingu niestandardowego zapewnia następujące korzyści:

  • Umożliwia ona aplikacjom klienckim, które zostały zaprojektowane do pracy ze starym interfejsem, w celu pracy z serwerami, które implementują nowy interfejs.
  • Umożliwia ona aplikacjom klienckim skompilowane pracę z nowym interfejsem w celu pracy z serwerami, które implementują stary interfejs.

Jeśli masz interfejs wprowadzający różne zachowanie marshalingu lub uwidoczniony w modelu obiektów składników (COM) w inny sposób, możesz zaprojektować niestandardowy marshaller zamiast używać marshallera międzyoperacyjnego. Korzystając z niestandardowego marshallera, można zminimalizować rozróżnienie między nowymi składnikami programu .NET Framework i istniejącymi składnikami MODELU COM.

Załóżmy na przykład, że tworzysz interfejs zarządzany o nazwie INew. Gdy ten interfejs jest uwidoczniony dla modelu COM za pośrednictwem standardowej otoki wywoływanej modelu COM (CCW), ma takie same metody jak interfejs zarządzany i używa reguł marshalingu wbudowanych w program marshaller międzyoperacyjną. Teraz załóżmy, że dobrze znany interfejs COM o nazwie IOld już zapewnia takie same funkcje jak INew interfejs. Projektując niestandardowy marshaller, można zapewnić niezarządzaną implementację IOld , która po prostu deleguje wywołania do zarządzanej implementacji interfejsu INew . W związku z tym niestandardowy marshaller działa jako most między zarządzanymi i niezarządzanych interfejsami.

Uwaga

Niestandardowe marshallers nie są wywoływane podczas wywoływania z kodu zarządzanego do niezarządzanego kodu w interfejsie tylko do wysyłania.

Definiowanie typu marshalingu

Przed utworzeniem niestandardowego marshallera należy zdefiniować zarządzane i niezarządzane interfejsy, które będą marshalowane. Te interfejsy często wykonują tę samą funkcję, ale są udostępniane inaczej niż zarządzane i niezarządzane obiekty.

Zarządzany kompilator tworzy interfejs zarządzany z metadanych, a wynikowy interfejs wygląda jak każdy inny zarządzany interfejs. W poniższym przykładzie przedstawiono typowy interfejs.

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

Zdefiniuj typ niezarządzany w języku IDL (Interface Definition Language) i skompilujesz go za pomocą kompilatora języka Microsoft Interface Definition Language (MIDL). Interfejs można zdefiniować w instrukcji biblioteki i przypisać mu identyfikator interfejsu z atrybutem uniwersalnego unikatowego identyfikatora (UUID), jak pokazano w poniższym przykładzie.

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

Kompilator MIDL tworzy kilka plików wyjściowych. Jeśli interfejs jest zdefiniowany w pliku Old.idl, plik wyjściowy Old_i.c definiuje zmienną const o identyfikatorze interfejsu (IID) interfejsu, jak pokazano w poniższym przykładzie.

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

Plik Old.h jest również produkowany przez MIDL. Zawiera definicję języka C++ interfejsu, który można uwzględnić w kodzie źródłowym języka C++.

Implementowanie interfejsu ICustomMarshaler

Niestandardowy marshaller musi zaimplementować ICustomMarshaler interfejs, aby zapewnić odpowiednie otoki do środowiska uruchomieniowego.

Poniższy kod języka C# wyświetla interfejs podstawowy, który musi zostać zaimplementowany przez wszystkie niestandardowe marshallers.

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

Interfejs ICustomMarshaler zawiera metody, które zapewniają obsługę konwersji, obsługę oczyszczania i informacje o danych do marshalingu.

Typ operacji ICustomMarshaler, metoda opis
Konwersja (z natywnego na kod zarządzany) MarshalNativeToManaged Marshaluje wskaźnik do danych natywnych do obiektu zarządzanego. Ta metoda zwraca niestandardową otokę wywołującą środowisko uruchomieniowe (RCW), która może marshalować niezarządzany interfejs, który jest przekazywany jako argument. Marshaller powinien zwrócić wystąpienie niestandardowego RCW dla tego typu.
Konwersja (z kodu zarządzanego na natywny) MarshalManagedToNative Przeprowadzanie marshalingu obiektu zarządzanego do wskaźnika do danych natywnych. Ta metoda zwraca niestandardową otokę wywoływaną modelu COM (CCW), która umożliwia przeprowadzanie marshalingu zarządzanego interfejsu przekazywanego jako argument. Marshaller powinien zwrócić wystąpienie niestandardowego CCW dla tego typu.
Oczyszczanie (kodu natywnego) CleanUpNativeData Umożliwia programowi marshaller wyczyszczenie danych natywnych (CCW), które są zwracane przez metodę MarshalManagedToNative .
Oczyszczanie (kodu zarządzanego) CleanUpManagedData Umożliwia programowi marshaller wyczyszczenie zarządzanych danych (RCW), które są zwracane przez metodę MarshalNativeToManaged .
Informacje (o kodzie natywnym) GetNativeDataSize Zwraca rozmiar niezarządzanych danych do marshalingu.

Konwersja

ICustomMarshaler.MarshalNativeToManaged

Marshaluje wskaźnik do danych natywnych do obiektu zarządzanego. Ta metoda zwraca niestandardową otokę wywołującą środowisko uruchomieniowe (RCW), która może marshalować niezarządzany interfejs, który jest przekazywany jako argument. Marshaller powinien zwrócić wystąpienie niestandardowego RCW dla tego typu.

ICustomMarshaler.MarshalManagedToNative

Przeprowadzanie marshalingu obiektu zarządzanego do wskaźnika do danych natywnych. Ta metoda zwraca niestandardową otokę wywoływaną modelu COM (CCW), która umożliwia przeprowadzanie marshalingu zarządzanego interfejsu przekazywanego jako argument. Marshaller powinien zwrócić wystąpienie niestandardowego CCW dla tego typu.

Czyszczenie

ICustomMarshaler.CleanUpNativeData

Umożliwia programowi marshaller wyczyszczenie danych natywnych (CCW), które są zwracane przez metodę MarshalManagedToNative .

ICustomMarshaler.CleanUpManagedData

Umożliwia programowi marshaller wyczyszczenie zarządzanych danych (RCW), które są zwracane przez metodę MarshalNativeToManaged .

Informacje o rozmiarze

ICustomMarshaler.GetNativeDataSize

Zwraca rozmiar niezarządzanych danych do marshalingu.

Uwaga

Jeśli niestandardowy marshaller wywołuje dowolne metody, które ustawiają ostatni błąd P/Invoke podczas marshalingu z natywnego do zarządzanego lub podczas czyszczenia, wartość zwrócona przez Marshal.GetLastWin32Error() i Marshal.GetLastPInvokeError() będzie reprezentować wywołanie w wywołaniach marshalingu lub oczyszczania. Może to spowodować pominięcie błędów podczas używania niestandardowych marshallerów z parametrami P/Invoke z ustawioną wartością DllImportAttribute.SetLastErrortrue. Aby zachować ostatni błąd P/Invoke, użyj Marshal.GetLastPInvokeError() metod i Marshal.SetLastPInvokeError(Int32) w implementacji ICustomMarshaler .

Implementowanie metody GetInstance

Oprócz implementowania interfejsu ICustomMarshaler niestandardowe marshallers muszą zaimplementować metodę static o nazwie GetInstance , która akceptuje String jako parametr i ma zwracany typ ICustomMarshaler. Ta static metoda jest wywoływana przez warstwę międzyoperacową COM środowiska uruchomieniowego języka wspólnego w celu utworzenia wystąpienia wystąpienia niestandardowego marshallera. Ciąg przekazywany do GetInstance jest plikiem cookie, którego metoda może użyć do dostosowania zwracanego marshallera niestandardowego. Poniższy przykład przedstawia minimalną, ale kompletną ICustomMarshaler implementację.

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

Stosowanie marshalAsAttribute

Aby użyć niestandardowego marshallera, należy zastosować MarshalAsAttribute atrybut do parametru lub pola, które jest marshalowane.

Należy również przekazać UnmanagedType.CustomMarshaler wartość wyliczenia do konstruktora MarshalAsAttribute . Ponadto należy określić MarshalType pole z jednym z następujących nazwanych parametrów:

  • MarshalType (wymagane): kwalifikowana przez zestaw nazwa niestandardowego marshallera. Nazwa powinna zawierać przestrzeń nazw i klasę niestandardowego marshallera. Jeśli niestandardowy marshaller nie jest zdefiniowany w zestawie, w którym jest używany, należy określić nazwę zestawu, w którym jest zdefiniowany.

    Uwaga

    Możesz użyć MarshalTypeRef pola zamiast MarshalType pola. MarshalTypeRef przyjmuje typ, który jest łatwiejszy do określenia.

  • MarshalCookie (opcjonalnie): plik cookie przekazywany do niestandardowego marshallera. Możesz użyć pliku cookie, aby podać dodatkowe informacje do marshallera. Jeśli na przykład ten sam marshaller jest używany do dostarczania wielu otoek, plik cookie identyfikuje określoną otokę. Plik cookie jest przekazywany do GetInstance metody marshallera.

Atrybut MarshalAsAttribute identyfikuje niestandardowy marshaller, aby mógł aktywować odpowiednią otokę. Następnie usługa międzyoperacyjności środowiska uruchomieniowego języka wspólnego sprawdza atrybut i tworzy niestandardowy marshaller przy pierwszym przesłonieniu argumentu (parametru lub pola).

Następnie środowisko uruchomieniowe wywołuje MarshalNativeToManaged metody i MarshalManagedToNative dla niestandardowego marshallera, aby aktywować poprawną otokę w celu obsługi wywołania.

Używanie niestandardowego marshallera

Gdy niestandardowy marshaller zostanie ukończony, możesz użyć go jako niestandardowej otoki dla określonego typu. W poniższym przykładzie przedstawiono definicję interfejsu zarządzanego IUserData :

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

W poniższym przykładzie interfejs używa niestandardowego marshallera, IUserData aby umożliwić niezarządzanym aplikacjom klienckim IOld przekazywanie interfejsu DoSomeStuff do metody .NewOldMarshaler Zarządzany opis DoSomeStuff metody przyjmuje INew interfejs, jak pokazano w poprzednim przykładzie, natomiast niezarządzana wersja przyjmuje IOld wskaźnik interfejsuDoSomeStuff, jak pokazano w poniższym przykładzie.

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

Biblioteka typów wygenerowana przez wyeksportowanie zarządzanej definicji daje niezarządzaną definicję IUserData pokazaną w tym przykładzie zamiast standardowej definicji. Atrybut MarshalAsAttribute zastosowany do argumentu INew w zarządzanej definicji DoSomeStuff metody wskazuje, że argument używa niestandardowego marshallera, jak pokazano w poniższym przykładzie.

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

W poprzednich przykładach pierwszym parametrem podanym dla atrybutu MarshalAsAttribute jest UnmanagedType.CustomMarshaler wartość UnmanagedType.CustomMarshalerwyliczenia .

Drugi parametr to MarshalType pole, które udostępnia kwalifikowaną przez zestaw nazwę niestandardowego marshallera. Ta nazwa składa się z przestrzeni nazw i klasy niestandardowego marshallera (MarshalType="MyCompany.NewOldMarshaler").