Kwalifikowanie typów platformy .NET dla współdziałania modelu COM
Uwidaczniaj typy platformy .NET w modelu COM
Jeśli zamierzasz uwidocznić typy w zestawie w aplikacjach COM, należy wziąć pod uwagę wymagania międzyoperacjności modelu COM w czasie projektowania. Typy zarządzane (klasa, interfejs, struktura i wyliczenie) bezproblemowo integrują się z typami COM, gdy są zgodne z następującymi wytycznymi:
Klasy powinny jawnie implementować interfejsy.
Mimo że interop COM zapewnia mechanizm automatycznego generowania interfejsu zawierającego wszystkie elementy członkowskie klasy i składowych jej klasy bazowej, znacznie lepiej jest zapewnić jawne interfejsy. Automatycznie wygenerowany interfejs jest nazywany interfejsem klasy. Aby uzyskać wskazówki, zobacz Wprowadzenie do interfejsu klasy.
Możesz użyć języka Visual Basic, C# i C++ do uwzględnienia definicji interfejsu w kodzie, zamiast używać języka IDL (Interface Definition Language) lub jego odpowiednika. Aby uzyskać szczegółowe informacje o składni, zobacz dokumentację języka.
Typy zarządzane muszą być publiczne.
Tylko typy publiczne w zestawie są rejestrowane i eksportowane do biblioteki typów. W związku z tym tylko typy publiczne są widoczne dla modelu COM.
Typy zarządzane udostępniają funkcje innym zarządzanym kodzie, który może nie być uwidoczniony w modelu COM. Na przykład konstruktory sparametryzowane, metody statyczne i pola stałe nie są widoczne dla klientów COM. Ponadto, ponieważ środowisko uruchomieniowe przeprowadza marshaling danych w i poza typem, dane mogą być kopiowane lub przekształcane.
Metody, właściwości, pola i zdarzenia muszą być publiczne.
Elementy członkowskie typów publicznych muszą być również publiczne, jeśli mają być widoczne dla modelu COM. Można ograniczyć widoczność zestawu, typu publicznego lub publicznych elementów członkowskich typu publicznego, stosując element ComVisibleAttribute. Domyślnie wszystkie typy publiczne i elementy członkowskie są widoczne.
Typy muszą mieć publiczny konstruktor bez parametrów do aktywowania z modelu COM.
Zarządzane typy publiczne są widoczne dla modelu COM. Jednak bez publicznego konstruktora bez parametrów (konstruktor bez argumentów) klienci COM nie mogą utworzyć typu. Klienci COM nadal mogą używać typu, jeśli jest aktywowany w inny sposób.
Typy nie mogą być abstrakcyjne.
Ani klienci COM, ani klienci platformy .NET nie mogą tworzyć typów abstrakcyjnych.
W przypadku eksportowania do modelu COM hierarchia dziedziczenia typu zarządzanego jest spłaszczona. Przechowywanie wersji różni się również między środowiskami zarządzanymi i niezarządzanych. Typy uwidocznione w modelu COM nie mają takich samych właściwości przechowywania wersji, jak inne typy zarządzane.
Korzystanie z typów COM z platformy .NET
Jeśli zamierzasz korzystać z typów COM z platformy .NET i nie chcesz używać narzędzi takich jak Tlbimp.exe (importer biblioteki typów), musisz postępować zgodnie z następującymi wytycznymi:
- Interfejsy muszą mieć ComImportAttribute zastosowane.
- Interfejsy muszą mieć GuidAttribute zastosowane z identyfikatorem interfejsu dla interfejsu COM.
- Interfejsy powinny mieć InterfaceTypeAttribute zastosowane do określenia podstawowego typu interfejsu tego interfejsu (
IUnknown
,IDispatch
, lubIInspectable
).- Domyślną opcją jest posiadanie podstawowego
IDispatch
typu i dołączenie zadeklarowanych metod do oczekiwanej tabeli funkcji wirtualnych dla interfejsu. - Tylko program .NET Framework obsługuje określanie podstawowego
IInspectable
typu .
- Domyślną opcją jest posiadanie podstawowego
Te wytyczne zawierają minimalne wymagania dotyczące typowych scenariuszy. Istnieje wiele innych opcji dostosowywania i opisano je w temacie Stosowanie atrybutów międzyoperacyjnych.
Definiowanie interfejsów COM na platformie .NET
Gdy kod platformy .NET próbuje wywołać metodę w obiekcie COM za pomocą interfejsu z atrybutem ComImportAttribute , musi utworzyć tabelę funkcji wirtualnych (znaną również jako vtable lub vftable), aby utworzyć definicję platformy .NET interfejsu w celu określenia kodu natywnego do wywołania. Ten proces jest złożony. W poniższych przykładach przedstawiono kilka prostych przypadków.
Rozważ interfejs COM z kilkoma metodami:
struct IComInterface : public IUnknown
{
STDMETHOD(Method)() = 0;
STDMETHOD(Method2)() = 0;
};
W przypadku tego interfejsu poniższa tabela zawiera opis układu tabeli funkcji wirtualnych:
IComInterface gniazdo tabeli funkcji wirtualnej |
Nazwa metody |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface::Method |
100 | IComInterface::Method2 |
Każda metoda jest dodawana do tabeli funkcji wirtualnych w kolejności deklarowanej. Określona kolejność jest definiowana przez kompilator języka C++, ale w przypadku prostych przypadków bez przeciążeń kolejność deklaracji definiuje kolejność w tabeli.
Zadeklaruj interfejs .NET odpowiadający temu interfejsowi w następujący sposób:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid(/* The IID for IComInterface */)]
interface IComInterface
{
void Method();
void Method2();
}
Parametr InterfaceTypeAttribute określa interfejs podstawowy. Udostępnia kilka opcji:
ComInterfaceType Wartość | Typ interfejsu podstawowego | Zachowanie elementów członkowskich w interfejsie przypisanym |
---|---|---|
InterfaceIsIUnknown |
IUnknown |
Tabela funkcji wirtualnych IUnknown ma najpierw elementy członkowskie , a następnie elementy członkowskie tego interfejsu w kolejności deklaracji. |
InterfaceIsIDispatch |
IDispatch |
Elementy członkowskie nie są dodawane do tabeli funkcji wirtualnych. Są one dostępne tylko za pośrednictwem .IDispatch |
InterfaceIsDual |
IDispatch |
Tabela funkcji wirtualnych IDispatch ma najpierw elementy członkowskie , a następnie elementy członkowskie tego interfejsu w kolejności deklaracji. |
InterfaceIsIInspectable |
IInspectable |
Tabela funkcji wirtualnych IInspectable ma najpierw elementy członkowskie , a następnie elementy członkowskie tego interfejsu w kolejności deklaracji. Obsługiwane tylko w programie .NET Framework. |
Dziedziczenie interfejsu COM i platforma .NET
System międzyoperatorowy MODELU COM, który używa ComImportAttribute elementu nie wchodzi w interakcję z dziedziczeniem interfejsu, dlatego może spowodować nieoczekiwane zachowanie, chyba że zostaną podjęte pewne kroki korygujące.
Generator źródła COM używający atrybutu System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute
współdziała z dziedziczeniem interfejsu, więc zachowuje się bardziej zgodnie z oczekiwaniami.
Dziedziczenie interfejsu COM w języku C++
W języku C++deweloperzy mogą deklarować interfejsy COM pochodzące z innych interfejsów COM w następujący sposób:
struct IComInterface : public IUnknown
{
STDMETHOD(Method)() = 0;
STDMETHOD(Method2)() = 0;
};
struct IComInterface2 : public IComInterface
{
STDMETHOD(Method3)() = 0;
};
Ten styl deklaracji jest regularnie używany jako mechanizm dodawania metod do obiektów COM bez zmieniania istniejących interfejsów, co byłoby zmianą powodującą niezgodność. Ten mechanizm dziedziczenia powoduje następujące układy tabeli funkcji wirtualnych:
IComInterface gniazdo tabeli funkcji wirtualnej |
Nazwa metody |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface::Method |
100 | IComInterface::Method2 |
IComInterface2 gniazdo tabeli funkcji wirtualnej |
Nazwa metody |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface::Method |
4 | IComInterface::Method2 |
5 | IComInterface2::Method3 |
W związku z tym łatwo jest wywołać metodę zdefiniowaną na IComInterface
podstawie IComInterface2*
metody . W szczególności wywoływanie metody w interfejsie podstawowym nie wymaga wywołania w celu QueryInterface
uzyskania wskaźnika do interfejsu podstawowego. Ponadto język C++ umożliwia niejawną konwersję z IComInterface2*
na IComInterface*
, która jest dobrze zdefiniowana i pozwala uniknąć ponownego QueryInterface
wywoływania. W związku z tym w języku C lub C++nigdy nie trzeba wywoływać wywołania QueryInterface
, aby uzyskać dostęp do typu podstawowego, jeśli nie chcesz, co może pozwolić na pewne ulepszenia wydajności.
Uwaga
Interfejsy WinRT nie są zgodne z tym modelem dziedziczenia. Są one definiowane tak, aby były zgodne z tym samym modelem [ComImport]
co oparty na modelu międzyoperacjowym MODELU COM na platformie .NET.
Dziedziczenie interfejsu za pomocą polecenia ComImportAttribute
Na platformie .NET kod języka C#, który wygląda jak dziedziczenie interfejsu, nie jest w rzeczywistości dziedziczeniem interfejsu. Spójrzmy na poniższy kod:
interface I
{
void Method1();
}
interface J : I
{
void Method2();
}
Ten kod nie mówi "J
implementuje I
". Kod rzeczywiście mówi: "każdy typ, który implementuje J
, musi również implementować I
." Ta różnica prowadzi do podstawowej decyzji projektowej, która sprawia, że dziedziczenie interfejsu w ComImportAttributemiędzyoperacyjności międzyoperacyjności. Interfejsy są zawsze traktowane samodzielnie; lista interfejsów podstawowych interfejsu interfejsu nie ma wpływu na żadne obliczenia w celu określenia tabeli funkcji wirtualnych dla danego interfejsu platformy .NET.
W rezultacie naturalny odpowiednik poprzedniego przykładu interfejsu COM języka C++ prowadzi do innego układu tabeli funkcji wirtualnych.
Kod C#:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
void Method();
void Method2();
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
void Method3();
}
Układy tabeli funkcji wirtualnych:
IComInterface gniazdo tabeli funkcji wirtualnej |
Nazwa metody |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface::Method |
100 | IComInterface::Method2 |
IComInterface2 gniazdo tabeli funkcji wirtualnej |
Nazwa metody |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface2::Method3 |
Ponieważ te tabele funkcji wirtualnych różnią się od przykładu języka C++, może to prowadzić do poważnych problemów w czasie wykonywania. Poprawna definicja tych interfejsów na platformie .NET ComImportAttribute ma następującą definicję:
[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 poziomie metadanych nie implementujeIComInterface
, ale określa tylko, IComInterface2
że implementatory IComInterface2
programu muszą również implementować IComInterface
element . W związku z tym każda metoda z podstawowych typów interfejsów musi być ponownie zadeklarowana.
Dziedziczenie interfejsu przy użyciu GeneratedComInterfaceAttribute
platformy (.NET 8 lub nowszej)
Generator źródła COM wyzwalany przez dziedziczenie interfejsu GeneratedComInterfaceAttribute
języka C# jako dziedziczenie interfejsu COM, więc tabele funkcji wirtualnych są określone zgodnie z oczekiwaniami. W poprzednim przykładzie poprawna definicja tych interfejsów na platformie .NET System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute
jest następująca:
[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
void Method();
void Method2();
}
[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
void Method3();
}
Metody interfejsów podstawowych nie muszą być ponownie deklarowane i nie powinny być ponownie deklarowane. W poniższej tabeli opisano wynikowe tabele funkcji wirtualnych:
IComInterface gniazdo tabeli funkcji wirtualnej |
Nazwa metody |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface::Method |
100 | IComInterface::Method2 |
IComInterface2 gniazdo tabeli funkcji wirtualnej |
Nazwa metody |
---|---|
0 | IUnknown::QueryInterface |
1 | IUnknown::AddRef |
2 | IUnknown::Release |
3 | IComInterface::Method |
4 | IComInterface::Method2 |
5 | IComInterface2::Method3 |
Jak widać, te tabele są zgodne z przykładem języka C++, więc te interfejsy będą działać poprawnie.