Sdílet prostřednictvím


Kurz: Použití ComWrappers rozhraní API

V tomto kurzu se dozvíte, jak správně podtřídu ComWrappers typu poskytnout optimalizované a kompatibilní řešení modelu COM, které je vhodné pro AOT. Než začnete s tímto kurzem, měli byste být obeznámeni s com, jeho architekturou a stávajícími řešeními komunikace modelu COM.

V tomto kurzu implementujete následující definice rozhraní. Tato rozhraní a jejich implementace ukazují:

  • Zařazování a odřazování typů přes hranice COM/.NET
  • Dva odlišné přístupy k využívání nativních objektů MODELU COM v .NET
  • Doporučený vzor pro povolení vlastního zprostředkovatele komunikace s objekty COM v .NET 5 a novějších verzích

Veškerý zdrojový kód použitý v tomto kurzu je k dispozici v úložišti dotnet/samples.

Poznámka:

V sadě .NET 8 SDK a novějších verzích je k dispozici generátor zdrojů, který vám automaticky vygeneruje implementaci ComWrappers rozhraní API. Další informace najdete v tématu ComWrappers generování zdroje.

Definice jazyka C#

interface IDemoGetType
{
    string? GetString();
}

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

Definice Win32 C++

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

Přehled návrhu ComWrappers

Rozhraní ComWrappers API bylo navržené tak, aby poskytovalo minimální interakci potřebnou k zajištění spolupráce modelu COM s modulem runtime .NET 5+ . To znamená, že mnoho z nicech, které existují s integrovaným systémem komunikace com, nejsou přítomny a musí být sestaveny ze základních stavebních bloků. Mezi dvě hlavní zodpovědnosti rozhraní API patří:

Tyto efektivity se provádějí vyžadováním vytvoření a získání obálky, aby bylo možné projít ComWrappers rozhraním API.

ComWrappers Vzhledem k tomu, že rozhraní API má tak málo zodpovědností, znamená to, že většina práce spolupráce by měla být zpracována spotřebitelem – to je pravda. Dodatečná práce je však z velké části mechanická a lze ji provést pomocí řešení zdrojové generace. Například řetěz nástrojů C#/WinRT je řešení zdrojové generace, které je postavené na ComWrappers poskytování podpory vzájemné spolupráce WinRT.

ComWrappers Implementace podtřídy

ComWrappers Poskytnutí podtřídy znamená, že modulu runtime .NET poskytnete dostatek informací pro vytváření a záznam obálky pro spravované objekty, které se promítají do objektů COM a COM, které se projektují do .NET. Než se podíváme na osnovu podtřídy, měli bychom definovat některé termíny.

Obálka spravovaného objektu – Spravované objekty .NET vyžadují obálky, které umožňují použití z non-.NET prostředí. Tyto obálky se historicky označují jako obálky COM s možností volání (CCW).

Nativní obálka objektu – objekty MODELU COM implementované v jazyce non-.NET vyžadují obálky, které umožňují použití z .NET. Tyto obálky se historicky nazývají obálky s možností volání modulu runtime (RCW).

Krok 1 – Definování metod pro implementaci a pochopení jejich záměru

Chcete-li rozšířit ComWrappers typ, musíte implementovat následující tři metody. Každá z těchto metod představuje účast uživatele při vytváření nebo odstraňování typu obálky. CreateObject() Metody ComputeVtables() vytvářejí obálku spravovaného objektu a nativní obálku objektů v uvedeném pořadí. Metoda ReleaseObjects() je používána modulem runtime k vytvoření požadavku na zadanou kolekci obálky, které se "uvolní" z základního nativního objektu. Ve většině případů může tělo ReleaseObjects() metody jednoduše vyvolat NotImplementedException, protože je volána pouze v pokročilém scénáři zahrnujícím architekturu Reference Tracker.

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

Pokud chcete metodu ComputeVtables() implementovat, rozhodněte se, které spravované typy chcete podporovat. V tomto kurzu budeme podporovat dvě dříve definovaná rozhraní (IDemoGetType a IDemoStoreType) a spravovaný typ, který implementuje dvě rozhraní (DemoImpl).

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

Pro tuto metodu CreateObject() budete také muset určit, co chcete podporovat. V tomto případě ale víme, že rozhraní MODELU COM, která nás zajímají, nikoli třídy COM. Rozhraní spotřebovaná na straně modelu COM jsou stejná jako rozhraní, IDemoGetType která promítáme ze strany .NET (tj. a IDemoStoreType).

V tomto kurzu nebudeme implementovat ReleaseObjects() .

Krok 2 – implementace ComputeVtables()

Začněme obálkou spravovaného objektu – tyto obálky jsou jednodušší. Pro každé rozhraní vytvoříte tabulku virtuálních metod nebo tabulku virtuálních metod, abyste je mohli promítat do prostředí MODELU COM. Pro účely tohoto kurzu definujete tabulku vtable jako posloupnost ukazatelů, kde každý ukazatel představuje implementaci funkce v rozhraní – pořadí je zde velmi důležité. V modelu COM každé rozhraní dědí z IUnknown. Typ IUnknown má tři metody definované v následujícím pořadí: QueryInterface(), AddRef()a Release(). Jakmile metody IUnknown přijdou konkrétní metody rozhraní. Například zvažte IDemoGetType a IDemoStoreType. Vtables pro typy by koncepčně vypadaly takto:

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

Díváme se na DemoImpl, už máme implementaci a StoreString()GetString() , ale co funkceIUnknown? Jak implementovat IUnknown instanci je nad rámec tohoto kurzu, ale lze ji provést ručně v ComWrappers. V tomto kurzu ale necháte modul runtime zpracovat tuto část. Implementaci IUnknown můžete získat pomocí ComWrappers.GetIUnknownImpl() metody.

Může to vypadat, že jste implementovali všechny metody, ale bohužel jsou v tabulce MODELU COM dostupné jenom IUnknown funkce. Vzhledem k tomu, že objekt COM není součástí modulu runtime, budete muset vytvořit nativní ukazatele funkce na vaši DemoImpl implementaci. To lze provést pomocí ukazatelů funkce jazyka UnmanagedCallersOnlyAttributeC# a . Funkci, která se má vložit do virtuální tabulky, můžete vytvořit tak, že vytvoříte static funkci, která napodobuje podpis funkce MODELU COM. Následuje příklad podpisu IDemoGetType.GetString() MODELU COM – vzpomeňte si z modelu COM ABI, že prvním argumentem je samotná instance.

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

Implementace obálky IDemoGetType.GetString() by se měla skládat z logiky zařazování a následného odeslání do spravovaného objektu zabaleného. Veškerý stav pro odeslání je obsažen v zadaném _this argumentu. Argument _this bude ve skutečnosti typu ComInterfaceDispatch*. Tento typ představuje strukturu nízké úrovně s jedním polem, Vtablekteré bude popsáno později. Další podrobnosti o tomto typu a jeho rozložení jsou podrobnosti implementace modulu runtime a neměly by být závislé na. Pokud chcete načíst spravovanou ComInterfaceDispatch* instanci z instance, použijte následující kód:

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

Teď, když máte metodu jazyka C#, kterou lze vložit do virtuální tabulky, můžete vytvořit vtable. Všimněte si použití RuntimeHelpers.AllocateTypeAssociatedMemory() pro přidělování paměti způsobem, který funguje s nenačítitelnými sestaveními.

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;

Přidělení virtuálních tabulek je první částí implementace ComputeVtables(). Měli byste také vytvořit komplexní definice modelu COM pro typy, které plánujete podporovat – myslet DemoImpl a jaké části by měly být použitelné z modelu COM. Pomocí vytvořených virtuálních tabulek teď můžete vytvořit řadu ComInterfaceEntry instancí, které představují kompletní zobrazení spravovaného objektu v modelu COM.

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;

Přidělení virtuálních tabulek a položek pro obálku spravovaného objektu může a mělo by být provedeno předem, protože data lze použít pro všechny instance typu. Práce zde může být provedena v konstruktoru static nebo inicializátoru modulu, ale měla by být provedena předem, takže ComputeVtables() metoda je co nejjednodušší a nejrychlejší.

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

Po implementaci ComputeVtables() metody ComWrappers bude podtřída schopna vytvořit obálky spravovaného objektu DemoImplpro instance . Mějte na paměti, že vrácená obálka spravovaného objektu z volání je GetOrCreateComInterfaceForObject() typu IUnknown*. Pokud nativní rozhraní API, které se předává obálkě, vyžaduje jiné rozhraní, Marshal.QueryInterface() musí být provedeno rozhraní.

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

Krok 3 – implementace CreateObject()

Vytváření nativní obálky objektu má více možností implementace a mnohem více nuancí než vytvoření obálky spravovaného objektu. První otázka, kterou je potřeba vyřešit, je způsob, ComWrappers jakým bude podtřída podporována v podpůrných typech modelu COM. Pokud chcete podporovat všechny typy modelu COM, které je možné, budete muset napsat značné množství kódu nebo použít některé chytré použití Reflection.Emit. V tomto kurzu budete podporovat pouze instance modelu COM, které implementují obě IDemoGetType instance i IDemoStoreType. Vzhledem k tomu, že víte, že existuje konečná sada a omezila, že všechny zadané instance modelu COM musí implementovat obě rozhraní, můžete zadat jeden staticky definovaný obálka; Dynamické případy jsou však v modelu COM běžné, abychom prozkoumali obě možnosti.

Obálka statického nativního objektu

Nejprve se podíváme na statickou implementaci. Obálka statického nativního objektu zahrnuje definování spravovaného typu, který implementuje rozhraní .NET a může přesměrovávat volání spravovaného typu do instance modelu COM. Následuje hrubý obrys statické obálky.

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

Chcete-li vytvořit instanci této třídy a poskytnout ji jako obálku, musíte definovat některé zásady. Pokud se tento typ používá jako obálka, zdá se, že protože implementuje obě rozhraní, základní instance modelu COM by měla implementovat obě rozhraní také. Vzhledem k tomu, že tuto zásadu přijímáte, budete ji muset potvrdit prostřednictvím volání Marshal.QueryInterface() instance modelu COM.

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

Obálka dynamického nativního objektu

Dynamické obálky jsou flexibilnější, protože poskytují způsob, jak se na typy za běhu dotazovat místo staticky. Pokud chcete tuto podporu poskytnout, využijete IDynamicInterfaceCastable ji – další podrobnosti najdete tady. Všimněte si, že DemoNativeDynamicWrapper toto rozhraní implementuje pouze. Funkce, které rozhraní poskytuje, je šance určit, jaký typ je podporován za běhu. Zdroj pro tento kurz provádí statickou kontrolu během vytváření, ale to je jednoduše pro sdílení kódu, protože kontrola může být odložena do volání DemoNativeDynamicWrapper.IsInterfaceImplemented().

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

Pojďme se podívat na jedno z rozhraní, která DemoNativeDynamicWrapper budou dynamicky podporovat. Následující kód poskytuje implementaci IDemoStoreType pomocí funkce výchozích metod rozhraní.

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

V tomto příkladu je potřeba poznamenat dvě důležité věci:

  1. Atribut DynamicInterfaceCastableImplementationAttribute . Tento atribut je vyžadován u jakéhokoli typu, který je vrácen z IDynamicInterfaceCastable metody. Má přidanou výhodu jednoduššího ořezávání IL, což znamená, že scénáře AOT jsou spolehlivější.
  2. Přetypování na DemoNativeDynamicWrapper. To je součástí dynamické povahy IDynamicInterfaceCastable. Vrácený typ IDynamicInterfaceCastable.GetInterfaceImplementation() se používá k "deka" typu, který implementuje IDynamicInterfaceCastable. Gist zde je this ukazatel není to, co předstírá, že je, protože povolujeme případ od DemoNativeDynamicWrapper do IDemoStoreTypeNativeWrapper.

Přesměrování volání do instance modelu COM

Bez ohledu na to, který nativní object Wrapper se používá, potřebujete možnost vyvolat funkce v instanci modelu COM. Implementace IDemoStoreTypeNativeWrapper.StoreString() může sloužit jako příklad použití unmanaged ukazatelů funkcí jazyka C#.

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

Pojďme se podívat na dereferencování instance MODELU COM pro přístup k jeho vtable implementace. Com ABI definuje, že první ukazatel objektu je na vtable typu a z tohoto místa je možné získat přístup k požadovanému slotu. Předpokládejme, že adresa objektu COM je 0x10000. První hodnota velikosti ukazatele by měla být adresa virtuální tabulky – v tomto příkladu 0x20000. Jakmile budete ve virtuální tabulce, vyhledáte čtvrtý slot (index 3 v indexování založeném na nule) pro přístup k implementaci StoreString() .

COM instance
0x10000  0x20000

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

Když budete mít ukazatel funkce, umožníte odeslat do této členské funkce na tomto objektu předáním instance objektu jako prvního parametru. Tento vzor by měl vypadat dobře na základě definic funkcí implementace Managed Object Wrapper.

Po implementaci CreateObject()ComWrappers metody bude podtřída schopna vytvořit nativní obálky objektů pro instance modelu COM, které implementují obě IDemoGetType a IDemoStoreType.

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

Krok 4 – Zpracování podrobností o životnosti nativního obálky objektů

Implementace ComputeVtables() zahrnovaly CreateObject() některé podrobnosti o životnosti obálky, ale existují další aspekty. I když to může být krátký krok, může také výrazně zvýšit složitost návrhu ComWrappers .

Na rozdíl od obálky spravovaného objektu, který je řízen voláním jeho AddRef() a Release() metod, doba života Nativní object Wrapper je nedeterministicky zpracována GC. Otázka je, kdy nativní obálka objektu volání Release()IntPtr , které představuje instanci COM? Existují dva obecné kontejnery:

  1. Finalizační metoda nativního objektového obálky je zodpovědná za volání metody instance Release() COM. Je to jediný čas, kdy je bezpečné volat tuto metodu. V tomto okamžiku je správně určen GC, že v modulu runtime .NET neexistují žádné další odkazy na nativní obálku objektu. Zde může být složitost, pokud správně podporujete COM Apartments; Další informace najdete v části Další důležité informace .

  2. Nativní obálka objektu implementuje IDisposable a volá Release() v Dispose().

Poznámka:

Vzor IDisposable by měl být podporován pouze v CreateObject() případě, CreateObjectFlags.UniqueInstance že během volání byl příznak předán. Pokud tento požadavek není dodržen, je možné po vyřazení znovu použít nativní obálky objektů.

Použití podtřídy ComWrappers

Teď máte podtřídu ComWrappers , kterou můžete testovat. Abyste se vyhnuli vytváření nativní knihovny, která vrací instanci MODELU COM, která implementuje IDemoGetType a IDemoStoreType, použijete Obálku spravovaného objektu a budete s ní zacházet jako s instancí modelu COM – to musí být možné, aby bylo možné předat com přesto.

Nejprve vytvoříme obálku spravovaného objektu. Vytvořte instanci a zobrazte DemoImpl její aktuální stav řetězce.

var demo = new DemoImpl();

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

Teď můžete vytvořit instanci a obálku spravovaného DemoComWrappers objektu, kterou pak můžete předat do prostředí COM.

var cw = new DemoComWrappers();

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

Místo předání obálky spravovaného objektu do prostředí COM předstírejte, že jste právě obdrželi tuto instanci modelu COM, takže místo toho vytvoříte nativní obálku objektu.

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

U obálky nativního objektu byste měli být schopni ho přetypovat na jedno z požadovaných rozhraní a použít ho jako normální spravovaný objekt. Můžete prozkoumat DemoImpl instanci a sledovat dopad operací na obálku nativního objektu, která zabalí obálku spravovaného objektu, která zase zabalí spravovanou instanci.

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

Vzhledem k tomu, že vaše ComWrapper podtřída byla navržena tak, aby podporovala CreateObjectFlags.UniqueInstance, můžete okamžitě vyčistit nativní obálku objektů namísto čekání na výskyt GC.

(rcw as IDisposable)?.Dispose();

Aktivace modelu COM s využitím ComWrappers

Vytváření objektů MODELU COM se obvykle provádí prostřednictvím aktivace modelu COM – složitý scénář mimo rozsah tohoto dokumentu. Abychom mohli poskytnout koncepční vzor, který se má sledovat, představíme CoCreateInstance() rozhraní API, použijeme k aktivaci modelu COM a ukážeme, jak se dá použít s ComWrappers.

Předpokládejme, že máte v aplikaci následující kód jazyka C#. Následující příklad používá CoCreateInstance() k aktivaci třídy COM a integrovaného systému zprostředkovatele komunikace modelu COM zařazování instance modelu COM do příslušného rozhraní. Všimněte si, že použití typeof(I).GUID je omezené na assert a jedná se o případ použití reflexe, který může ovlivnit, pokud je kód přívětivý pro AOT.

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

Při převodu výše uvedeného na použití ComWrappers je potřeba odebrat MarshalAs(UnmanagedType.Interface) z CoCreateInstance() volání nespravovaného kódu a provést řazení ručně.

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

Je také možné abstrahovat funkce ve stylu továrny, jako ActivateClass<I> je zahrnutí aktivační logiky do konstruktoru třídy pro nativní objekt wrapper. Konstruktor může použít ComWrappers.GetOrRegisterObjectForComInstance() rozhraní API k přidružení nově vytvořeného spravovaného objektu k aktivované instanci modelu COM.

Další důležité informace

Nativní kompilace AOT – kompilace AOT (Head-of-Time) poskytuje lepší náklady na spuštění, protože kompilace JIT se vyhne. Na některých platformách se také často vyžaduje potřeba kompilace JIT. Podpora AOT byla cílem ComWrappers rozhraní API, ale jakákoli implementace obálky musí být opatrní, aby neúmyslně nezavedl případy, kdy AOT rozdělí, například použití reflexe. Vlastnost Type.GUID je příkladem toho, kde se reflexe používá, ale nejevným způsobem. Vlastnost Type.GUID používá reflexi ke kontrole atributů typu a pak potenciálně název typu a obsahující sestavení, aby se vygenerovala jeho hodnota.

Generování zdroje – většina kódu, který je potřeba pro zprostředkovatele komunikace modelu COM, a ComWrappers implementace může být pravděpodobně automaticky vygenerována některými nástroji. Zdroj pro oba typy obálky lze vygenerovat s ohledem na správné definice modelu COM – například Knihovna typů (TLB), IDL nebo primární sestavení vzájemné spolupráce (PIA).

Globální registrace – vzhledem k tomu, že ComWrappers rozhraní API bylo navrženo jako nová fáze zprostředkovatele komunikace modelu COM, bylo potřeba mít nějaký způsob, jak částečně integrovat se stávajícím systémem. V rozhraní API jsou globálně ovlivněné statické metody ComWrappers , které umožňují registraci globální instance pro různé podpory. Tyto metody jsou navržené pro ComWrappers instance, které očekávají komplexní podporu spolupráce modelu COM ve všech případech – je to podobné integrovanému systému komunikace modelu COM.

Podpora nástroje Reference Tracker – Tato podpora se používá především pro scénáře WinRT a představuje pokročilý scénář. U většiny ComWrapper implementací by buď CreateComInterfaceFlags.TrackerSupportCreateObjectFlags.TrackerObject příznak měl vyvolat NotSupportedException. Pokud chcete tuto podporu povolit, třeba na platformě Windows nebo jiné platformě než Windows, důrazně doporučujeme odkazovat na řetěz nástrojů C#/WinRT.

Kromě životnosti, systému typů a funkčních funkcí, které jsou popsány dříve, vyžaduje implementace modelu COM kompatibilní s ComWrappers dalšími aspekty. Pro každou implementaci, která se bude používat na platformě Windows, je potřeba vzít v úvahu následující skutečnosti:

  • Apartmány – organizační struktura COM pro vlákna se nazývá "Apartmány" a má striktní pravidla, která je třeba dodržovat pro stabilní provoz. Tento kurz neimplementuje nativní obálky objektů pracující s apartmány, ale jakákoli implementace připravená pro produkční prostředí by měla být v apartmánech. K tomu doporučujeme použít RoGetAgileReference rozhraní API zavedené ve Windows 8. U verzí starších než Windows 8 zvažte tabulku globálního rozhraní.

  • Zabezpečení – COM poskytuje bohatý model zabezpečení pro aktivaci třídy axied oprávnění.