Kvalifikace typů .NET pro spolupráci modelu COM

Zveřejnění typů .NET pro com

Pokud máte v úmyslu zveřejnit typy v sestavení aplikacím MODELU COM, zvažte požadavky zprostředkovatele komunikace modelu COM v době návrhu. Spravované typy (třída, rozhraní, struktura a výčty) se bezproblémově integrují s typy modelu COM, pokud dodržujete následující pokyny:

  • Třídy by měly implementovat rozhraní explicitně.

    Přestože interoperabilita modelu COM poskytuje mechanismus pro automatické generování rozhraní obsahujícího všechny členy třídy a členů její základní třídy, je mnohem lepší poskytovat explicitní rozhraní. Automaticky generované rozhraní se nazývá rozhraní třídy. Pokyny najdete v tématu Úvod do rozhraní třídy.

    Pomocí jazyka Visual Basic, C# a C++ můžete do kódu začlenit definice rozhraní, a nemusíte tak používat jazyk IDL (Interface Definition Language) nebo jeho ekvivalent. Podrobnosti o syntaxi najdete v dokumentaci k jazyku.

  • Spravované typy musí být veřejné.

    Do knihovny typů se registrují a exportují pouze veřejné typy v sestavení. V důsledku toho jsou pro com viditelné pouze veřejné typy.

    Spravované typy zveřejňují funkce pro jiný spravovaný kód, který nemusí být vystaven modelu COM. Například parametrizované konstruktory, statické metody a konstantní pole nejsou vystaveny klientům modelu COM. Kromě toho, protože modul runtime zařazuje data do a z nějakého typu, můžou se data zkopírovat nebo transformovat.

  • Metody, vlastnosti, pole a události musí být veřejné.

    Členové veřejných typů musí být také veřejné, pokud mají být viditelné modelu COM. Viditelnost sestavení, veřejného typu nebo veřejných členů veřejného typu můžete omezit použitím objektu ComVisibleAttribute. Ve výchozím nastavení jsou viditelné všechny veřejné typy a členy.

  • Typy musí mít veřejný konstruktor bez parametrů, který se má aktivovat z modelu COM.

    Spravované, veřejné typy jsou viditelné pro com. Bez veřejného konstruktoru bez parametrů (konstruktor bez argumentů) však klienti modelu COM nemohou typ vytvořit. Klienti modelu COM můžou typ dál používat, pokud je aktivovaný jiným způsobem.

  • Typy nemohou být abstraktní.

    Klienti modelu COM ani klienti .NET nemohou vytvářet abstraktní typy.

Při exportu do modelu COM se hierarchie dědičnosti spravovaného typu zploštěla. Správa verzí se také liší mezi spravovanými a nespravovanými prostředími. Typy vystavené modelu COM nemají stejné charakteristiky správy verzí jako jiné spravované typy.

Využívání typů modelu COM z .NET

Pokud máte v úmyslu využívat typy modelu COM z .NET a nechcete používat nástroje, jako je Tlbimp.exe (Import knihovny typů), musíte postupovat podle těchto pokynů:

  • Rozhraní musí obsahovat ComImportAttribute použité rozhraní.
  • Rozhraní musí mít GuidAttribute použité rozhraní s ID rozhraní modelu COM.
  • Rozhraní by měla být InterfaceTypeAttribute použita k určení základního typu rozhraní tohoto rozhraní (IUnknown, IDispatchnebo IInspectable).
    • Výchozí možností je mít základní typ IDispatch a připojit deklarované metody k očekávané virtuální tabulce funkcí pro rozhraní.
    • Pouze rozhraní .NET Framework podporuje zadávání základního IInspectabletypu .

Tyto pokyny poskytují minimální požadavky pro běžné scénáře. Existuje mnoho dalších možností přizpůsobení a jsou popsány v části Použití atributů vzájemné spolupráce.

Definování rozhraní MODELU COM v .NET

Když se kód .NET pokusí volat metodu objektu COM prostřednictvím rozhraní s ComImportAttribute atributem, musí vytvořit virtuální tabulku funkcí (označovanou také jako vtable nebo vftable), aby vytvořila definici rozhraní .NET, aby bylo možné určit nativní kód, který se má volat. Tento proces je složitý. Následující příklady ukazují některé jednoduché případy.

Zvažte rozhraní MODELU COM s několika metodami:

struct IComInterface : public IUnknown
{
    STDMETHOD(Method)() = 0;
    STDMETHOD(Method2)() = 0;
};

Pro toto rozhraní popisuje následující tabulka rozložení tabulky virtuálních funkcí:

IComInterface slot tabulky virtuálních funkcí Název metody
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2

Každá metoda se přidá do tabulky virtuálních funkcí v pořadí, v jakém byla deklarována. Konkrétní pořadí je definováno kompilátorem jazyka C++, ale v jednoduchých případech bez přetížení definuje pořadí deklarací pořadí v tabulce.

Deklarujte rozhraní .NET, které odpovídá tomuto rozhraní následujícím způsobem:

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid(/* The IID for IComInterface */)]
interface IComInterface
{
    void Method();
    void Method2();
}

Určuje InterfaceTypeAttribute základní rozhraní. Nabízí několik možností:

ComInterfaceType Hodnotu Základní typ rozhraní Chování členů v atributovaném rozhraní
InterfaceIsIUnknown IUnknown Tabulka virtuálních funkcí má nejprve členy IUnknown, pak členy tohoto rozhraní v pořadí deklarace.
InterfaceIsIDispatch IDispatch Členy nejsou přidány do tabulky virtuálních funkcí. Jsou přístupné jenom prostřednictvím IDispatch.
InterfaceIsDual IDispatch Tabulka virtuálních funkcí má nejprve členy IDispatch, pak členy tohoto rozhraní v pořadí deklarace.
InterfaceIsIInspectable IInspectable Tabulka virtuálních funkcí má nejprve členy IInspectable, pak členy tohoto rozhraní v pořadí deklarace. Podporováno pouze v rozhraní .NET Framework.

Dědičnost rozhraní COM a .NET

Systém komunikace s objekty COM, který využívá ComImportAttribute dědičnost rozhraní, proto může způsobit neočekávané chování, pokud nejsou přijata některá zmírňujících kroků.

Zdrojový generátor modelu COM, který používá System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute atribut, komunikuje s dědičností rozhraní, takže se chová podle očekávání.

Dědičnost rozhraní MODELU COM v jazyce C++

V jazyce C++ můžou vývojáři deklarovat rozhraní MODELU COM, která jsou odvozena z jiných rozhraní modelu COM, následujícím způsobem:

struct IComInterface : public IUnknown
{
    STDMETHOD(Method)() = 0;
    STDMETHOD(Method2)() = 0;
};

struct IComInterface2 : public IComInterface
{
    STDMETHOD(Method3)() = 0;
};

Tento styl deklarace se pravidelně používá jako mechanismus pro přidání metod do objektů MODELU COM beze změny stávajících rozhraní, což by byla zásadní změna. Výsledkem tohoto mechanismu dědičnosti jsou následující rozložení tabulky virtuálních funkcí:

IComInterface slot tabulky virtuálních funkcí Název metody
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
IComInterface2 slot tabulky virtuálních funkcí Název metody
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
5 IComInterface2::Method3

V důsledku toho je snadné volat metodu definovanou IComInterface z objektu IComInterface2*. Konkrétně volání metody na základní rozhraní nevyžaduje volání pro QueryInterface získání ukazatele na základní rozhraní. Kromě toho C++ umožňuje implicitní převod z IComInterface2*IComInterface*, který je dobře definovaný a umožňuje vyhnout se volání QueryInterface znovu. V důsledku toho v jazyce C nebo C++ nemusíte volat QueryInterface , abyste se dostali k základnímu typu, pokud nechcete, což může umožnit určitá vylepšení výkonu.

Poznámka:

Rozhraní WinRT nedodržují tento model dědičnosti. Jsou definované tak, aby sledovaly stejný model jako [ComImport]model komunikace modelu COM v .NET.

Dědičnost rozhraní s využitím ComImportAttribute

V .NET kód jazyka C#, který vypadá jako dědičnost rozhraní, ve skutečnosti dědičnost rozhraní není. Uvažujte následující kód:

interface I
{
    void Method1();
}
interface J : I
{
    void Method2();
}

Tento kód neřekne, "J implements I." Kód ve skutečnosti říká, že "jakýkoli typ, který implementuje J , musí také implementovat I." Tento rozdíl vede k základnímu rozhodnutí o návrhu, které dělá dědičnost rozhraní v ComImportAttribute-založené vzájemné spolupráce unergonomic. Rozhraní jsou vždy považována za vlastní; Základní seznam rozhraní rozhraní nemá žádný vliv na žádné výpočty k určení tabulky virtuálních funkcí pro dané rozhraní .NET.

V důsledku toho přirozený ekvivalent předchozího příkladu rozhraní modelu COM jazyka C++ vede k jinému rozložení tabulky virtuálních funkcí.

Kód C#:

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
    void Method();
    void Method2();
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
    void Method3();
}

Rozložení tabulky virtuálních funkcí:

IComInterface slot tabulky virtuálních funkcí Název metody
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
IComInterface2 slot tabulky virtuálních funkcí Název metody
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface2::Method3

Vzhledem k tomu, že se tyto tabulky virtuálních funkcí liší od příkladu jazyka C++, povede to k vážným problémům za běhu. Správná definice těchto rozhraní v .NET s ComImportAttribute následujícím způsobem:

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
    void Method();
    void Method2();
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
    new void Method();
    new void Method2();
    void Method3();
}

Na úrovni metadat neimplementuje, IComInterface2 ale pouze určuje, že implementátory IComInterface2 musí také implementovat IComInterface.IComInterface Proto musí být každá metoda ze základních typů rozhraní předefinována.

Dědičnost rozhraní s GeneratedComInterfaceAttribute (.NET 8 a novějším)

Generátor zdroje modelu COM aktivovaný GeneratedComInterfaceAttribute implementací dědičnosti rozhraní jazyka C# jako dědičnosti rozhraní MODELU COM, takže virtuální tabulky funkcí jsou rozloženy podle očekávání. Pokud vezmete předchozí příklad, správná definice těchto rozhraní v .NET s System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute následujícím způsobem:

[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
    void Method();
    void Method2();
}

[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
    void Method3();
}

Metody základních rozhraní nemusí být předefinovány a neměly by být předefinovány. Následující tabulka popisuje výsledné virtuální tabulky funkcí:

IComInterface slot tabulky virtuálních funkcí Název metody
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
IComInterface2 slot tabulky virtuálních funkcí Název metody
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
5 IComInterface2::Method3

Jak vidíte, tyto tabulky odpovídají příkladu jazyka C++, takže tato rozhraní budou fungovat správně.

Viz také