Aracılığıyla paylaş


Öğretici: API'yi ComWrappers kullanma

Bu öğreticide, iyileştirilmiş ve AOT dostu bir COM birlikte çalışma çözümü sağlamak için türü düzgün bir şekilde alt sınıfa ComWrappers almayı öğreneceksiniz. Bu öğreticiye başlamadan önce COM, mimarisi ve mevcut COM birlikte çalışma çözümleri hakkında bilgi sahibi olmanız gerekir.

Bu öğreticide aşağıdaki arabirim tanımlarını uygulayacaksınız. Bu arabirimler ve uygulamaları şunları gösterir:

  • COM/.NET sınırında türleri sıralama ve özetleme kaldırma.
  • .NET'te yerel COM nesnelerini kullanmaya yönelik iki farklı yaklaşım.
  • .NET 5 ve sonrasında özel COM birlikte çalışabilirlik özelliğini etkinleştirmek için önerilen desen.

Bu öğreticide kullanılan tüm kaynak kodu dotnet/samples deposunda kullanılabilir.

Not

.NET 8 SDK ve sonraki sürümlerinde, sizin için otomatik olarak bir API uygulaması oluşturmak için bir ComWrappers kaynak oluşturucu sağlanır. Daha fazla bilgi için bkz ComWrappers . kaynak oluşturma.

C# tanımları

interface IDemoGetType
{
    string? GetString();
}

interface IDemoStoreType
{
    void StoreString(int len, string? str);
}

Win32 C++ tanımları

MIDL_INTERFACE("92BAA992-DB5A-4ADD-977B-B22838EE91FD")
IDemoGetType : public IUnknown
{
    HRESULT STDMETHODCALLTYPE GetString(_Outptr_ wchar_t** str) = 0;
};

MIDL_INTERFACE("30619FEA-E995-41EA-8C8B-9A610D32ADCB")
IDemoStoreType : public IUnknown
{
    HRESULT STDMETHODCALLTYPE StoreString(int len, _In_z_ const wchar_t* str) = 0;
};

Tasarıma ComWrappers genel bakış

API, ComWrappers .NET 5+ çalışma zamanıyla COM birlikte çalışma zamanı gerçekleştirmek için gereken en düşük etkileşimi sağlamak üzere tasarlanmıştır. Bu, yerleşik COM birlikte çalışma sistemiyle var olan özelliklerin çoğunun mevcut olmadığı ve temel yapı bloklarından oluşturulması gerektiği anlamına gelir. API'nin iki birincil sorumluluğu şunlardır:

  • Verimli nesne tanımlama (örneğin, bir IUnknown* örnek ve yönetilen nesne arasında eşleme).
  • Çöp Toplayıcı (GC) etkileşimi.

Bu verimlilikler, SARMALAYıCı oluşturma ve alma işleminin ComWrappers API üzerinden geçmesi gerekerek gerçekleştirilir.

ComWrappers API'nin çok az sorumluluğu olduğundan, birlikte çalışma çalışmalarının çoğunun tüketici tarafından ele alınması gerektiği anlamına gelir; bu doğrudur. Bununla birlikte, ek iş büyük ölçüde mekaniktir ve bir kaynak oluşturma çözümü tarafından gerçekleştirilebilir. Örnek olarak, C#/WinRT araç zinciri , WinRT birlikte çalışma desteği sağlamak için üzerine ComWrappers kurulmuş bir kaynak oluşturma çözümüdür.

Alt sınıf uygulama ComWrappers

Alt sınıf sağlamak ComWrappers , COM'a yansıtılan yönetilen nesneler ve .NET'e yansıtılan COM nesneleri için sarmalayıcılar oluşturmak ve kaydetmek için .NET çalışma zamanına yeterli bilgi vermek anlamına gelir. Alt sınıfın ana hatlarını incelemeden önce bazı terimler tanımlamamız gerekir.

Yönetilen Nesne Sarmalayıcı – Yönetilen .NET nesneleri, non-.NET bir ortamdan kullanımı etkinleştirmek için sarmalayıcılar gerektirir. Bu sarmalayıcılar geçmişte COM Çağrılabilir Sarmalayıcılar (CCW) olarak adlandırılır.

Yerel Nesne Sarmalayıcı – non-.NET dilde uygulanan COM nesneleri, sarmalayıcıların .NET'ten kullanımı etkinleştirmesini gerektirir. Bu sarmalayıcılar geçmişte Çalışma Zamanı Çağrılabilir Sarmalayıcıları (RCW) olarak adlandırılır.

1. Adım – Amaçlarını uygulamak ve anlamak için yöntemler tanımlama

Türü genişletmek ComWrappers için aşağıdaki üç yöntemi uygulamanız gerekir. Bu yöntemlerin her biri, kullanıcının bir sarmalayıcı türü oluşturma veya silme işlemine katılımını temsil eder. ComputeVtables() ve CreateObject() yöntemleri sırasıyla bir Yönetilen Nesne Sarmalayıcı ve Yerel Nesne Sarmalayıcı oluşturur. ReleaseObjects() yöntemi, sağlanan sarmalayıcı koleksiyonunun temel alınan yerel nesneden "serbest bırakılması" için istekte bulunmak için çalışma zamanı tarafından kullanılır. Çoğu durumda, yalnızca Başvuru İzleyicisi çerçevesini ReleaseObjects()içeren gelişmiş bir senaryoda çağrıldığından yönteminin gövdesini oluşturabilirNotImplementedException.

// See referenced sample for implementation.
class DemoComWrappers : ComWrappers
{
    protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) =>
        throw new NotImplementedException();

    protected override object? CreateObject(IntPtr externalComObject, CreateObjectFlags flags) =>
        throw new NotImplementedException();

    protected override void ReleaseObjects(IEnumerable objects) =>
        throw new NotImplementedException();
}

Yöntemini uygulamak ComputeVtables() için hangi yönetilen türleri desteklemek istediğinize karar verin. Bu öğreticide, daha önce tanımlanmış iki arabirimi ( ve ) ve IDemoStoreTypeiki arabirimi (IDemoGetTypeDemoImpl) uygulayan bir yönetilen türü destekleyeceğiz.

class DemoImpl : IDemoGetType, IDemoStoreType
{
    string? _string;
    public string? GetString() => _string;
    public void StoreString(int _, string? str) => _string = str;
}

yöntemi için CreateObject() neyi desteklemek istediğinizi de belirlemeniz gerekir. Ancak bu durumda, COM sınıflarını değil yalnızca ilgilendiğimiz COM arabirimlerini biliyoruz. COM tarafından tüketilen arabirimler, .NET tarafından yansıtılan arabirimlerle aynıdır (yani IDemoGetType ve IDemoStoreType).

Bu öğreticide uygulamayacağız ReleaseObjects() .

2. Adım – Uygulama ComputeVtables()

Yönetilen Nesne Sarmalayıcı ile başlayalım; bu sarmalayıcılar daha kolaydır. Bunları COM ortamına yansıtmak için her arabirim için bir Sanal Yöntem Tablosu veya vtable oluşturacaksınız. Bu öğreticide, bir vtable'ı işaretçi dizisi olarak tanımlayacaksınız; burada her işaretçi bir arabirimdeki işlevin uygulamasını temsil eder; burada sıra çok önemlidir. COM'da her arabirim öğesinden IUnknowndevralır. Türün IUnknown şu sırada tanımlanan üç yöntemi vardır: QueryInterface(), AddRef()ve Release(). IUnknown Yöntemler geldikten sonra belirli arabirim yöntemleri gelir. Örneğin, ve IDemoStoreTypedeğerini göz önünde bulundurunIDemoGetType. Kavramsal olarak, türlerin vtable'ları aşağıdaki gibi görünür:

IDemoGetType    | IDemoStoreType
==================================
QueryInterface  | QueryInterface
AddRef          | AddRef
Release         | Release
GetString       | StoreString

öğesine baktığımızda DemoImplve StoreString()için GetString() zaten bir uygulamamız var ama işlevler ne olacakIUnknown? Bir IUnknown örneğin nasıl uygulanacağı bu öğreticinin kapsamı dışındadır, ancak içinde ComWrappersel ile yapılabilir. Ancak bu öğreticide çalışma zamanının bu bölümü işlemesine izin vereceksiniz. yöntemini kullanarak ComWrappers.GetIUnknownImpl() uygulamayı alabilirsinizIUnknown.

Tüm yöntemleri uygulamış gibi görünebilirsiniz, ancak ne yazık ki com sanal makinesinde yalnızca IUnknown işlevler kullanılabilir. COM çalışma zamanının dışında olduğundan, uygulamanız için DemoImpl yerel işlev işaretçileri oluşturmanız gerekir. Bu işlem C# işlev işaretçileri ve UnmanagedCallersOnlyAttributekullanılarak yapılabilir. COM işlev imzasını taklit eden bir işlev oluşturarak vtable'a eklenecek bir static işlev oluşturabilirsiniz. Aşağıda için IDemoGetType.GetString() COM imzası örneği verilmiştir. COM ABI'den ilk bağımsız değişkenin örneğin kendisi olduğunu hatırlayın.

[UnmanagedCallersOnly]
public static int GetString(IntPtr _this, IntPtr* str);

sarmalayıcı uygulaması IDemoGetType.GetString() , sıralama mantığından ve ardından sarmalanan yönetilen nesneye bir gönderimden oluşmalıdır. Dağıtım için tüm durum, sağlanan _this bağımsız değişken içinde yer alır. Bağımsız _this değişken aslında türünde ComInterfaceDispatch*olacaktır. Bu tür, Vtabledaha sonra tartışılacak tek bir alanı olan düşük düzeyli bir yapıyı temsil eder. Bu türün ve düzeninin diğer ayrıntıları çalışma zamanının uygulama ayrıntılarıdır ve buna bağlı olmamalıdır. Yönetilen örneği bir ComInterfaceDispatch* örnekten almak için aşağıdaki kodu kullanın:

IDemoGetType inst = ComInterfaceDispatch.GetInstance<IDemoGetType>((ComInterfaceDispatch*)_this);

Artık bir vtable'a eklenebilen bir C# yönteminiz olduğuna göre, vtable'ı oluşturabilirsiniz. Belleği kaldırılabilir derlemelerle çalışacak şekilde ayırmaya yönelik kullanımına RuntimeHelpers.AllocateTypeAssociatedMemory() dikkat edin.

GetIUnknownImpl(
    out IntPtr fpQueryInterface,
    out IntPtr fpAddRef,
    out IntPtr fpRelease);

// Local variables with increment act as a guard against incorrect construction of
// the native vtable. It also enables a quick validation of final size.
int tableCount = 4;
int idx = 0;
var vtable = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(
    typeof(DemoComWrappers),
    IntPtr.Size * tableCount);
vtable[idx++] = fpQueryInterface;
vtable[idx++] = fpAddRef;
vtable[idx++] = fpRelease;
vtable[idx++] = (IntPtr)(delegate* unmanaged<IntPtr, IntPtr*, int>)&ABI.IDemoGetTypeManagedWrapper.GetString;
Debug.Assert(tableCount == idx);
s_IDemoGetTypeVTable = (IntPtr)vtable;

Vtable'ların ayrılması, uygulamasının ComputeVtables()ilk bölümüdür. Ayrıca, desteklemeyi planladığınız türler için kapsamlı COM tanımları oluşturmanız gerekir; düşünün ve com'dan hangi bölümlerinin kullanılabilir olması gerektiğini düşünün DemoImpl . Artık, yapılandırılan vtable'ları kullanarak, COM'da yönetilen nesnenin ComInterfaceEntry tam görünümünü temsil eden bir dizi örnek oluşturabilirsiniz.

s_DemoImplDefinitionLen = 2;
int idx = 0;
var entries = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(
    typeof(DemoComWrappers),
    sizeof(ComInterfaceEntry) * s_DemoImplDefinitionLen);
entries[idx].IID = IDemoGetType.IID_IDemoGetType;
entries[idx++].Vtable = s_IDemoGetTypeVTable;
entries[idx].IID = IDemoStoreType.IID_IDemoStoreType;
entries[idx++].Vtable = s_IDemoStoreVTable;
Debug.Assert(s_DemoImplDefinitionLen == idx);
s_DemoImplDefinition = entries;

Yönetilen Nesne Sarmalayıcı için vtable'ların ve girişlerin ayrılması, verilerin türün tüm örnekleri için kullanılabilmesi için önceden yapılabilir ve yapılması gerekir. Buradaki iş bir static oluşturucuda veya modül başlatıcıda gerçekleştirilebilir, ancak yöntemin mümkün olduğunca basit ve hızlı olması için ComputeVtables() önceden yapılması gerekir.

protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags,
out int count)
{
    if (obj is DemoImpl)
    {
        count = s_DemoImplDefinitionLen;
        return s_DemoImplDefinition;
    }

    // Unknown type
    count = 0;
    return null;
}

yöntemini uyguladıktan ComputeVtables() sonra, ComWrappers alt sınıf örnekleri için Yönetilen Nesne Sarmalayıcıları DemoImplüretebilecektir. çağrısından GetOrCreateComInterfaceForObject() döndürülen Yönetilen Nesne Sarmalayıcı'nın türünde IUnknown*olduğunu unutmayın. Sarmalayıcıya geçirilen yerel API farklı bir arabirim gerektiriyorsa, bu arabirim için bir Marshal.QueryInterface() gerçekleştirilmelidir.

var cw = new DemoComWrappers();
var demo = new DemoImpl();
IntPtr ccw = cw.GetOrCreateComInterfaceForObject(demo, CreateComInterfaceFlags.None);

3. Adım – Uygulama CreateObject()

Yerel Nesne Sarmalayıcı oluşturma işlemi, Yönetilen Nesne Sarmalayıcı oluşturmaktan çok daha fazla uygulama seçeneğine ve çok daha fazla ayrıntıya sahiptir. Ele alınması gereken ilk soru, alt sınıfın ComWrappers destekleyici COM türlerinde ne kadar izinli olacağıdır. Mümkün olan tüm COM türlerini desteklemek için önemli miktarda kod yazmanız veya bazı akıllı kullanımlarını Reflection.Emitkullanmanız gerekir. Bu öğreticide, yalnızca hem IDemoStoreTypehem de IDemoGetType uygulayan COM örneklerini destekleyeceksiniz. Sonlu bir küme olduğunu bildiğiniz ve sağlanan tüm COM örneklerinin her iki arabirimi de uygulaması gerektiğini kısıtladığınız için, tek, statik olarak tanımlanmış bir sarmalayıcı sağlayabilirsiniz; ancak com'da dinamik durumlar her iki seçeneği de keşfedeceğimiz kadar yaygındır.

Statik Yerel Nesne Sarmalayıcı

Önce statik uygulamaya bakalım. Statik Yerel Nesne Sarmalayıcı, .NET arabirimlerini uygulayan bir yönetilen tür tanımlamayı içerir ve yönetilen türdeki çağrıları COM örneğine iletebilir. Statik sarmalayıcının kaba bir ana hattı aşağıdadır.

// See referenced sample for implementation.
class DemoNativeStaticWrapper
    : IDemoGetType
    , IDemoStoreType
{
    public string? GetString() =>
        throw new NotImplementedException();

    public void StoreString(int len, string? str) =>
        throw new NotImplementedException();
}

Bu sınıfın bir örneğini oluşturmak ve sarmalayıcı olarak sağlamak için bazı ilkeler tanımlamanız gerekir. Bu tür sarmalayıcı olarak kullanılıyorsa, her iki arabirimi de uyguladığından, temel alınan COM örneğinin her iki arabirimi de uygulaması gerektiği görünür. Bu ilkeyi benimsediğinize göre, COM örneğindeki çağrılar Marshal.QueryInterface() aracılığıyla bunu onaylamanız gerekir.

int hr = Marshal.QueryInterface(ptr, ref IDemoGetType.IID_IDemoGetType, out IntPtr IDemoGetTypeInst);
if (hr != 0)
{
    return null;
}

hr = Marshal.QueryInterface(ptr, ref IDemoStoreType.IID_IDemoStoreType, out IntPtr IDemoStoreTypeInst);
if (hr != 0)
{
    Marshal.Release(IDemoGetTypeInst);
    return null;
}

return new DemoNativeStaticWrapper()
{
    IDemoGetTypeInst = IDemoGetTypeInst,
    IDemoStoreTypeInst = IDemoStoreTypeInst
};

Dinamik Yerel Nesne Sarmalayıcı

Dinamik sarmalayıcılar, türlerin statik olarak değil çalışma zamanında sorgulanması için bir yol sağladığından daha esnektir. Bu desteği sağlamak için kullanacaksınız IDynamicInterfaceCastable ; daha fazla ayrıntıya buradan ulaşabilirsiniz. Yalnızca bu DemoNativeDynamicWrapper arabirimi uyguladığını gözlemleyin. Arabirimin sağladığı işlevsellik, çalışma zamanında hangi türün desteklendiğini belirleme şansıdır. Bu öğreticinin kaynağı oluşturma sırasında statik bir denetim yapar, ancak bu yalnızca kod paylaşımına yöneliktir çünkü denetim çağrısı yapılana DemoNativeDynamicWrapper.IsInterfaceImplemented()kadar ertelenebilir.

// See referenced sample for implementation.
internal class DemoNativeDynamicWrapper
    : IDynamicInterfaceCastable
{
    public RuntimeTypeHandle GetInterfaceImplementation(RuntimeTypeHandle interfaceType) =>
        throw new NotImplementedException();

    public bool IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) =>
        throw new NotImplementedException();
}

Şimdi dinamik olarak desteklenecek arabirimlerden DemoNativeDynamicWrapper birine bakalım. Aşağıdaki kod, varsayılan arabirim yöntemleri özelliğinin kullanılmasını sağlarIDemoStoreType.

[DynamicInterfaceCastableImplementation]
unsafe interface IDemoStoreTypeNativeWrapper : IDemoStoreType
{
    public static void StoreString(IntPtr inst, int len, string? str);

    void IDemoStoreType.StoreString(int len, string? str)
    {
        var inst = ((DemoNativeDynamicWrapper)this).IDemoStoreTypeInst;
        StoreString(inst, len, str);
    }
}

Bu örnekte dikkat edilmesi gereken iki önemli nokta vardır:

  1. DynamicInterfaceCastableImplementationAttribute özniteliği. Bu öznitelik, bir yöntemden döndürülen herhangi bir IDynamicInterfaceCastable türde gereklidir. IL kırpmayı kolaylaştırma avantajına sahiptir ve bu da AOT senaryolarının daha güvenilir olduğu anlamına gelir.
  2. için atama DemoNativeDynamicWrapper. Bu, öğesinin dinamik doğasının IDynamicInterfaceCastablebir parçasıdır. 'den IDynamicInterfaceCastable.GetInterfaceImplementation() döndürülen tür, uygulayan IDynamicInterfaceCastabletürü "paketle" kullanmak için kullanılır. Buradaki örnek, işaretçinin this olduğu gibi davranmadığıdır çünkü 'den DemoNativeDynamicWrapper 'e bir servis talebine izin vermemiz gerekir IDemoStoreTypeNativeWrapper.

Çağrıları COM örneğine iletme

Hangi Yerel Nesne Sarmalayıcının kullanıldığına bakılmaksızın, COM örneğinde işlevleri çağırabilmeniz gerekir. uygulaması IDemoStoreTypeNativeWrapper.StoreString() , C# işlev işaretçilerini kullanma unmanaged örneği olarak işlev görebilir.

public static void StoreString(IntPtr inst, int len, string? str)
{
    IntPtr strLocal = Marshal.StringToCoTaskMemUni(str);
    int hr = ((delegate* unmanaged<IntPtr, int, IntPtr, int>)(*(*(void***)inst + 3 /* IDemoStoreType.StoreString slot */)))(inst, len, strLocal);
    if (hr != 0)
    {
        Marshal.FreeCoTaskMem(strLocal);
        Marshal.ThrowExceptionForHR(hr);
    }
}

Şimdi com örneğinin başvurusunu kaldırarak vtable uygulamasına erişmesini inceleyelim. COM ABI, bir nesnenin ilk işaretçisinin türün vtable'ı olduğunu ve buradan istenen yuvaya erişilebileceğini tanımlar. COM nesnesinin adresinin olduğunu 0x10000varsayalım. İşaretçi boyutundaki ilk değer, bu örnekteki 0x20000vtable'ın adresi olmalıdır. Vtable'a geçtikten sonra uygulamaya erişmek StoreString() için dördüncü yuvayı (sıfır tabanlı dizin oluşturmada dizin 3) ararsınız.

COM instance
0x10000  0x20000

VTable for IDemoStoreType
0x20000  <Address of QueryInterface>
0x20008  <Address of AddRef>
0x20010  <Address of Release>
0x20018  <Address of StoreString>

İşlev işaretçisinin olması, nesne örneğini ilk parametre olarak geçirerek bu nesnedeki üye işlevine göndermenizi sağlar. Bu desen, Yönetilen Nesne Sarmalayıcı uygulamasının işlev tanımlarına göre tanıdık görünmelidir.

CreateObject() Yöntem uygulandıktan sonra, ComWrappers alt sınıf hem IDemoStoreTypehem de IDemoGetType uygulayan COM örnekleri için Yerel Nesne Sarmalayıcıları üretebilecektir.

IntPtr iunk = ...; // Get a COM instance from native code.
object rcw = cw.GetOrCreateObjectForComInstance(iunk, CreateObjectFlags.UniqueInstance);

4. Adım – Yerel Nesne Sarmalayıcı yaşam süresi ayrıntılarını işleme

ComputeVtables() ve CreateObject() uygulamaları bazı sarmalayıcı ömrü ayrıntılarını ele alır, ancak dikkat edilmesi gereken başka noktalar da vardır. Bu kısa bir adım olsa da, tasarımın karmaşıklığını ComWrappers önemli ölçüde artırabilir.

ve Release() yöntemlerine AddRef() yapılan çağrılar tarafından denetlenen Yönetilen Nesne Sarmalayıcı'nın aksine, Yerel Nesne Sarmalayıcının ömrü GC tarafından belirsiz bir şekilde işlenmez. Buradaki soru, COM örneğini temsil eden Yerel Nesne Sarmalayıcısı'nı IntPtr ne zaman çağırdığıdırRelease()? İki genel demet vardır:

  1. Yerel Nesne Sarmalayıcı'nın Sonlandırıcısı, COM örneğinin Release() yöntemini çağırmaktan sorumludur. Bu yöntemi çağırmanın güvenli olduğu tek zaman budur. Bu noktada, .NET çalışma zamanında Yerel Nesne Sarmalayıcı'ya başka başvuru olmadığı GC tarafından doğru şekilde belirlendi. COM Dairelerini düzgün bir şekilde destekliyorsanız burada karmaşıklık olabilir; daha fazla bilgi için Dikkat edilmesi gereken ek noktalar bölümüne bakın.

  2. Yerel Nesne Sarmalayıcı içinde Dispose()uygular IDisposable ve çağırırRelease().

Not

Desen IDisposable yalnızca çağrı sırasında bayrağı geçirildiyse CreateObject()CreateObjectFlags.UniqueInstance desteklenmelidir. Bu gereksinime uyulmazsa, atılan Yerel Nesne Sarmalayıcıları atıldıktan sonra yeniden kullanılabilir.

ComWrappers Alt sınıfı kullanma

Artık test edilebilen bir ComWrappers alt sınıfınız var. ve IDemoStoreTypeuygulayan IDemoGetType bir COM örneği döndüren bir yerel kitaplık oluşturmaktan kaçınmak için Yönetilen Nesne Sarmalayıcı'yı kullanır ve bunu bir COM örneği olarak ele alırsınız; yine de COM'yi geçirmek için bu mümkün olmalıdır.

Önce bir Yönetilen Nesne Sarmalayıcı oluşturalım. Örneğin DemoImpl örneğini oluşturun ve geçerli dize durumunu görüntüleyin.

var demo = new DemoImpl();

string? value = demo.GetString();
Console.WriteLine($"Initial string: {value ?? "<null>"}");

Şimdi, bir COM ortamına DemoComWrappers geçirebileceğiniz bir ve Yönetilen Nesne Sarmalayıcısı örneği oluşturabilirsiniz.

var cw = new DemoComWrappers();

IntPtr ccw = cw.GetOrCreateComInterfaceForObject(demo, CreateComInterfaceFlags.None);

Yönetilen Nesne Sarmalayıcı'yı bir COM ortamına geçirmek yerine, bu COM örneğini yeni almış gibi yapın, böylece bunun yerine yerel nesne sarmalayıcı oluşturursunuz.

var rcw = cw.GetOrCreateObjectForComInstance(ccw, CreateObjectFlags.UniqueInstance);

Yerel Nesne Sarmalayıcı ile bunu istenen arabirimlerden birine dönüştürebilmeniz ve normal yönetilen nesne olarak kullanabilmeniz gerekir. Örneği inceleyebilir DemoImpl ve yönetilen örneği sarmalayan yönetilen Nesne Sarmalayıcısını sarmalayan Yerel Nesne Sarmalayıcı üzerindeki işlemlerin etkisini gözlemleyebilirsiniz.

var getter = (IDemoGetType)rcw;
var store = (IDemoStoreType)rcw;

string msg = "hello world!";
store.StoreString(msg.Length, msg);
Console.WriteLine($"Setting string through wrapper: {msg}");

value = demo.GetString();
Console.WriteLine($"Get string through managed object: {value}");

msg = msg.ToUpper();
demo.StoreString(msg.Length, msg.ToUpper());
Console.WriteLine($"Setting string through managed object: {msg}");

value = getter.GetString();
Console.WriteLine($"Get string through wrapper: {value}");

Alt sınıfınız ComWrapper destekleyecek CreateObjectFlags.UniqueInstanceşekilde tasarlandığından, GC'nin gerçekleşmesini beklemek yerine Yerel Nesne Sarmalayıcı'yı hemen temizleyebilirsiniz.

(rcw as IDisposable)?.Dispose();

com etkinleştirme ile ComWrappers

COM nesnelerinin oluşturulması genellikle bu belgenin kapsamı dışında karmaşık bir senaryo olan COM Etkinleştirmesi aracılığıyla gerçekleştirilir. İzlenecek kavramsal bir desen sağlamak için, COM Etkinleştirmesi için kullanılan API'yi tanıtır CoCreateInstance() ve ile ComWrappersnasıl kullanılabileceğini gösteririz.

Uygulamanızda aşağıdaki C# kodunun olduğunu varsayalım. Aşağıdaki örnek, COM örneğini uygun arabirime göre sıralamak için bir COM sınıfını ve yerleşik COM birlikte çalışma sistemini etkinleştirmek için kullanır CoCreateInstance() . kullanımı typeof(I).GUID bir onay ile sınırlıdır ve kodun AOT kullanımına uygun olması durumunda etkilenebilen bir yansıma kullanımı örneği olduğunu unutmayın.

public static I ActivateClass<I>(Guid clsid, Guid iid)
{
    Debug.Assert(iid == typeof(I).GUID);
    int hr = CoCreateInstance(ref clsid, IntPtr.Zero, /*CLSCTX_INPROC_SERVER*/ 1, ref iid, out object obj);
    if (hr < 0)
    {
        Marshal.ThrowExceptionForHR(hr);
    }
    return (I)obj;
}

[DllImport("Ole32")]
private static extern int CoCreateInstance(
    ref Guid rclsid,
    IntPtr pUnkOuter,
    int dwClsContext,
    ref Guid riid,
    [MarshalAs(UnmanagedType.Interface)] out object ppObj);

Yukarıdakini kullanıma ComWrappers dönüştürmek için P/Invoke'dan CoCreateInstance() öğesinin kaldırılması MarshalAs(UnmanagedType.Interface) ve el ile sıralamanın gerçekleştirilmesi gerekir.

static ComWrappers s_ComWrappers = ...;

public static I ActivateClass<I>(Guid clsid, Guid iid)
{
    Debug.Assert(iid == typeof(I).GUID);
    int hr = CoCreateInstance(ref clsid, IntPtr.Zero, /*CLSCTX_INPROC_SERVER*/ 1, ref iid, out IntPtr obj);
    if (hr < 0)
    {
        Marshal.ThrowExceptionForHR(hr);
    }
    return (I)s_ComWrappers.GetOrCreateObjectForComInstance(obj, CreateObjectFlags.None);
}

[DllImport("Ole32")]
private static extern int CoCreateInstance(
    ref Guid rclsid,
    IntPtr pUnkOuter,
    int dwClsContext,
    ref Guid riid,
    out IntPtr ppObj);

Yerel Nesne Sarmalayıcı için sınıf oluşturucusunda etkinleştirme mantığını ekleyerek fabrika ActivateClass<I> stilindeki işlevleri soyutlamanız da mümkündür. Oluşturucu, yeni yapılan yönetilen nesneyi etkinleştirilmiş COM örneğiyle ilişkilendirmek için API'yi kullanabilir ComWrappers.GetOrRegisterObjectForComInstance() .

Dikkat edilecek diğer noktalar

Yerel AOT – JIT derlemesi önlendiğinden, önceden (AOT) derlemesi gelişmiş başlangıç maliyeti sağlar. Bazı platformlarda JIT derleme gereksinimini ortadan kaldırmak da genellikle gereklidir. AOT'nin desteklenmesi API'nin ComWrappers bir hedefiydi, ancak herhangi bir sarmalayıcı uygulaması, yansıma kullanma gibi AOT'nin kesildiği durumları yanlışlıkla tanıtmamaya dikkat etmelidir. Type.GUID özelliği, yansımanın kullanıldığı ancak belirgin olmayan bir şekilde kullanıldığı bir örnektir. özelliği, Type.GUID türün özniteliklerini incelemek için yansıma kullanır ve ardından değerini oluşturmak için türün adını ve derlemeyi içerme olasılığını kullanır.

Kaynak oluşturma : COM birlikte çalışma ve uygulama ComWrappers için gereken kodun çoğu büyük olasılıkla bazı araçlar tarafından otomatik olarak oluşturulabilir. Tür Kitaplığı (TLB), IDL veya Birincil Birlikte Çalışma Derlemesi (PIA) gibi uygun COM tanımları göz önünde bulundurularak her iki sarmalayıcı türü için de kaynak oluşturulabilir.

Genel kayıtComWrappers API, COM birlikte çalışmasının yeni bir aşaması olarak tasarlandığından, mevcut sistemle kısmen tümleştirmenin bir yolunun olması gerekiyordu. API'de çeşitli destek için genel bir örneğin kaydedilmesine ComWrappers izin veren genel olarak etkileyen statik yöntemler vardır. Bu yöntemler, yerleşik COM birlikte çalışma sistemine benzer şekilde her durumda kapsamlı COM birlikte çalışma desteği sağlamayı bekleyen örnekler için ComWrappers tasarlanmıştır.

Başvuru İzleyicisi desteği – Bu destek WinRT senaryoları için birincil olarak kullanılır ve gelişmiş bir senaryodur. Çoğu ComWrapper uygulama için CreateComInterfaceFlags.TrackerSupport veya CreateObjectFlags.TrackerObject bayrağı bir NotSupportedExceptionoluşturmalıdır. Bu desteği bir Windows platformunda veya hatta Windows dışı bir platformda etkinleştirmek istiyorsanız, C#/WinRT araç zincirine başvurmanız kesinlikle önerilir.

Daha önce ele alınan yaşam süresi, tür sistemi ve işlevsel özelliklerin yanı sıra, COM ile uyumlu bir uygulama ComWrappers için dikkat edilmesi gereken ek noktalar vardır. Windows platformunda kullanılacak herhangi bir uygulama için aşağıdaki noktalar vardır:

  • Daireler – COM'un iş parçacığı oluşturma organizasyon yapısı "Apartmanlar" olarak adlandırılır ve kararlı operasyonlar için uyulması gereken katı kurallara sahiptir. Bu öğreticide, daireye duyarlı Yerel Nesne Sarmalayıcıları uygulanmaz, ancak üretime hazır tüm uygulamalar daireye duyarlı olmalıdır. Bunu başarmak için Windows 8'de kullanıma sunulan API'yi kullanmanızı RoGetAgileReference öneririz. Windows 8 öncesi sürümler için Genel Arabirim Tablosu'nu göz önünde bulundurun.

  • Güvenlik – COM, sınıf etkinleştirme ve yakın izin için zengin bir güvenlik modeli sağlar.