Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Tutorial: Utilizar a API
Neste tutorial, você aprenderá como subclassificar corretamente o ComWrappers tipo para fornecer uma solução de interoperabilidade COM otimizada e amigável para AOT. Antes de iniciar este tutorial, você deve estar familiarizado com COM, sua arquitetura e soluções de interoperabilidade COM existentes.
Neste tutorial, você implementará as seguintes definições de interface. Essas interfaces e suas implementações demonstrarão:
- Processamento de marshalling e unmarshalling de tipos através da barreira COM/.NET.
- Duas abordagens distintas para consumir objetos COM nativos no .NET.
- Um padrão recomendado para habilitar a interoperabilidade COM personalizada no .NET 5 e posterior.
Todo o código-fonte usado neste tutorial está disponível no repositório dotnet/samples.
Observação
No SDK do .NET 8 e versões posteriores, um gerador de código-fonte é fornecido para gerar automaticamente uma implementação de ComWrappers API para você. Para obter mais informações, consulte ComWrappers Geração de fontes.
Definições de C#
interface IDemoGetType
{
string? GetString();
}
interface IDemoStoreType
{
void StoreString(int len, string? str);
}
Definições do 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;
};
Visão geral do ComWrappers design
A ComWrappers API foi projetada para fornecer a interação mínima necessária para alcançar a interoperabilidade COM com o ambiente de execução do .NET 5+. Isso significa que muitas das sutilezas que existem com o sistema de interoperabilidade COM integrado não estão presentes e devem ser construídas a partir de blocos de construção básicos. As duas principais responsabilidades da API são:
- Identificação eficiente de objetos (por exemplo, mapeamento entre uma
IUnknown*instância e um objeto gerenciado). - Interação do Coletor de Lixo (GC).
Essas eficiências são alcançadas ao exigir que a criação de wrapper e aquisição passem pela API ComWrappers.
Como a ComWrappers API tem tão poucas responsabilidades, é lógico que a maior parte do trabalho de interoperabilidade deve ser tratada pelo consumidor – isso é verdade. No entanto, o trabalho adicional é em grande parte mecânico e pode ser realizado por uma solução de geração de fonte. Por exemplo, a cadeia de ferramentas C#/WinRT é uma solução de geração de código-fonte construída em cima de ComWrappers para fornecer suporte à interoperabilidade do WinRT.
Implementar uma subclasse ComWrappers
Fornecer uma ComWrappers subclasse significa fornecer informações suficientes ao runtime do .NET para criar e registar wrappers para objetos geridos que estão a ser projetados para COM e objetos COM que estão a ser projetados para .NET. Antes de olharmos para um esboço da subclasse, devemos definir alguns termos.
Managed Object Wrapper – Os objetos .NET gerenciados exigem wrappers para habilitar o uso de um ambiente non-.NET. Esses wrappers são historicamente chamados de COM Callable Wrappers (CCW).
Native Object Wrapper – objetos COM que são implementados em uma linguagem non-.NET exigem wrappers para habilitar o uso do .NET. Esses wrappers são historicamente chamados de Runtime Callable Wrappers (RCW).
Passo 1 – Definir métodos para implementar e compreender a sua intenção
Para estender o ComWrappers tipo, você deve implementar os três métodos a seguir. Cada um desses métodos representa a participação do usuário na criação ou exclusão de um tipo de wrapper. Os ComputeVtables() métodos e CreateObject() criam um Managed Object Wrapper e um Native Object Wrapper, respectivamente. O método ReleaseObjects() é usado pelo runtime para solicitar que a coleção fornecida de wrappers seja "libertada" do objeto nativo subjacente. Na maioria das vezes, o corpo do método ReleaseObjects() pode simplesmente disparar NotImplementedException, pois é apenas chamado em um cenário avançado envolvendo a estrutura 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();
}
Para implementar o ComputeVtables() método, decida quais tipos gerenciados você gostaria de suportar. Para este tutorial, suportaremos as duas interfaces definidas anteriormente (IDemoGetType e IDemoStoreType) e um tipo gerenciado que implementa as duas interfaces (DemoImpl).
class DemoImpl : IDemoGetType, IDemoStoreType
{
string? _string;
public string? GetString() => _string;
public void StoreString(int _, string? str) => _string = str;
}
Para o CreateObject() método, você também precisará determinar o que deseja suportar. Neste caso, porém, só conhecemos as interfaces COM em que estamos interessados, não as classes COM. As interfaces que estão sendo consumidas do lado COM são as mesmas que estamos projetando do lado .NET (ou seja, IDemoGetType e IDemoStoreType).
Não implementaremos ReleaseObjects() neste tutorial.
Etapa 2 – Implementar ComputeVtables()
Vamos começar com o Managed Object Wrapper – esses wrappers são mais fáceis. Você criará uma Virtual Method Table, ou vtable, para cada interface, a fim de projetá-los no ambiente COM. Para este tutorial, você definirá um vtable como uma sequência de ponteiros, onde cada ponteiro representa uma implementação de uma função em uma interface – a ordem é muito importante aqui. Em COM, cada interface herda do IUnknown. O IUnknown tipo tem três métodos definidos na seguinte ordem: QueryInterface(), AddRef()e Release(). Depois dos métodos de IUnknown, vêm os métodos de interface específicos. Por exemplo, considere IDemoGetType e IDemoStoreType. Conceitualmente, as tabelas virtuais para os tipos seriam da seguinte forma:
IDemoGetType | IDemoStoreType
==================================
QueryInterface | QueryInterface
AddRef | AddRef
Release | Release
GetString | StoreString
Olhando para DemoImpl, já temos uma implementação para GetString() e StoreString(), mas e quanto às funções IUnknown? Como implementar uma IUnknown instância está além do escopo deste tutorial, mas pode ser feito manualmente no ComWrappers. No entanto, neste tutorial, você permitirá que o tempo de execução manipule essa parte. Você pode obter a IUnknown implementação usando o ComWrappers.GetIUnknownImpl() método.
Pode parecer que você implementou todos os métodos, mas infelizmente, apenas as IUnknown funções são consumíveis em um vtable COM. Como o COM está fora do tempo de execução, você precisará criar ponteiros de função nativos para sua DemoImpl implementação. Isso pode ser feito usando ponteiros de função C# e o UnmanagedCallersOnlyAttribute. Você pode criar uma função para inserir no vtable criando uma static função que imita a assinatura da função COM. Segue-se um exemplo da assinatura COM para IDemoGetType.GetString() – lembre-se da COM ABI que o primeiro argumento é a própria instância.
[UnmanagedCallersOnly]
public static int GetString(IntPtr _this, IntPtr* str);
A implementação do wrapper deve IDemoGetType.GetString() consistir em lógica de empacotamento e, em seguida, um despacho para o objeto gerenciado que está sendo encapsulado. Todo o estado para despacho está contido no argumento fornecido _this. O _this argumento será, na verdade, do tipo ComInterfaceDispatch*. Este tipo representa uma estrutura de baixo nível com um único campo, Vtableque será discutido mais adiante. Mais detalhes deste tipo e do seu layout são questões de implementação do tempo de execução e não se deve depender deles. Para recuperar a instância gerenciada de uma ComInterfaceDispatch* instância, use o seguinte código:
IDemoGetType inst = ComInterfaceDispatch.GetInstance<IDemoGetType>((ComInterfaceDispatch*)_this);
Agora que você tem um método C# que pode ser inserido em um vtable, você pode construir o vtable. Observe o uso de RuntimeHelpers.AllocateTypeAssociatedMemory() para alocar memória de uma forma que funcione com assemblies descarregáveis.
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;
A alocação de vtables é a primeira parte da implementação ComputeVtables(). Você também deve construir definições COM abrangentes para os tipos que está planejando suportar – pense DemoImpl e quais partes dele devem ser utilizáveis no COM. Usando os vtables construídos, pode agora criar uma série de instâncias ComInterfaceEntry que representam a visão completa do objeto gerido em 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;
A alocação de vtables e entradas para o Managed Object Wrapper pode e deve ser feita com antecedência, uma vez que os dados podem ser usados para todas as instâncias do tipo. O trabalho aqui pode ser executado em um static construtor ou um inicializador de módulo, mas deve ser feito com antecedência para que o método seja o ComputeVtables() mais simples e rápido possível.
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;
}
Depois de implementar o ComputeVtables() método, a ComWrappers subclasse poderá produzir Managed Object Wrappers para instâncias de DemoImpl. Lembre-se de que, na chamada para GetOrCreateComInterfaceForObject(), o Wrapper de Objecto Gerenciado retornado é do tipo IUnknown*. Se a API nativa que está sendo passada para o wrapper requer uma interface diferente, uma Marshal.QueryInterface() interface para essa interface deve ser executada.
var cw = new DemoComWrappers();
var demo = new DemoImpl();
IntPtr ccw = cw.GetOrCreateComInterfaceForObject(demo, CreateComInterfaceFlags.None);
Etapa 3 – Implementar CreateObject()
A construção de um Wrapper de Objeto Nativo tem mais opções de implementação e muito mais nuances do que a construção de um Wrapper de Objeto Gerenciado. A primeira questão a ser abordada é o quão permissiva a ComWrappers subclasse será no suporte a tipos COM. Para suportar todos os tipos COM, o que é possível, você precisará escrever uma quantidade substancial de código ou empregar alguns usos inteligentes do Reflection.Emit. Para este tutorial, irá dar suporte apenas a instâncias COM que implementem IDemoGetType e IDemoStoreType. Como você sabe que há um conjunto finito e restringiu que qualquer instância COM fornecida deve implementar ambas as interfaces, você pode fornecer um único wrapper definido estaticamente; no entanto, casos dinâmicos são comuns o suficiente em COM para que exploremos ambas as opções.
Wrapper de objeto nativo estático
Vamos examinar a implementação estática primeiro. O Native Object Wrapper estático envolve a definição de um tipo gerenciado que implementa as interfaces .NET e pode encaminhar as chamadas no tipo gerenciado para a instância COM. Segue-se um esboço aproximado do invólucro estático.
// 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();
}
Para construir uma instância dessa classe e fornecê-la como um wrapper, você deve definir alguma política. Se esse tipo for usado como um wrapper, parece que, uma vez que implementa ambas as interfaces, a instância COM subjacente deve implementar ambas as interfaces também. Dado que você está adotando essa política, você precisará confirmar isso por meio de chamadas para Marshal.QueryInterface() a instância 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
};
Wrapper de objeto nativo dinâmico
Os wrappers dinâmicos são mais flexíveis porque permitem que os tipos sejam consultados em tempo de execução em vez de estaticamente. Para prestar este apoio, usará IDynamicInterfaceCastable. Observe que DemoNativeDynamicWrapper apenas implementa esta interface. A funcionalidade que a interface oferece é uma oportunidade para determinar que tipo é suportado em tempo de execução. A fonte deste tutorial faz uma verificação estática durante a criação, mas isso é simplesmente para compartilhamento de código, já que a verificação pode ser adiada até que uma chamada seja feita para 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();
}
Vamos olhar para uma das interfaces que DemoNativeDynamicWrapper suportarão dinamicamente. O código a seguir fornece a implementação de IDemoStoreType usando o recurso de métodos de interface padrão.
[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);
}
}
Há duas coisas importantes a observar neste exemplo:
- O
DynamicInterfaceCastableImplementationAttributeatributo. Esse atributo é necessário em qualquer tipo retornado de umIDynamicInterfaceCastablemétodo. Ele tem o benefício adicional de facilitar o corte de IL, o que significa que os cenários de AOT são mais confiáveis. - O elenco para
DemoNativeDynamicWrapper. Isto faz parte da natureza dinâmica doIDynamicInterfaceCastable. O tipo que é retornado deIDynamicInterfaceCastable.GetInterfaceImplementation()é usado para "cobrir" o tipo que implementaIDynamicInterfaceCastable. A essência aqui é que othisponteiro não é o que finge ser, porque estamos permitindo um caso deDemoNativeDynamicWrapperatéIDemoStoreTypeNativeWrapper.
Encaminhe as chamadas para a instância COM
Independentemente de qual Native Object Wrapper é usado, você precisa da capacidade de invocar funções em uma instância COM. A implementação de IDemoStoreTypeNativeWrapper.StoreString() pode servir como um exemplo de utilização de ponteiros de função C# unmanaged.
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);
}
}
Vamos examinar a desreferenciação da instância COM para acessar sua implementação vtable. O ABI do COM define que o primeiro ponteiro de um objeto aponta para a vtable (tabela de funções virtuais) do tipo e, a partir daí, o slot desejado pode ser acessado. Vamos supor que o endereço do objeto COM seja 0x10000. O primeiro valor do tamanho do ponteiro deve ser o endereço do vtable – neste exemplo 0x20000. Quando estiver no vtable, você procurará o quarto slot (índice 3 na indexação baseada em zero) para acessar a StoreString() implementação.
COM instance
0x10000 0x20000
VTable for IDemoStoreType
0x20000 <Address of QueryInterface>
0x20008 <Address of AddRef>
0x20010 <Address of Release>
0x20018 <Address of StoreString>
Ter o ponteiro da função permite que você despache para essa função de membro nesse objeto passando a instância do objeto como o primeiro parâmetro. Esse padrão deve parecer familiar com base nas definições de função da implementação do Managed Object Wrapper.
Depois que o CreateObject() método for implementado, a ComWrappers subclasse poderá produzir Native Object Wrappers para instâncias COM que implementam tanto IDemoGetType quanto IDemoStoreType.
IntPtr iunk = ...; // Get a COM instance from native code.
object rcw = cw.GetOrCreateObjectForComInstance(iunk, CreateObjectFlags.UniqueInstance);
Etapa 4 – Gerir os detalhes do ciclo de vida do Native Object Wrapper
As implementações ComputeVtables() e CreateObject() cobriram alguns detalhes do ciclo de vida do envoltório, mas há outras considerações. Embora isso possa ser um passo curto, também pode aumentar significativamente a complexidade do ComWrappers projeto.
Ao contrário do Managed Object Wrapper, que é controlado por chamadas para seus AddRef() métodos e Release() , o tempo de vida de um Native Object Wrapper é manipulado não deterministicamente pelo GC. A questão aqui é: quando é que o Native Object Wrapper chama o Release() no IntPtr que representa a instância COM? Existem duas categorias gerais:
O Finalizador do Native Object Wrapper é responsável por chamar o método da instância COM
Release(). Este é o único momento em que é seguro chamar este método. Neste ponto, foi determinado corretamente pelo GC que não há outras referências ao Native Object Wrapper no tempo de execução do .NET. Pode haver complexidade aqui se você estiver apoiando adequadamente COM Apartments; para obter mais informações, consulte a seção Considerações adicionais .O Native Object Wrapper implementa
IDisposablee chamaRelease()emDispose().
Observação
O padrão IDisposable só deve ser suportado se, no decorrer da CreateObject() chamada, o sinalizador CreateObjectFlags.UniqueInstance tenha sido passado. Se esse requisito não for seguido, é possível que os invólucros de objetos nativos descartados sejam reutilizados após serem descartados.
Usando a ComWrappers subclasse
Agora você tem uma ComWrappers subclasse que pode ser testada. Para evitar a criação de uma biblioteca nativa que retorna uma instância COM que implementa IDemoGetType e IDemoStoreType, você usará o Managed Object Wrapper e o tratará como uma instância COM – isso deve ser possível para passá-lo COM de qualquer maneira.
Vamos criar um Wrapper de Objeto Gerenciado primeiro. Instancie uma DemoImpl instância e exiba seu estado atual da cadeia de caracteres.
var demo = new DemoImpl();
string? value = demo.GetString();
Console.WriteLine($"Initial string: {value ?? "<null>"}");
Agora você pode criar uma instância de DemoComWrappers e um Managed Object Wrapper que você pode passar para um ambiente COM.
var cw = new DemoComWrappers();
IntPtr ccw = cw.GetOrCreateComInterfaceForObject(demo, CreateComInterfaceFlags.None);
Em vez de passar o Wrapper de Objeto Gerenciado para um ambiente COM, finja que acabou de receber essa instância COM, para que você crie um Wrapper de Objeto Nativo para ele.
var rcw = cw.GetOrCreateObjectForComInstance(ccw, CreateObjectFlags.UniqueInstance);
Com o Native Object Wrapper, você deve ser capaz de convertê-lo em uma das interfaces desejadas e usá-lo como um objeto gerenciado normal. Pode examinar a instância DemoImpl e observar o impacto das operações no Native Object Wrapper que encapsula um Managed Object Wrapper que, por sua vez, envolve a instância gerida.
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}");
Como a sua ComWrapper subclasse foi projetada para oferecer suporte a CreateObjectFlags.UniqueInstance, você pode limpar o envelope de objetos nativos imediatamente, em vez de esperar que ocorra um GC.
(rcw as IDisposable)?.Dispose();
Ativação COM com ComWrappers
A criação de objetos COM normalmente é realizada via Ativação COM – um cenário complexo fora do escopo deste documento. Para fornecer um padrão conceitual, apresentamos a CoCreateInstance() API, usada para a ativação COM, e ilustramos como ela pode ser usada com ComWrappers.
Suponha que você tenha o seguinte código C# em seu aplicativo. O exemplo abaixo usa CoCreateInstance() para ativar uma classe COM e o sistema de interoperabilidade COM interno para marshalar a instância COM para a interface apropriada. Observe que o uso de typeof(I).GUID é limitado a uma asserção e é um caso de uso da reflexão, que pode afetar se o código é amigável para 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);
A conversão do acima para uso de ComWrappers envolve remover o MarshalAs(UnmanagedType.Interface) do P/Invoke de CoCreateInstance() e realizar o marshalling manualmente.
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);
Também é possível abstrair funções de estilo de fábrica, como ActivateClass<I> ao incluir a lógica de ativação no construtor de classe para um Object Wrapper Nativo. O construtor pode usar a ComWrappers.GetOrRegisterObjectForComInstance() API para associar o objeto gerenciado recém-construído com a instância COM ativada.
Considerações adicionais
AOT nativo – A compilação Ahead-of-time (AOT) oferece melhorias no custo de inicialização ao evitar a compilação JIT. Remover a necessidade de compilação JIT também é frequentemente necessário em algumas plataformas. O suporte à AOT era um objetivo da ComWrappers API, mas qualquer implementação de wrapper deve ter cuidado para não introduzir inadvertidamente situações em que a AOT pode falhar, como o uso de reflexão. A Type.GUID propriedade é um exemplo de onde a reflexão é usada, mas de uma forma não óbvia. A propriedade Type.GUID usa reflexão para inspecionar os atributos do tipo e, em seguida, potencialmente o nome do tipo e o assembly que o contém para gerar o seu valor.
Geração de código-fonte – A maioria do código necessário para interoperabilidade COM e uma ComWrappers implementação provavelmente pode ser gerada automaticamente por algumas ferramentas. A origem para ambos os tipos de wrappers pode ser gerada de acordo com as definições COM adequadas – por exemplo, Biblioteca de Tipos (TLB), IDL ou um Primary Interop Assembly (PIA).
Registro global – Como a ComWrappers API foi projetada como uma nova fase de interoperabilidade COM, ela precisava ter alguma maneira de se integrar parcialmente ao sistema existente. Existem métodos estáticos que têm impacto global na API ComWrappers que permitem o registo de uma instância global para vários suportes. Esses métodos são projetados para ComWrappers instâncias que esperam fornecer suporte abrangente de interoperabilidade COM em todos os casos – semelhante ao sistema de interoperabilidade COM integrado.
Suporte ao Monitor de Referências – Este suporte é usado principalmente para cenários do WinRT e representa um cenário avançado. Para a maioria das ComWrapper implementações, um CreateComInterfaceFlags.TrackerSupport ou CreateObjectFlags.TrackerObject sinalizador deve lançar um NotSupportedException. Se você quiser habilitar esse suporte, talvez em uma plataforma Windows ou até mesmo não-Windows, é altamente recomendável fazer referência à cadeia de ferramentas C#/WinRT.
Além da longevidade, do sistema de tipos e dos recursos funcionais que são discutidos anteriormente, uma implementação compatível com COM requer considerações adicionais. Para qualquer implementação que será usada na plataforma Windows, há as seguintes considerações:
Apartamentos – A estrutura organizacional da COM para threading é chamada de "Apartamentos" e possui regras rígidas que devem ser seguidas para operações estáveis. Este tutorial não implementa Native Object Wrappers adaptados a apartamentos, mas qualquer implementação pronta para produção deve ser adaptada a apartamentos. Para fazer isso, recomendamos usar a API introduzida
RoGetAgileReferenceno Windows 8. Para versões anteriores ao Windows 8, considere a Tabela de Interface Global.Segurança – O COM fornece um modelo de segurança avançado para ativação de classe e permissão por proxy.