Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Quando un client COM chiama un oggetto .NET, Common Language Runtime crea l'oggetto gestito e un wrapper chiamabile COM (CCW) per l'oggetto. Impossibile fare riferimento direttamente a un oggetto .NET, i client COM usano il CCW come proxy per l'oggetto gestito.
Il runtime crea esattamente un CCW per un oggetto gestito, indipendentemente dal numero di client COM che richiedono i servizi. Come illustrato nella seguente figura, diversi client COM possono mantenere un riferimento al CCW che espone l'interfaccia INew. Il CCW, a sua volta, contiene un unico riferimento all'oggetto gestito che implementa l'interfaccia e che viene successivamente sottoposto a garbage collection. Sia i client COM che .NET possono effettuare richieste nello stesso oggetto gestito contemporaneamente.
I wrapper chiamabili COM sono invisibili ad altre classi in esecuzione all'interno del runtime .NET. Il loro scopo principale è gestire il passaggio di chiamate tra codice gestito e non gestito; tuttavia, i CCW gestiscono anche l'identità dell'oggetto e la durata degli oggetti gestiti che avvolgono.
Identità dell'oggetto
Il runtime alloca la memoria per l'oggetto .NET dall'heap sottoposto a Garbage Collection, che consente al runtime di spostare l'oggetto in memoria in base alle esigenze. Al contrario, il runtime alloca la memoria per il CCW da un heap non raccolto, consentendo ai client COM di fare riferimento direttamente al wrapper.
Durata dell'oggetto
A differenza del client .NET che esegue il wrapping, il CCW viene conteggiato in modo tradizionale COM. Quando il conteggio dei riferimenti nel CCW raggiunge lo zero, il wrapper rilascia il riferimento sull'oggetto gestito. Durante il ciclo successivo di raccolta dei rifiuti viene raccolto un oggetto gestito senza riferimenti rimanenti.
Simulazione di interfacce COM
CCW espone tutte le interfacce pubbliche, visibili a COM, i tipi di dati e i valori restituiti ai client COM in modo coerente con l'imposizione di interazioni basate su interfaccia da parte di COM. Per un client COM, richiamare metodi su un oggetto .NET è identico a richiamare metodi su un oggetto COM.
Per creare questo approccio senza problemi, ccw produce interfacce COM tradizionali, ad esempio IUnknown e IDispatch. Come illustrato nella figura seguente, il CCW mantiene un singolo riferimento nell'oggetto .NET di cui esegue il wrapping. Sia il client COM che l'oggetto .NET interagiscono tra loro attraverso la costruzione di proxy e stub del CCW.
Oltre a esporre le interfacce implementate in modo esplicito da una classe nell'ambiente gestito, il runtime .NET fornisce implementazioni delle interfacce COM elencate nella tabella seguente per conto dell'oggetto. Una classe .NET può eseguire l'override del comportamento predefinito fornendo la propria implementazione di queste interfacce. Tuttavia, il runtime fornisce sempre l'implementazione per le interfacce IUnknown e IDispatch .
Interfaccia | Descrizione |
---|---|
IDispatch | Fornisce un meccanismo per l'associazione tardiva al tipo. |
IErrorInfo | Fornisce una descrizione testuale dell'errore, la relativa origine, un file della Guida, il contesto della Guida e il GUID dell'interfaccia che ha definito l'errore (sempre GUID_NULL per le classi .NET). |
IProvideClassInfo | Consente ai client COM di accedere all'interfaccia ITypeInfo implementata da una classe gestita. Restituisce COR_E_NOTSUPPORTED in .NET Core per i tipi non importati da COM. |
ISupportErrorInfo | Consente a un client COM di determinare se l'oggetto gestito supporta l'interfaccia IErrorInfo . In tal caso, consente al client di ottenere un puntatore all'oggetto di eccezione più recente. Tutti i tipi gestiti supportano l'interfaccia IErrorInfo . |
ITypeInfo (solo .NET Framework) | Fornisce informazioni sul tipo per una classe che corrisponde esattamente alle informazioni sul tipo generate da Tlbexp.exe. |
IUnknown | Fornisce l'implementazione standard dell'interfaccia IUnknown con cui il client COM gestisce la durata del CCW e fornisce la coercizione dei tipi. |
Una classe gestita può anche fornire le interfacce COM descritte nella tabella seguente.
Interfaccia | Descrizione |
---|---|
Interfaccia della classe (_classname) | Interfaccia, esposta dal runtime e non definita in modo esplicito, che espone tutte le interfacce, i metodi, le proprietà e i campi pubblici esposti in modo esplicito su un oggetto gestito. |
IConnectionPoint e IConnectionPointContainer | Interfaccia per oggetti che generano eventi basati su delegati (un'interfaccia per registrare i sottoscrittori di eventi). |
IDispatchEx (solo .NET Framework) | Interfaccia fornita dal runtime se la classe implementa IExpando. L'interfaccia IDispatchEx è un'estensione dell'interfaccia IDispatch che, a differenza di IDispatch, abilita l'enumerazione, l'aggiunta, l'eliminazione e la chiamata con distinzione tra maiuscole e minuscole dei membri. |
IEnumVARIANT | Interfaccia per le classi di tipo raccolta, che enumera gli oggetti nella raccolta se la classe implementa IEnumerable. |
Introduzione all'interfaccia della classe
L'interfaccia della classe, che non è definita in modo esplicito nel codice gestito, è un'interfaccia che espone tutti i metodi, le proprietà, i campi e gli eventi pubblici esposti in modo esplicito nell'oggetto .NET. Questa interfaccia può essere un'interfaccia doppia o a singolo invio. L'interfaccia della classe riceve il nome della classe .NET stessa, preceduta da un carattere di sottolineatura. Ad esempio, per la classe Mammal, l'interfaccia della classe è _Mammal.
Per le classi derivate, l'interfaccia della classe espone anche tutti i metodi, le proprietà e i campi pubblici della classe base. La classe derivata espone anche un'interfaccia di classe per ogni classe di base. Ad esempio, se la classe Mammal estende la classe MammalSuperclass, che si estende System.Object, l'oggetto .NET espone ai client COM tre interfacce di classe denominate _Mammal, _MammalSuperclass e _Object.
Si consideri ad esempio la classe .NET seguente:
' Applies the ClassInterfaceAttribute to set the interface to dual.
<ClassInterface(ClassInterfaceType.AutoDual)> _
' Implicitly extends System.Object.
Public Class Mammal
Sub Eat()
Sub Breathe()
Sub Sleep()
End Class
// Applies the ClassInterfaceAttribute to set the interface to dual.
[ClassInterface(ClassInterfaceType.AutoDual)]
// Implicitly extends System.Object.
public class Mammal
{
public void Eat() {}
public void Breathe() {}
public void Sleep() {}
}
Il client COM può ottenere un puntatore a un'interfaccia di classe denominata _Mammal
. In .NET Framework è possibile usare lo strumento di esportazione della libreria dei tipi (Tlbexp.exe) per generare una libreria dei tipi contenente la definizione dell'interfaccia _Mammal
. L'utilità di esportazione della libreria dei tipi non è supportata in .NET Core. Se la Mammal
classe ha implementato una o più interfacce, le interfacce verranno visualizzate sotto la coclasse.
[odl, uuid(…), hidden, dual, nonextensible, oleautomation]
interface _Mammal : IDispatch
{
[id(0x00000000), propget] HRESULT ToString([out, retval] BSTR*
pRetVal);
[id(0x60020001)] HRESULT Equals([in] VARIANT obj, [out, retval]
VARIANT_BOOL* pRetVal);
[id(0x60020002)] HRESULT GetHashCode([out, retval] short* pRetVal);
[id(0x60020003)] HRESULT GetType([out, retval] _Type** pRetVal);
[id(0x6002000d)] HRESULT Eat();
[id(0x6002000e)] HRESULT Breathe();
[id(0x6002000f)] HRESULT Sleep();
}
[uuid(…)]
coclass Mammal
{
[default] interface _Mammal;
}
La generazione dell'interfaccia della classe è facoltativa. Per impostazione predefinita, l'interoperabilità COM genera un'interfaccia di sola distribuzione per ogni classe che esporti in una libreria dei tipi. È possibile impedire o modificare la creazione automatica di questa interfaccia applicando ClassInterfaceAttribute alla tua classe. Anche se l'interfaccia della classe può semplificare l'esposizione di classi gestite a COM, i relativi usi sono limitati.
Attenzione
L'uso dell'interfaccia della classe, invece di definire in modo esplicito il proprio, può complicare il controllo delle versioni future della classe gestita. Leggere le linee guida seguenti prima di usare l'interfaccia della classe.
Definire un'interfaccia esplicita per i client COM da usare anziché generare l'interfaccia della classe.
Poiché l'interoperabilità COM genera automaticamente un'interfaccia di classe, le modifiche post-versione alla classe possono modificare il layout dell'interfaccia della classe esposta da Common Language Runtime. Poiché i client COM sono in genere impreparati per gestire le modifiche nel layout di un'interfaccia, si interrompono se si modifica il layout del membro della classe.
Questa linea guida rafforza il concetto che le interfacce esposte ai client COM devono rimanere immutabili. Per ridurre il rischio di interrompere i client COM riordinando inavvertitamente il layout dell'interfaccia, isolare tutte le modifiche alla classe dal layout dell'interfaccia definendo in modo esplicito le interfacce.
Usare ClassInterfaceAttribute per annullare la generazione automatica dell'interfaccia della classe e implementare un'interfaccia esplicita per la classe, come illustrato nel frammento di codice seguente:
<ClassInterface(ClassInterfaceType.None)>Public Class LoanApp
Implements IExplicit
Sub M() Implements IExplicit.M
…
End Class
[ClassInterface(ClassInterfaceType.None)]
public class LoanApp : IExplicit
{
int IExplicit.M() { return 0; }
}
Il valore ClassInterfaceType.None impedisce la generazione dell'interfaccia della classe quando i metadati della classe vengono esportati in una libreria dei tipi. Nell'esempio precedente, i client COM possono accedere alla LoanApp
classe solo tramite l'interfaccia IExplicit
.
Evitare di memorizzare nella cache gli identificatori di dispatch (DispIds)
L'uso dell'interfaccia della classe è un'opzione accettabile per i client con script, i client di Microsoft Visual Basic 6.0 o qualsiasi client con associazione tardiva che non memorizza nella cache i DispId dei membri dell'interfaccia. DispId identifica i membri dell'interfaccia per abilitare l'associazione tardiva.
Per l'interfaccia della classe, la generazione di DispIds si basa sulla posizione del membro nell'interfaccia . Se si modifica l'ordine del membro ed esporta la classe in una libreria dei tipi, i DispId generati nell'interfaccia della classe verranno modificati.
Per evitare l'interruzione dei client COM ad associazione tardiva quando si utilizza l'interfaccia di classe, applicare ClassInterfaceAttribute con il valore ClassInterfaceType.AutoDispatch. Questo valore implementa un'interfaccia di classe solo dispatch, ma omette la descrizione dell'interfaccia dalla libreria dei tipi. Senza una descrizione dell'interfaccia, i client non possono memorizzare nella cache i DispId in fase di compilazione. Anche se si tratta del tipo di interfaccia predefinito per l'interfaccia della classe, è possibile applicare il valore dell'attributo in modo esplicito.
<ClassInterface(ClassInterfaceType.AutoDispatch)> Public Class LoanApp
Implements IAnother
Sub M() Implements IAnother.M
…
End Class
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class LoanApp
{
public int M() { return 0; }
}
Per ottenere il DispId di un membro dell'interfaccia in fase di esecuzione, i client COM possono chiamare IDispatch.GetIdsOfNames. Per richiamare un metodo sull'interfaccia, passare l'oggetto DispId restituito come argomento a IDispatch.Invoke.
Limitare l'uso dell'opzione di interfaccia doppia per l'interfaccia della classe.
Le interfacce doppie consentono l'associazione anticipata e tardiva ai membri dell'interfaccia da parte dei client COM. In fase di progettazione e durante i test, potrebbe risultare utile impostare l'interfaccia della classe su doppia. Per una classe gestita (e le relative classi di base) che non verranno mai modificate, questa opzione è accettabile. In tutti gli altri casi, evitare di impostare l'interfaccia della classe su doppia.
Un'interfaccia doppia generata automaticamente potrebbe essere appropriata in rari casi; Tuttavia, più spesso crea complessità correlata alla versione. Ad esempio, i client COM che usano l'interfaccia di classe di una classe derivata possono interrompere facilmente le modifiche alla classe base. Quando una terza parte fornisce la classe di base, il layout dell'interfaccia della classe è fuori dal tuo controllo. Inoltre, a differenza di un'interfaccia dispatch-only, un'interfaccia duale (ClassInterfaceType.AutoDual) fornisce una descrizione dell'interfaccia di classe nella libreria dei tipi esportata. Tale descrizione incoraggia i client con associazione tardiva a memorizzare nella cache i DispIds durante la fase di compilazione.
Assicurarsi che tutte le notifiche degli eventi COM siano associate in ritardo.
Per impostazione predefinita, le informazioni sul tipo COM vengono incorporate direttamente negli assembly gestiti, eliminando la necessità degli assembly di interoperabilità primari. Tuttavia, una delle limitazioni delle informazioni sul tipo incorporato è che non supporta il recapito delle notifiche degli eventi COM da chiamate di tabella virtuale con associazione anticipata, ma supporta solo chiamate ad associazione IDispatch::Invoke
tardiva.
Se l'applicazione richiede chiamate con associazione anticipata ai metodi dell'interfaccia evento COM, è possibile impostare la proprietà Incorpora tipi di interoperabilità in Visual Studio su true
o includere l'elemento seguente nel file di progetto:
<EmbedInteropTypes>True</EmbedInteropTypes>