Obálka volatelná aplikacemi COM

Jakmile klient modelu COM zavolá objekt rozhraní .NET, vytvoří modul CLR (Common Language Runtime) pro daný objekt spravovaný objekt a obálku volatelnou modelem COM (CCW). Pokud nelze odkazovat na objekt rozhraní .NET přímo, budou klienti modelu COM pro spravovaný objekt používat objekt CCW jako proxy.

Modul runtime vytvoří pro spravovaný objekt přesně jeden objekt CCW, bez ohledu na počet klientů modelu COM, kteří vyžadují služby modelu. Jak znázorňuje následující obrázek, může větší počet klientů modelu COM používat odkaz na objekt CCW, který zpřístupňuje rozhraní INew. Na druhou stranu platí, že objekt CCW obsahuje jediný odkaz na spravovaný objekt, který implementuje rozhraní a u kterého je prováděno uvolnění paměti. Klienti modelu COM a rozhraní .NET mohou vytvářet požadavky na stejný spravovaný objekt současně.

Multiple COM clients holding a reference to the CCW that exposes INew.

Obálky com, které lze volat, jsou neviditelné pro jiné třídy spuštěné v rámci modulu runtime .NET. Jejich hlavním účelem je zařadit volání mezi spravovaný a nespravovaný kód. Nicméně objekty CCW provádějí také správu identity a doby života spravovaných objektů, které zabalují.

Identita objektu

Modul runtime přiděluje paměť objektu rozhraní .NET z množství uvolněné paměti, což modulu runtime umožňuje přesouvat objekt v paměti podle potřeby. Naopak modul runtime přiděluje paměť pro objekt CCW z nenashromážděného množství, což klientům modelu COM umožňuje přímo odkazovat na obálku.

Doba života objektu

Na rozdíl od objektu, který provádí zabalení klienta rozhraní .NET, je objekt CCW založen na tradičním způsobu počítání odkazů modelu COM. Pokud v případě objektů CCW dosáhne počet odkazů nuly, uvolní obálka svůj odkaz na spravovaný objekt. Spravovaný objekt, který již nemá žádné další odkazy, je shromážděn v rámci dalšího cyklu uvolňování paměti.

Simulace rozhraní MODELU COM

CcW zveřejňuje všechna veřejná rozhraní, datové typy, datové typy a vrací hodnoty klientům MODELU COM způsobem, který je konzistentní s vynucením interakce modelu COM na základě rozhraní. Pro klienta MODELU COM je vyvolání metod objektu .NET stejné jako vyvolání metod v objektu COM.

Pro vytvoření tohoto bezproblémového přístupu ccW vyrábí tradiční rozhraní MODELU COM, jako jsou IUnknown a IDispatch. Jak ukazuje následující obrázek, CCW udržuje jeden odkaz na objekt .NET, který zabalí. Klient MODELU COM i objekt .NET vzájemně komunikují prostřednictvím proxy serveru a konstrukce zástupných procedur CCW.

Diagram that shows how CCW manufactures COM interfaces.

Kromě zveřejnění rozhraní, která jsou explicitně implementována třídou ve spravovaném prostředí, modul runtime .NET poskytuje implementace rozhraní COM uvedené v následující tabulce jménem objektu. Třída .NET může přepsat výchozí chování tím, že poskytuje vlastní implementaci těchto rozhraní. Modul runtime však vždy poskytuje implementaci pro rozhraní IUnknown a IDispatch .

Rozhraní Popis
Idispatch Poskytuje mechanismus pro pozdní vazbu k typu.
Ierrorinfo Poskytuje textový popis chyby, její zdroj, soubor nápovědy, kontext nápovědy a identifikátor GUID rozhraní, které definovalo chybu (vždy GUID_NULL pro třídy .NET).
IProvideClassInfo Umožňuje klientům modelu COM získat přístup k rozhraní ITypeInfo implementované spravovanou třídou. Vrátí v COR_E_NOTSUPPORTED .NET Core pro typy, které se neimportují z modelu COM.
ISupportErrorInfo Umožňuje klientovi modelu COM určit, zda spravovaný objekt podporuje rozhraní IErrorInfo . Pokud ano, umožní klientovi získat ukazatel na nejnovější objekt výjimky. Všechny spravované typy podporují rozhraní IErrorInfo .
ITypeInfo (pouze .NET Framework) Poskytuje informace o typu pro třídu, která je přesně stejná jako informace o typu vytvořené Tlbexp.exe.
IUnknown Poskytuje standardní implementaci rozhraní IUnknown , se kterým klient COM spravuje životnost CCW a poskytuje typ převodu.

Spravovaná třída může také poskytovat rozhraní MODELU COM popsaná v následující tabulce.

Rozhraní Popis
Rozhraní třídy (_classname) Rozhraní vystavené modulem runtime, které není explicitně definováno, které zveřejňuje všechna veřejná rozhraní, metody, vlastnosti a pole explicitně vystavená u spravovaného objektu.
I Připojení ionPoint a I Připojení ionPointContainer Rozhraní pro objekty, které zdrojové události založené na delegátech (rozhraní pro registraci odběratelů událostí).
IDispatchEx (pouze .NET Framework) Rozhraní dodané modulem runtime, pokud třída implementuje IExpando. IDispatchEx rozhraní je rozšíření IDispatch rozhraní, které, na rozdíl od IDispatch, umožňuje výčtu, přidání, odstranění a rozlišování malých a velkých písmen volání členů.
IEnumVARIANT Rozhraní pro třídy typu kolekce, které výčet objektů v kolekci, pokud třída implementuje IEnumerable.

Představujeme rozhraní třídy

Rozhraní třídy, které není explicitně definováno ve spravovaném kódu, je rozhraní, které zveřejňuje všechny veřejné metody, vlastnosti, pole a události, které jsou explicitně vystaveny v objektu .NET. Toto rozhraní může být duální nebo dispečerské rozhraní. Rozhraní třídy obdrží název samotné třídy .NET před podtržítkem. Například u třídy Savce je rozhraní třídy _Mammal.

Pro odvozené třídy rozhraní třídy také zveřejňuje všechny veřejné metody, vlastnosti a pole základní třídy. Odvozená třída také zveřejňuje rozhraní třídy pro každou základní třídu. Pokud například třída Savce rozšiřuje třídu SavceSuperclass, která sama rozšiřuje System.Object, objekt .NET zveřejňuje klientům MODELU COM tři rozhraní třídy s názvem _Mammal, _MammalSuperclass a _Object.

Představte si například následující třídu .NET:

' 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() {}
}

Klient MODELU COM může získat ukazatel na rozhraní třídy s názvem _Mammal. V rozhraní .NET Framework můžete pomocí nástroje Pro export typů knihovny (Tlbexp.exe) vygenerovat knihovnu typů obsahující definici _Mammal rozhraní. Exportér knihovny typů není v .NET Core podporován. Mammal Pokud třída implementovala jedno nebo více rozhraní, rozhraní by se zobrazila v rámci třídy coclass.

[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;
}

Generování rozhraní třídy je volitelné. Ve výchozím nastavení generuje zprostředkovatele komunikace modelu COM rozhraní určené pouze pro odesílání pro každou třídu, kterou exportujete do knihovny typů. Automatické vytváření tohoto rozhraní můžete zabránit nebo upravit použitím třídy ClassInterfaceAttribute . I když rozhraní třídy může usnadnit úlohu vystavení spravovaných tříd modelu COM, jeho použití jsou omezené.

Upozornění

Použití rozhraní třídy, místo explicitního definování vlastní, může komplikovat budoucí správu verzí spravované třídy. Před použitím rozhraní třídy si přečtěte následující pokyny.

Definujte explicitní rozhraní pro klienty MODELU COM, které se mají použít místo generování rozhraní třídy.

Vzhledem k tomu, že interop modelu COM generuje rozhraní třídy automaticky, změny po verzi třídy mohou změnit rozložení rozhraní třídy vystavené modulem CLR (Common Language Runtime). Vzhledem k tomu, že klienti modelu COM jsou obvykle nepřipraveni ke zpracování změn v rozložení rozhraní, přeruší se, pokud změníte rozložení člena třídy.

Toto vodítko posiluje pojem, že rozhraní vystavená klientům modelu COM musí zůstat neměnná. Chcete-li snížit riziko narušení klientů modelu COM neúmyslným přeuspořádání rozložení rozhraní, izolujte všechny změny třídy od rozložení rozhraní explicitním definováním rozhraní.

Použijte ClassInterfaceAttribute k odpojování automatického generování rozhraní třídy a implementaci explicitního rozhraní pro třídu, jak ukazuje následující fragment kódu:

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

Hodnota ClassInterfaceType.None zabraňuje generování rozhraní třídy při exportu metadat třídy do knihovny typů. V předchozím příkladu mají klienti MODELU COM přístup ke LoanApp třídě pouze prostřednictvím IExplicit rozhraní.

Vyhněte se ukládání identifikátorů odesílání do mezipaměti (DispId)

Použití rozhraní třídy je přijatelná možnost pro skriptované klienty, klienty Microsoft Visual Basic 6.0 nebo jakéhokoli opožděného klienta, který neukládá do mezipaměti DispId členů rozhraní. Identifikátory DispId identifikují členy rozhraní pro povolení pozdní vazby.

Pro rozhraní třídy je generování DispIds založeno na pozici člena v rozhraní. Pokud změníte pořadí člena a exportujete třídu do knihovny typů, změníte DispIds vygenerované v rozhraní třídy.

Chcete-li zabránit přerušení pozdní vazby com klienti při použití rozhraní třídy, použijte ClassInterfaceAttribute s ClassInterfaceType.AutoDispatch hodnotu. Tato hodnota implementuje rozhraní třídy pouze pro odesílání, ale vynechá popis rozhraní z knihovny typů. Bez popisu rozhraní klienti nemůžou v době kompilace ukládat identifikátory DispId do mezipaměti. I když se jedná o výchozí typ rozhraní pro rozhraní třídy, můžete hodnotu atributu použít explicitně.

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

Chcete-li získat DispId člen rozhraní v době běhu, klienti COM mohou volat IDispatch.GetIdsOfNames. Chcete-li vyvolat metodu v rozhraní, předejte vrácený DispId jako argument IDispatch.Invoke.

Omezte použití možnosti duálního rozhraní pro rozhraní třídy.

Duální rozhraní umožňují včasné a pozdní vazby na členy rozhraní klienty MODELU COM. Při návrhu a během testování může být užitečné nastavit rozhraní třídy na duální. Pro spravovanou třídu (a její základní třídy), která se nikdy nezmění, je tato možnost také přijatelná. Ve všech ostatních případech se vyhněte nastavení rozhraní třídy na duální.

Automaticky generované duální rozhraní může být vhodné ve výjimečných případech; ale častěji vytváří složitost související s verzí. Například klienti modelu COM používající rozhraní třídy odvozené třídy mohou snadno přerušit změny základní třídy. Pokud třetí strana poskytuje základní třídu, rozložení rozhraní třídy je mimo vaši kontrolu. Na rozdíl od rozhraní pouze pro odesílání poskytuje duální rozhraní (ClassInterfaceType.AutoDual) popis rozhraní třídy v exportované knihovně typů. Takový popis podporuje opožděné klienty, aby v době kompilace ukašly dispidy.

Ujistěte se, že všechna oznámení událostí modelu COM jsou zpožděná.

Ve výchozím nastavení jsou informace o typu modelu COM vloženy přímo do spravovaných sestavení, což eliminuje potřebu primárních sestavení vzájemné spolupráce (PIA). Jedním z omezení informací o vloženém typu je, že nepodporuje doručování oznámení událostí modelu COM voláními vtable s časnou vazbou, ale podporuje pouze opožděná IDispatch::Invoke volání.

Pokud vaše aplikace vyžaduje volání rozhraní událostí modelu COM s časnou vazbou, můžete v sadě Visual Studio truenastavit vlastnost Embed Interop Types nebo zahrnout do souboru projektu následující prvek:

<EmbedInteropTypes>True</EmbedInteropTypes>

Viz také