Interfaccia System.Runtime.InteropServices.ICustomMarshaler
Questo articolo fornisce osservazioni supplementari alla documentazione di riferimento per questa API.
L'interfaccia ICustomMarshaler fornisce wrapper personalizzati per la gestione delle chiamate ai metodi.
Un marshaller fornisce un ponte tra le funzionalità delle interfacce precedenti e nuove. Il marshalling personalizzato offre i vantaggi seguenti:
- Consente alle applicazioni client progettate per funzionare con un'interfaccia precedente per lavorare anche con i server che implementano una nuova interfaccia.
- Consente alle applicazioni client compilate di usare una nuova interfaccia per lavorare con i server che implementano un'interfaccia precedente.
Se si dispone di un'interfaccia che introduce un comportamento di marshalling diverso o esposto a Component Object Model (COM) in modo diverso, è possibile progettare un marshaller personalizzato anziché usare il marshaller di interoperabilità. Usando un marshaller personalizzato, è possibile ridurre al minimo la distinzione tra i nuovi componenti di .NET Framework e i componenti COM esistenti.
Si supponga, ad esempio, di sviluppare un'interfaccia gestita denominata INew
. Quando questa interfaccia viene esposta a COM tramite un wrapper com chiamabile standard (CCW), ha gli stessi metodi dell'interfaccia gestita e usa le regole di marshalling incorporate nel marshaller di interoperabilità. Si supponga ora che un'interfaccia COM nota denominata IOld
fornisca già la stessa funzionalità dell'interfaccia INew
. Progettando un marshaller personalizzato, è possibile fornire un'implementazione non gestita di IOld
che delega semplicemente le chiamate all'implementazione gestita dell'interfaccia INew
. Di conseguenza, il marshaller personalizzato funge da ponte tra le interfacce gestite e non gestite.
Nota
I marshaller personalizzati non vengono richiamati quando si chiama dal codice gestito al codice non gestito in un'interfaccia di sola distribuzione.
Definire il tipo di marshalling
Prima di poter creare un marshaller personalizzato, è necessario definire le interfacce gestite e non gestite che verranno sottoposto a marshalling. Queste interfacce eseguono in genere la stessa funzione, ma vengono esposte in modo diverso agli oggetti gestiti e non gestiti.
Un compilatore gestito produce un'interfaccia gestita dai metadati e l'interfaccia risultante è simile a qualsiasi altra interfaccia gestita. Nell'esempio seguente viene illustrata un'interfaccia tipica.
public interface INew
{
void NewMethod();
}
Public Interface INew
Sub NewMethod()
End Interface
Definire il tipo non gestito in Interface Definition Language (IDL) e compilarlo con il compilatore MIDL (Microsoft Interface Definition Language). Definire l'interfaccia all'interno di un'istruzione di libreria e assegnarla un ID interfaccia con l'attributo UUID (Universal Unique Identifier), come illustrato nell'esempio seguente.
[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library OldLib {
[uuid(9B2BAADD-0705-11D3-A0CD-00C04FA35826)]
interface IOld : IUnknown
HRESULT OldMethod();
}
Il compilatore MIDL produce diversi file di output. Se l'interfaccia è definita in Old.idl, il file di output Old_i.c definisce una const
variabile con l'identificatore di interfaccia (IID) dell'interfaccia, come illustrato nell'esempio seguente.
const IID IID_IOld = {0x9B2BAADD,0x0705,0x11D3,{0xA0,0xCD,0x00,0xC0,0x4F,0xA3,0x58,0x26}};
Il file Old.h viene prodotto anche da MIDL. Contiene una definizione C++ dell'interfaccia che può essere inclusa nel codice sorgente C++.
Implementare l'interfaccia ICustomMarshaler
Il marshaller personalizzato deve implementare l'interfaccia ICustomMarshaler per fornire i wrapper appropriati al runtime.
Il codice C# seguente visualizza l'interfaccia di base che deve essere implementata da tutti i marshaller personalizzati.
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
L'interfaccia ICustomMarshaler include metodi che forniscono supporto per la conversione, supporto per la pulizia e informazioni sui dati da sottoporre a marshalling.
Tipo di operazione | Metodo ICustomMarshaler | Descrizione |
---|---|---|
Conversione (da codice nativo a codice gestito) | MarshalNativeToManaged | Effettua il marshalling di un puntatore ai dati nativi in un oggetto gestito. Questo metodo restituisce un wrapper chiamabile di runtime personalizzato (RCW) in grado di effettuare il marshalling dell'interfaccia non gestita passata come argomento. Il marshaller deve restituire un'istanza dell'RCW personalizzato per tale tipo. |
Conversione (da codice gestito a nativo) | MarshalManagedToNative | Effettua il marshalling di un oggetto gestito in un puntatore ai dati nativi. Questo metodo restituisce un wrapper com chiamabile personalizzato (CCW) in grado di effettuare il marshalling dell'interfaccia gestita passata come argomento. Il marshaller deve restituire un'istanza del CCW personalizzato per tale tipo. |
Pulizia (di codice nativo) | CleanUpNativeData | Consente al marshaller di pulire i dati nativi (CCW) restituiti dal MarshalManagedToNative metodo . |
Pulizia (di codice gestito) | CleanUpManagedData | Consente al marshaller di pulire i dati gestiti (RCW) restituiti dal MarshalNativeToManaged metodo . |
Informazioni (sul codice nativo) | GetNativeDataSize | Restituisce le dimensioni dei dati non gestiti da sottoporre a marshalling. |
Conversione
ICustomMarshaler.MarshalNativeToManaged
Effettua il marshalling di un puntatore ai dati nativi in un oggetto gestito. Questo metodo restituisce un wrapper chiamabile di runtime personalizzato (RCW) in grado di effettuare il marshalling dell'interfaccia non gestita passata come argomento. Il marshaller deve restituire un'istanza dell'RCW personalizzato per tale tipo.
ICustomMarshaler.MarshalManagedToNative
Effettua il marshalling di un oggetto gestito in un puntatore ai dati nativi. Questo metodo restituisce un wrapper com chiamabile personalizzato (CCW) in grado di effettuare il marshalling dell'interfaccia gestita passata come argomento. Il marshaller deve restituire un'istanza del CCW personalizzato per tale tipo.
Pulizia
ICustomMarshaler.CleanUpNativeData
Consente al marshaller di pulire i dati nativi (CCW) restituiti dal MarshalManagedToNative metodo .
ICustomMarshaler.CleanUpManagedData
Consente al marshaller di pulire i dati gestiti (RCW) restituiti dal MarshalNativeToManaged metodo .
Informazioni sulle dimensioni
ICustomMarshaler.GetNativeDataSize
Restituisce le dimensioni dei dati non gestiti da sottoporre a marshalling.
Nota
Se un marshaller personalizzato chiama i metodi che impostano l'ultimo errore P/Invoke durante il marshalling da nativo a gestito o durante la pulizia, il valore restituito da Marshal.GetLastWin32Error() e Marshal.GetLastPInvokeError() rappresenterà la chiamata nelle chiamate di marshalling o pulizia. Ciò può causare errori mancanti quando si usano marshaller personalizzati con P/Invoke con DllImportAttribute.SetLastError impostato su true
. Per mantenere l'ultimo errore P/Invoke, usare i Marshal.GetLastPInvokeError() metodi e Marshal.SetLastPInvokeError(Int32) nell'implementazione ICustomMarshaler .
Implementare il metodo GetInstance
Oltre a implementare l'interfaccia ICustomMarshaler , i marshaller personalizzati devono implementare un static
metodo denominato GetInstance
che accetta come String parametro e ha un tipo restituito di ICustomMarshaler. Questo static
metodo viene chiamato dal livello di interoperabilità COM di Common Language Runtime per creare un'istanza del marshaller personalizzato. La stringa passata a GetInstance
è un cookie che il metodo può usare per personalizzare il marshaller personalizzato restituito. L'esempio seguente mostra un'implementazione minima, ma completa ICustomMarshaler .
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();
}
Applicare MarshalAsAttribute
Per usare un marshaller personalizzato, è necessario applicare l'attributo al parametro o al campo di cui viene eseguito il MarshalAsAttribute marshalling.
È inoltre necessario passare il valore di UnmanagedType.CustomMarshaler enumerazione al MarshalAsAttribute costruttore. Inoltre, è necessario specificare il MarshalType campo con uno dei parametri denominati seguenti:
MarshalType (obbligatorio): nome completo dell'assembly del marshaller personalizzato. Il nome deve includere lo spazio dei nomi e la classe del marshaller personalizzato. Se il marshaller personalizzato non è definito nell'assembly in cui viene usato, è necessario specificare il nome dell'assembly in cui è definito.
Nota
È possibile usare il MarshalTypeRef campo anziché il MarshalType campo . MarshalTypeRef accetta un tipo più semplice da specificare.
MarshalCookie (facoltativo): cookie passato al marshaller personalizzato. È possibile usare il cookie per fornire informazioni aggiuntive al marshaller. Ad esempio, se lo stesso marshaller viene usato per fornire un certo numero di wrapper, il cookie identifica un wrapper specifico. Il cookie viene passato al
GetInstance
metodo del marshaller.
L'attributo MarshalAsAttribute identifica il marshaller personalizzato in modo che possa attivare il wrapper appropriato. Il servizio di interoperabilità di Common Language Runtime esamina quindi l'attributo e crea il marshaller personalizzato la prima volta che l'argomento (parametro o campo) deve essere sottoposto a marshalling.
Il runtime chiama quindi i MarshalNativeToManaged metodi e MarshalManagedToNative sul marshaller personalizzato per attivare il wrapper corretto per gestire la chiamata.
Usare un marshaller personalizzato
Al termine del marshaller personalizzato, è possibile usarlo come wrapper personalizzato per un determinato tipo. L'esempio seguente illustra la definizione dell'interfaccia IUserData
gestita:
interface IUserData
{
void DoSomeStuff(INew pINew);
}
Public Interface IUserData
Sub DoSomeStuff(pINew As INew)
End Interface
Nell'esempio seguente l'interfaccia IUserData
usa il NewOldMarshaler
marshaller personalizzato per consentire alle applicazioni client non gestite di passare un'interfaccia IOld
al DoSomeStuff
metodo . La descrizione gestita del DoSomeStuff
metodo accetta un'interfaccia INew
, come illustrato nell'esempio precedente, mentre la versione non gestita di DoSomeStuff
accetta un IOld
puntatore all'interfaccia, come illustrato nell'esempio seguente.
[uuid(9B2BAADA-0705-11D3-A0CD-00C04FA35826)]
library UserLib {
[uuid(9B2BABCD-0705-11D3-A0CD-00C04FA35826)]
interface IUserData : IUnknown
HRESULT DoSomeStuff(IUnknown* pIOld);
}
La libreria dei tipi generata esportando la definizione gestita di IUserData
restituisce la definizione non gestita illustrata in questo esempio anziché la definizione standard. L'attributo MarshalAsAttribute applicato all'argomento INew
nella definizione gestita del DoSomeStuff
metodo indica che l'argomento usa un marshaller personalizzato, come illustrato nell'esempio seguente.
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
Negli esempi precedenti il primo parametro fornito all'attributo MarshalAsAttribute è il UnmanagedType.CustomMarshaler valore UnmanagedType.CustomMarshaler
di enumerazione .
Il secondo parametro è il MarshalType campo , che fornisce il nome completo dell'assembly del marshaller personalizzato. Questo nome è costituito dallo spazio dei nomi e dalla classe del marshaller personalizzato (MarshalType="MyCompany.NewOldMarshaler"
).