Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Den här artikeln innehåller ytterligare kommentarer till referensdokumentationen för det här API:et.
Gränssnittet ICustomMarshaler innehåller anpassade omslutningar för hantering av metodanrop.
En marshaller ger en brygga mellan funktionerna i gamla och nya gränssnitt. Anpassad marskalkering ger följande fördelar:
- Det gör det möjligt för klientprogram som har utformats för att fungera med ett gammalt gränssnitt att också fungera med servrar som implementerar ett nytt gränssnitt.
- Det gör att klientprogram som skapats för att fungera med ett nytt gränssnitt fungerar med servrar som implementerar ett gammalt gränssnitt.
Om du har ett gränssnitt som introducerar olika marskalkeringsbeteenden eller som exponeras för komponentobjektmodellen (COM) på ett annorlunda sätt, kan du utforma en egen marskalker i stället för att använda interop-marskalkern. Genom att använda en anpassad marshaller kan du minimera skillnaden mellan nya .NET Framework-komponenter och befintliga COM-komponenter.
Anta till exempel att du utvecklar ett hanterat gränssnitt med namnet INew. När det här gränssnittet exponeras för COM via en STANDARD COM-anropsbar omslutning (CCW) har det samma metoder som det hanterade gränssnittet och använder de regler för marskalkering som är inbyggda i interop marshaller. Anta nu att ett välkänt COM-gränssnitt med namnet IOld redan har samma funktioner som INew gränssnittet. Genom att utforma en anpassad marshaller kan du tillhandahålla en ohanterad implementering av IOld som helt enkelt delegerar anropen till den hanterade implementeringen av INew gränssnittet. Därför fungerar den anpassade marshallern som en brygga mellan de hanterade och ohanterade gränssnitten.
Anmärkning
Anpassade marshallers anropas inte när man anropar från hanterad kod till ohanterad kod i ett dispatch-enbart gränssnitt.
Definiera marskalkeringstypen
Innan du kan skapa en anpassad marshaller måste du specificera de hanterade och ohanterade gränssnitt som ska marshallas. Dessa gränssnitt utför ofta samma funktion men exponeras på olika sätt för hanterade och ohanterade objekt.
En hanterad kompilator skapar ett hanterat gränssnitt från metadata och det resulterande gränssnittet ser ut som andra hanterade gränssnitt. I följande exempel visas ett typiskt gränssnitt.
public interface INew
{
void NewMethod();
}
Public Interface INew
Sub NewMethod()
End Interface
Du definierar den ohanterade typen i Gränssnittsdefinitionsspråk (IDL) och kompilerar den med MICROSOFT Interface Definition Language (MIDL)-kompilatorn. Du definierar gränssnittet i en biblioteksinstruktor och tilldelar det ett gränssnitts-ID med attributet UUID (Universal Unique Identifier), vilket visas i följande exempel.
[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library OldLib {
[uuid(9B2BAADD-0705-11D3-A0CD-00C04FA35826)]
interface IOld : IUnknown
HRESULT OldMethod();
}
MIDL-kompilatorn skapar flera utdatafiler. Om gränssnittet definieras i Old.idl definierar utdatafilen Old_i.c en const variabel med gränssnittsidentifieraren (IID) i gränssnittet, vilket visas i följande exempel.
const IID IID_IOld = {0x9B2BAADD,0x0705,0x11D3,{0xA0,0xCD,0x00,0xC0,0x4F,0xA3,0x58,0x26}};
Filen Old.h produceras också av MIDL. Den innehåller en C++-definition av gränssnittet som kan ingå i C++-källkoden.
Implementera gränssnittet ICustomMarshaler
Din anpassade marshaller måste implementera ICustomMarshaler-gränssnittet för att tillhandahålla lämpliga omslag till körmiljön.
Följande C#-kod visar basgränssnittet som måste implementeras av alla anpassade 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
Gränssnittet ICustomMarshaler innehåller metoder som ger konverteringsstöd, rensningsstöd och information om de data som ska konverteras.
| Typ av åtgärd | ICustomMarshaler-metod | Beskrivning |
|---|---|---|
| Konvertering (från intern till hanterad kod) | MarshalNativeToManaged | Konverterar en pekare till inbyggda data till ett hanterat objekt. Den här metoden returnerar en anpassad runtime-anropsomslag (RCW) som kan konvertera det ohanterade gränssnittet som skickas som ett argument. Marshallern bör returnera en instans av den anpassade RCW:n för den typen. |
| Konvertering (från hanterad till intern kod) | MarshalManagedToNative | Konverterar ett hanterat objekt till en pekare till inbyggda data. Den här metoden returnerar en anpassad COM-anropsbar omslutning (CCW) som kan konvertera det hanterade gränssnitt som skickas som ett argument. Marshallern bör returnera en instans av den anpassade CCW:en för den typen. |
| Rensning (av nativ kod) | CleanUpNativeData | Gör det möjligt för marshallern att rensa de inbyggda data (CCW) som returneras av MarshalManagedToNative-metoden. |
| Rensning (av hanterad kod) | CleanUpManagedData | Gör det möjligt för marshaller att rensa de hanterade data (RCW) som returneras av MarshalNativeToManaged metoden. |
| Information (om nativ kod) | GetNativeDataSize | Returnerar storleken på de ohanterade data som ska överföras. |
Omvandling
ICustomMarshaler.MarshalNativeToManaged
Konverterar en pekare till inbyggda data till ett hanterat objekt. Den här metoden returnerar en anpassad runtime-anropsomslag (RCW) som kan konvertera det ohanterade gränssnittet som skickas som ett argument. Marshallern bör returnera en instans av den anpassade RCW:n för den typen.
ICustomMarshaler.MarshalManagedToNative
Konverterar ett hanterat objekt till en pekare till inbyggda data. Den här metoden returnerar en anpassad COM-anropsbar omslutning (CCW) som kan konvertera det hanterade gränssnitt som skickas som ett argument. Marshallern bör returnera en instans av den anpassade CCW:en för den typen.
Rengöring
ICustomMarshaler.CleanUpNativeData
Gör det möjligt för marshallern att rensa de inbyggda data (CCW) som returneras av MarshalManagedToNative-metoden.
ICustomMarshaler.CleanUpManagedData
Gör det möjligt för marshaller att rensa de hanterade data (RCW) som returneras av MarshalNativeToManaged metoden.
Storleksinformation
ICustomMarshaler.GetNativeDataSize
Returnerar storleken på de ohanterade data som ska överföras.
Anmärkning
Om en anpassad marshaller anropar några metoder som ställer in det sista P/Invoke-felet vid konvertering från inbyggd till hanterad kod eller vid städning, kommer värdet som returneras av Marshal.GetLastWin32Error() och Marshal.GetLastPInvokeError() att representera anropet i marschallerings- eller städanropen. Detta kan orsaka att fel missas när man använder anpassade marshallers med P/Invokes med DllImportAttribute.SetLastError inställt på true. För att bevara det senaste P/Invoke-felet, använd metoderna Marshal.GetLastPInvokeError() och Marshal.SetLastPInvokeError(Int32) i ICustomMarshaler-implementeringen.
Implementera Metoden GetInstance
Förutom att implementera ICustomMarshaler-gränssnittet måste skräddarsydda marshallers implementera en static-metod med namnet GetInstance som accepterar en String som parameter och har ICustomMarshaler som returtyp. Den här static metoden anropas av common language runtime:s COM interop-lager för att instansiera den anpassade marshallern. Strängen som skickas till GetInstance är en cookie som metoden kan använda för att anpassa den anpassade marshallern som returneras. I följande exempel visas en minimal, men fullständig, ICustomMarshaler implementering.
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();
}
Använd MarshalAsAttribute
Om du vill använda en anpassad marshaller måste du applicera MarshalAsAttribute attributet för parametern eller fältet som marshallas.
Du måste också skicka UnmanagedType.CustomMarshaler uppräkningsvärdet till MarshalAsAttribute konstruktorn. Dessutom måste du ange fältet MarshalType med någon av följande namngivna parametrar:
MarshalType (krävs): Det sammansättningskvalificerade namnet på den anpassade marshallern. Namnet ska innehålla namnrymden och klassen för den anpassade marshallern. Om den anpassade marshallern inte har definierats i den assembly som den används i måste du ange namnet på den assembly där den definieras.
Anmärkning
Du kan använda fältet MarshalTypeRef i stället för fältet MarshalType . MarshalTypeRef tar en typ som är enklare att ange.
MarshalCookie (valfritt): En cookie som skickas till den anpassade marshallern. Du kan använda cookien för att ge ytterligare information till marshallern. Om samma marshaler används för att tillhandahålla ett antal förpackningar, så identifierar en cookie en specifik förpackning. Cookien skickas till
GetInstancemarshallermetoden.
Attributet MarshalAsAttribute identifierar den anpassade marshallern så att den kan aktivera lämplig omslutning. Common Language Runtime's interop-tjänst undersöker sedan attributet och skapar den anpassade marshallern första gången argumentet (parametern eller fältet) behöver marshallas.
Körningen anropar metoderna MarshalNativeToManaged och MarshalManagedToNative på den anpassade marshallern för att aktivera den korrekta inneslutningen och hantera samtalet.
Använd en anpassad marshaller
När den egna marshallern är klar kan du använda den som ett skräddarsytt omslag för en specifik typ. I följande exempel visas definitionen av det IUserData hanterade gränssnittet:
interface IUserData
{
void DoSomeStuff(INew pINew);
}
Public Interface IUserData
Sub DoSomeStuff(pINew As INew)
End Interface
I följande exempel använder IUserData-gränssnittet NewOldMarshaler-anpassade marshallern för att möjliggöra för ohanterade klientprogram att överföra ett IOld-gränssnitt till DoSomeStuff-metoden. Den hanterade beskrivningen av DoSomeStuff-metoden tar INew-gränssnittet, som visas i föregående exempel, medan den ohanterade versionen av DoSomeStuff tar IOld-gränssnittspekaren, som visas i följande exempel.
[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library UserLib {
[uuid(9B2BABCD-0705-11D3-A0CD-00C04FA35826)]
interface IUserData : IUnknown
HRESULT DoSomeStuff(IUnknown* pIOld);
}
Typbiblioteket som genereras genom att exportera den hanterade definitionen ger IUserData den ohanterade definition som visas i det här exemplet i stället för standarddefinitionen. Attributet MarshalAsAttribute som tillämpas på INew argumentet i den hanterade definitionen av DoSomeStuff metoden anger att argumentet använder en anpassad marshaller, vilket visas i följande exempel.
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
I de föregående exemplen är den första parametern som anges i attributet MarshalAsAttribute uppräkningsvärdet UnmanagedType.CustomMarshaler.
Den andra parametern är fältet MarshalType som tillhandahåller det sammansättningskvalificerade namnet på den anpassade marshallern. Det här namnet består av namnområdet och klassen för den anpassade marshallern (MarshalType="MyCompany.NewOldMarshaler").