COM birlikte çalışma için .NET türlerini niteleme

.NET türlerini COM'da kullanıma sunma

Bir derlemedeki türleri COM uygulamalarına kullanıma sunma amacınız varsa, tasarım zamanında COM birlikte çalışma gereksinimlerini göz önünde bulundurun. Yönetilen türler (sınıf, arabirim, yapı ve numaralandırma) aşağıdaki yönergelere uyduğunda COM türleriyle sorunsuz bir şekilde tümleşir:

  • Sınıflar arabirimleri açıkça uygulamalıdır.

    COM birlikte çalışma, sınıfın tüm üyelerini ve temel sınıfının üyelerini içeren bir arabirimi otomatik olarak oluşturmak için bir mekanizma sağlasa da, açık arabirimler sağlamak çok daha iyidir. Otomatik olarak oluşturulan arabirim, sınıf arabirimi olarak adlandırılır. Yönergeler için bkz . Sınıf arabirimine giriş.

    Arabirim Tanım Dili (IDL) veya eşdeğerini kullanmak yerine, arabirim tanımlarını kodunuzla birleştirmek için Visual Basic, C# ve C++ kullanabilirsiniz. Söz dizimi ayrıntıları için dil belgelerinize bakın.

  • Yönetilen türler genel olmalıdır.

    Yalnızca bir derlemedeki ortak türler kaydedilir ve tür kitaplığına dışarı aktarılır. Sonuç olarak, COM'da yalnızca genel türler görünür.

    Yönetilen türler, COM'a sunulmamış olabilecek diğer yönetilen kodlarda özellikleri kullanıma sunar. Örneğin, parametreli oluşturucular, statik yöntemler ve sabit alanlar COM istemcilerine sunulmaz. Ayrıca, çalışma zamanı verileri bir türün içinde ve dışında sıraladıkça, veriler kopyalanabilir veya dönüştürülebilir.

  • Yöntemler, özellikler, alanlar ve olaylar genel olmalıdır.

    Ortak türlerin üyeleri de COM'a görünür olacaksa genel olmalıdır. uygulamasını uygulayarak ComVisibleAttributebir derlemenin, genel türün veya ortak türün genel üyelerinin görünürlüğünü kısıtlayabilirsiniz. Varsayılan olarak, tüm genel türler ve üyeler görünür.

  • Türlerin COM'dan etkinleştirilmesi için genel parametresiz bir oluşturucuya sahip olması gerekir.

    Yönetilen, ortak türler COM tarafından görülebilir. Ancak, ortak parametresiz oluşturucu (bağımsız değişkeni olmayan bir oluşturucu) olmadan COM istemcileri türü oluşturamaz. COM istemcileri, başka bir yolla etkinleştirilirse türü kullanmaya devam edebilir.

  • Türler soyut olamaz.

    Ne COM istemcileri ne de .NET istemcileri soyut türler oluşturamazlar.

COM'a aktarıldığında, yönetilen bir türün devralma hiyerarşisi düzleştirilmiştir. Sürüm oluşturma, yönetilen ve yönetilmeyen ortamlar arasında da farklılık gösterir. COM'a sunulan türler, diğer yönetilen türlerle aynı sürüm oluşturma özelliklerine sahip değildir.

.NET'ten COM türlerini kullanma

.NET'ten COM türlerini kullanmayı planlıyorsanız ve Tlbimp.exe (Tür Kitaplığı İçeri Aktarıcısı) gibi araçları kullanmak istemiyorsanız, şu yönergeleri izlemeniz gerekir:

  • Arabirimlerin uygulanmış olması gerekir ComImportAttribute .
  • Arabirimler, COM arabirimi için GuidAttribute Arabirim Kimliği ile uygulanmış olmalıdır.
  • Arabirimler, bu arabirimin InterfaceTypeAttribute (IUnknown, IDispatchveya IInspectable) temel arabirim türünü belirtmek için uygulanmış olmalıdır.
    • Varsayılan seçenek, temel türüne IDispatch sahip olmak ve bildirilen yöntemleri arabirim için beklenen sanal işlev tablosuna eklemektir.
    • Yalnızca .NET Framework, temel türü belirtmeyi IInspectabledestekler.

Bu yönergeler, yaygın senaryolar için en düşük gereksinimleri sağlar. Daha birçok özelleştirme seçeneği vardır ve Birlikte Çalışma Özniteliklerini Uygulama bölümünde açıklanmıştır.

.NET'te COM arabirimlerini tanımlama

.NET kodu, özniteliğine sahip ComImportAttribute bir arabirim aracılığıyla COM nesnesinde bir yöntemi çağırmaya çalıştığında, çağrılacak yerel kodu belirlemek için arabirimin .NET tanımını oluşturmak için bir sanal işlev tablosu (vtable veya vftable olarak da bilinir) oluşturması gerekir. Bu işlem karmaşıktır. Aşağıdaki örneklerde bazı basit durumlar gösterilmektedir.

Birkaç yöntemi olan bir COM arabirimini göz önünde bulundurun:

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

Bu arabirim için aşağıdaki tabloda sanal işlev tablosu düzeni açıklanmaktadır:

IComInterface sanal işlev tablosu yuvası Yöntem adı
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2

Her yöntem sanal işlev tablosuna bildirildiği sırayla eklenir. Belirli bir sıra C++ derleyicisi tarafından tanımlanır, ancak aşırı yükleme içermeyen basit durumlar için bildirim sırası tablodaki sırayı tanımlar.

Bu arabirime karşılık gelen bir .NET arabirimini aşağıdaki gibi bildirin:

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

InterfaceTypeAttribute temel arabirimini belirtir. Birkaç seçenek sunar:

ComInterfaceType Değer Temel arabirim türü Öznitelikli arabirimdeki üyeler için davranış
InterfaceIsIUnknown IUnknown Sanal işlev tablosunda önce üyeleri IUnknown, ardından bildirim sırasına göre bu arabirimin üyeleri bulunur.
InterfaceIsIDispatch IDispatch Üyeler sanal işlev tablosuna eklenmez. Bunlara yalnızca üzerinden IDispatcherişilebilir.
InterfaceIsDual IDispatch Sanal işlev tablosunda önce üyeleri IDispatch, ardından bildirim sırasına göre bu arabirimin üyeleri bulunur.
InterfaceIsIInspectable IInspectable Sanal işlev tablosunda önce üyeleri IInspectable, ardından bildirim sırasına göre bu arabirimin üyeleri bulunur. Yalnızca .NET Framework'te desteklenir.

COM arabirimi devralma ve .NET

kullanan ComImportAttribute COM birlikte çalışma sistemi arabirim devralma ile etkileşim kurmaz, bu nedenle bazı hafifletici adımlar atılmadığı sürece beklenmeyen davranışlara neden olabilir.

özniteliğini System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute kullanan COM kaynak oluşturucu, arabirim devralma ile etkileşime girer, bu nedenle beklendiği gibi daha fazla davranır.

C++ dilinde COM arabirimi devralma

C++ dilinde geliştiriciler, diğer COM arabirimlerinden türetilen COM arabirimlerini aşağıdaki gibi bildirebilir:

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

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

Bu bildirim stili, mevcut arabirimleri değiştirmeden COM nesnelerine yöntem eklemek için düzenli olarak bir mekanizma olarak kullanılır ve bu da hataya neden olur. Bu devralma mekanizması aşağıdaki sanal işlev tablosu düzenlerine neden olur:

IComInterface sanal işlev tablosu yuvası Yöntem adı
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
IComInterface2 sanal işlev tablosu yuvası Yöntem adı
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
5 IComInterface2::Method3

Sonuç olarak, üzerinde tanımlanan IComInterface bir yönteminden IComInterface2*çağırmak kolaydır. Özellikle, temel arabirimde bir yöntemi çağırmak, temel arabirime QueryInterface bir işaretçi almak için bir çağrı gerektirmez. Buna ek olarak, C++ ile IComInterface2* arasında örtük bir dönüştürmeye IComInterface*izin verir ve iyi tanımlanmıştır ve yeniden QueryInterface çağırmaktan kaçınmanıza olanak tanır. Sonuç olarak, C veya C++'da, istemiyorsanız temel türe ulaşmak için hiçbir zaman aramanız QueryInterface gerekmez; bu da bazı performans iyileştirmelerine izin verebilir.

Not

WinRT arabirimleri bu devralma modelini izlemez. .NET'teki tabanlı COM birlikte çalışma modeliyle [ComImport]aynı modeli izlemek için tanımlanır.

ile arabirim devralma ComImportAttribute

.NET'te, arabirim devralma gibi görünen C# kodu aslında arabirim devralma değildir. Aşağıdaki kodu inceleyin:

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

Bu kod "J uygular I" demiyor. Kod aslında "uygulayan J her tür de uygulamalıdır I" diyor. Bu fark, tabanlı birlikte çalışmada ComImportAttributearabirim devralmayı çapraz olmayan hale getiren temel tasarım kararına yol açar. Arabirimler her zaman kendi başına kabul edilir; bir arabirimin temel arabirim listesi, belirli bir .NET arabirimi için sanal işlev tablosunu belirlemeye yönelik hesaplamaları etkilemez.

Sonuç olarak, önceki C++ COM arabirimi örneğinin doğal eşdeğeri farklı bir sanal işlev tablosu düzenine yol açar.

C# kodu:

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

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

Sanal işlev tablosu düzenleri:

IComInterface sanal işlev tablosu yuvası Yöntem adı
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
IComInterface2 sanal işlev tablosu yuvası Yöntem adı
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface2::Method3

Bu sanal işlev tabloları C++ örneğinden farklı olduğundan, çalışma zamanında ciddi sorunlara yol açar. ile ComImportAttribute .NET'te bu arabirimlerin doğru tanımı aşağıdaki gibidir:

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

Meta veri düzeyinde uygulamaz IComInterface2IComInterface , ancak yalnızca uygulayıcılarının IComInterface2 da uygulaması IComInterfacegerektiğini belirtir. Bu nedenle, temel arabirim türlerinden her yöntemin yeniden yüklenmesi gerekir.

(.NET 8 ve üzeri) ile GeneratedComInterfaceAttribute arabirim devralma

tarafından GeneratedComInterfaceAttribute tetiklenen COM kaynak oluşturucu, C# arabirimi devralmayı COM arabirimi devralma olarak uygular, böylece sanal işlev tabloları beklendiği gibi düzenlenir. Önceki örneği ele alırsanız, ile System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute .NET'te bu arabirimlerin doğru tanımı aşağıdaki gibidir:

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

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

Temel arabirimlerin yöntemlerinin yeniden yüklenmesi gerekmez ve yeniden işlenmemelidir. Aşağıdaki tabloda, sonuçta elde edilen sanal işlev tabloları açıklanmaktadır:

IComInterface sanal işlev tablosu yuvası Yöntem adı
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
IComInterface2 sanal işlev tablosu yuvası Yöntem adı
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
5 IComInterface2::Method3

Gördüğünüz gibi, bu tablolar C++ örneğiyle eşleşeceğinden, bu arabirimler düzgün çalışır.

Ayrıca bkz.