Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Руководство: Использование
В этом руководстве вы узнаете, как правильно подклассировать ComWrappers тип для предоставления оптимизированного и удобного для AOT решения взаимодействия COM. Перед началом работы с этим руководством необходимо ознакомиться с COM, его архитектурой и существующими решениями взаимодействия COM.
В этом руководстве описано, как реализовать следующие определения интерфейса. Эти интерфейсы и их реализации демонстрируют:
- Маршаллирование и демаршалинг типов через границу COM/.NET.
- Два различных подхода к потреблению собственных COM-объектов в .NET.
- Рекомендуемый шаблон для включения пользовательских компонентов COM в .NET 5 и более поздних версий.
Весь исходный код, используемый в этом руководстве, доступен в репозитории dotnet/samples.
Замечание
В пакете SDK для .NET 8 и более поздних версий предоставляется генератор исходного кода, который автоматически создаёт реализацию API ComWrappers. Дополнительные сведения см. в разделеComWrappers "Создание источника".
Определения C#
interface IDemoGetType
{
string? GetString();
}
interface IDemoStoreType
{
void StoreString(int len, string? str);
}
Определения 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;
};
Общие сведения о дизайне ComWrappers
ComWrappers API был разработан для обеспечения минимального взаимодействия, необходимого для выполнения COM-взаимодействия с средой выполнения .NET 5+. Это означает, что многие из удобств, которые существуют во встроенной системе взаимодействия COM, отсутствуют и должны быть созданы из основных компонентов. Двумя основными обязанностями API являются:
- Эффективная идентификация объектов (например, сопоставление между экземпляром
IUnknown*и управляемым объектом). - Взаимодействие сборщика мусора (GC).
Эти эффективности достигаются путем создания и получения оболочек с использованием API ComWrappers.
ComWrappers Так как API имеет так мало обязанностей, это означает, что большая часть работы взаимодействия должна обрабатываться потребителем - это верно. Однако дополнительная работа является в значительной степени механической и может выполняться решением для генерации источников. Например, цепочка инструментов C#/WinRT — это решение для генерации исходного кода, созданное на базе ComWrappers, чтобы обеспечить поддержку взаимодействия с WinRT.
Реализуйте подкласс ComWrappers
Предоставление подкласса означает обеспечение достаточной ComWrappers информации среде выполнения .NET для создания и регистрации оберток для управляемых объектов, проецируемых в COM, и объектов COM, проецируемых в .NET. Прежде чем мы рассмотрим структуру подкласса, мы должны определить некоторые термины.
Оболочка управляемых объектов — управляемые объекты .NET требуют оболочек для использования в не .NET-среде. Эти оболочки исторически называются COM-вызываемыми оболочками (CCW).
Оболочка собственных объектов — COM-объекты, реализованные на языке non-.NET, требуют оболочки для включения использования из .NET. Эти оболочки исторически называются вызываемыми оболочками среды выполнения (RCW).
Шаг 1. Определение методов для реализации и понимания их намерения
Чтобы расширить ComWrappers тип, необходимо реализовать следующие три метода. Каждый из этих методов представляет участие пользователя в создании или удалении типа оболочки. Методы ComputeVtables() и CreateObject() создают оболочку управляемого объекта и оболочку собственного объекта соответственно. Метод ReleaseObjects() используется средой выполнения для запроса на «высвобождение» предоставленной коллекции оболочек из базового нативного объекта. В большинстве случаев тело метода ReleaseObjects() может просто бросать NotImplementedException, так как оно вызывается только в продвинутом сценарии с использованием фреймворка 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();
}
Чтобы реализовать ComputeVtables() метод, определите, какие управляемые типы вы хотите поддерживать. В этом руководстве мы будем поддерживать два ранее определенных интерфейса (IDemoGetType и IDemoStoreType) и управляемый тип, реализующий два интерфейса (DemoImpl).
class DemoImpl : IDemoGetType, IDemoStoreType
{
string? _string;
public string? GetString() => _string;
public void StoreString(int _, string? str) => _string = str;
}
CreateObject() Для метода также необходимо определить, что вы хотите поддерживать. В этом случае мы знаем только интересующие нас интерфейсы COM, а не классы COM. Интерфейсы, используемые со стороны COM, совпадают с теми, которые мы проецируем со стороны .NET (т. е., IDemoGetType и IDemoStoreType).
Мы не будем реализовывать ReleaseObjects() в этом руководстве.
Шаг 2. Реализация ComputeVtables()
Начнем с оболочки управляемого объекта. Эти оболочки проще. Вы создадите таблицу виртуальных методов или vtable для каждого интерфейса, чтобы проецировать их в com-среду. В этом руководстве вы определите vtable как последовательность указателей, где каждый указатель представляет реализацию функции в интерфейсе. Порядок очень важен здесь. В COM каждый интерфейс наследуется от IUnknown. Тип IUnknown имеет три метода, определенные в следующем порядке: QueryInterface(), AddRef()и Release(). После методов IUnknown следуют конкретные методы интерфейса. Например, рассмотрим IDemoGetType и IDemoStoreType. Концептуально виртуальные таблицы для типов будут выглядеть следующим образом:
IDemoGetType | IDemoStoreType
==================================
QueryInterface | QueryInterface
AddRef | AddRef
Release | Release
GetString | StoreString
Глядя на DemoImpl, у нас уже есть реализация GetString() и StoreString(), но что о функциях IUnknown ? Как реализовать инстанцию не входит в рамки IUnknown этого руководства, но это можно сделать вручную в ComWrappers. Однако в этом руководстве вы позволите среде выполнения обрабатывать эту часть. Реализацию IUnknown можно получить с помощью ComWrappers.GetIUnknownImpl() метода.
Возможно, вы реализовали все методы, но, к сожалению, только функции из IUnknown доступны для использования в vtable COM. Так как COM находится за пределами среды выполнения, необходимо создать нативные указатели функций на DemoImpl реализацию. Это можно сделать с помощью указателей функции C# и UnmanagedCallersOnlyAttribute. Вы можете создать функцию для вставки в vtable, создав static функцию, которая имитирует сигнатуру функции COM. Ниже приведен пример сигнатуры COM для IDemoGetType.GetString() — напоминаем, что в соответствии с COM ABI первый аргумент является самим экземпляром.
[UnmanagedCallersOnly]
public static int GetString(IntPtr _this, IntPtr* str);
Реализация оболочки IDemoGetType.GetString() должна состоять из логики маршаллинга, а затем отправки в управляемый объект, который упаковывается. Все состояние для отправки содержится в указанном _this аргументе. Аргумент _this фактически будет типом ComInterfaceDispatch*. Этот тип представляет собой низкоуровневую структуру с одним полем, Vtableкоторое будет обсуждаться позже. Дополнительные сведения об этом типе и его макете являются подробными сведениями о реализации среды выполнения и не должны зависеть от них. Чтобы получить управляемый экземпляр из экземпляра ComInterfaceDispatch* , используйте следующий код:
IDemoGetType inst = ComInterfaceDispatch.GetInstance<IDemoGetType>((ComInterfaceDispatch*)_this);
Теперь, когда у вас есть метод C#, который можно вставить в vtable, можно создать vtable. Обратите внимание на использование RuntimeHelpers.AllocateTypeAssociatedMemory() для выделения памяти таким образом, чтобы обеспечить совместимость с выгружаемыми сборками.
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;
Выделение vtables является первой частью реализации ComputeVtables(). Вы также должны создавать комплексные определения COM для типов, которые планируете поддерживать, рассматривая, какие части DemoImpl должны быть доступны из COM. Используя созданные виртуальные таблицы, теперь можно создать ряд ComInterfaceEntry экземпляров, представляющих полное представление управляемого объекта в 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;
Выделение vtables и записей для оболочки управляемого объекта может и должно быть выполнено заранее, так как данные можно использовать для всех экземпляров типа. Здесь можно выполнить работу в static конструкторе или инициализаторе модуля, но это необходимо сделать заранее, чтобы ComputeVtables() метод был максимально простым и быстрым.
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;
}
После того как вы реализуете метод ComputeVtables(), подкласс ComWrappers сможет создавать обёртки управляемых объектов для экземпляров DemoImpl. Будьте внимательны, что возвращённый объект-оболочка из вызова GetOrCreateComInterfaceForObject() является объектом типа IUnknown*. Если для собственного API, передаваемого в оболочку, требуется другой интерфейс, необходимо выполнить Marshal.QueryInterface() для этого интерфейса.
var cw = new DemoComWrappers();
var demo = new DemoImpl();
IntPtr ccw = cw.GetOrCreateComInterfaceForObject(demo, CreateComInterfaceFlags.None);
Шаг 3. Реализация CreateObject()
Создание оболочки собственного объекта имеет больше возможностей реализации и значительно больше нюансов, чем создание оболочки управляемого объекта. Первый вопрос, который следует решить, заключается в том, насколько разрешительно ComWrappers подкласс будет поддерживать типы COM. Для поддержки всех типов COM, что возможно, необходимо написать значительный объем кода или использовать некоторые умные способы использования Reflection.Emit. В этом руководстве вы будете поддерживать только экземпляры COM, реализующие оба IDemoGetType и IDemoStoreType. Поскольку вы знаете, что существует конечный набор, и ограничены в том, что любой предоставленный COM-экземпляр должен реализовывать оба интерфейса, можно предоставить одну статически определённую оболочку. Однако динамические случаи достаточно распространены в COM, поэтому мы рассмотрим оба варианта.
Статическая оболочка нативных объектов
Сначала рассмотрим статическую реализацию. Статическая оболочка нативного объекта предполагает определение управляемого типа, который реализует интерфейсы .NET и может перенаправлять вызовы этого типа на COM-экземпляр. Примерный план статической оболочки представлен.
// 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();
}
Чтобы создать экземпляр этого класса и предоставить его в качестве оболочки, необходимо определить определенную политику. Если этот тип используется в качестве оболочки, казалось бы, что так как он реализует оба интерфейса, базовый экземпляр COM также должен реализовать оба интерфейса. Учитывая, что вы принимаете эту политику, необходимо подтвердить это с помощью вызовов Marshal.QueryInterface() в 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
};
Оболочка динамических нативных объектов
Динамические оболочки являются более гибкими, так как они предоставляют способ запроса типов во время выполнения вместо статического. Чтобы обеспечить эту поддержку, вы будете использовать IDynamicInterfaceCastable. Обратите внимание, что DemoNativeDynamicWrapper реализует только этот интерфейс. Функциональные возможности, которые предоставляет интерфейс, — это возможность определить, какой тип поддерживается во время выполнения. Источник для этого руководства выполняет статическую проверку во время создания, но это делается исключительно для совместного использования кода, поскольку проверку можно отложить до вызова 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();
}
Рассмотрим один из интерфейсов, которые DemoNativeDynamicWrapper будет поддерживать динамически. Следующий код предоставляет реализацию IDemoStoreType с использованием функции методы интерфейса по умолчанию.
[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);
}
}
В этом примере важно отметить два важных элемента:
- Атрибут
DynamicInterfaceCastableImplementationAttribute. Этот атрибут требуется для любого типа, возвращаемого методомIDynamicInterfaceCastable. Это дает дополнительное преимущество упрощения обрезки IL, означая, что сценарии AOT становятся более надежными. - Приведение к
DemoNativeDynamicWrapper. Это часть динамической природыIDynamicInterfaceCastable. Тип, возвращаемый изIDynamicInterfaceCastable.GetInterfaceImplementation(), используется для обёртывания типа, реализующегоIDynamicInterfaceCastable. Главное здесь в том, что указательthis— это не то, чем оно кажется, потому что мы допускаем ситуацию отDemoNativeDynamicWrapperкIDemoStoreTypeNativeWrapper.
Переадресация вызовов на экземпляр COM
Независимо от того, какая объектная оболочка используется, требуется возможность вызова функций в COM-экземпляре. Реализация IDemoStoreTypeNativeWrapper.StoreString() может служить примером использования unmanaged указателей функций 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);
}
}
Давайте рассмотрим разыменование экземпляра COM, чтобы получить доступ к его реализации виртуальной таблицы (vtable). COM ABI определяет, что первый указатель объекта указывает на vtable этого типа, и оттуда можно получить доступ к нужному слоту. Предположим, адрес COM-объекта 0x10000. Первое значение размера указателя должно быть адресом vtable в этом примере 0x20000. Когда вы находитесь в vtable, найдите четвертый слот (индекс 3 в индексации, начинающейся с нуля), чтобы получить доступ к StoreString() реализации.
COM instance
0x10000 0x20000
VTable for IDemoStoreType
0x20000 <Address of QueryInterface>
0x20008 <Address of AddRef>
0x20010 <Address of Release>
0x20018 <Address of StoreString>
После этого указатель функции позволяет отправлять эту функцию-член в этот объект, передав экземпляр объекта в качестве первого параметра. Этот шаблон должен выглядеть знакомым на основе определений функций реализации оболочки управляемого объекта.
После реализации метода CreateObject() подкласс ComWrappers сможет создавать оболочки для собственных объектов для экземпляров COM, реализующих как IDemoGetType, так и IDemoStoreType.
IntPtr iunk = ...; // Get a COM instance from native code.
object rcw = cw.GetOrCreateObjectForComInstance(iunk, CreateObjectFlags.UniqueInstance);
Шаг 4. Обработка сведений о времени существования оболочки собственного объекта
Реализации ComputeVtables() и CreateObject() охватили некоторые подробности времени жизни оболочки, но есть и другие аспекты для рассмотрения. Хотя это может быть короткий шаг, он также может значительно увеличить сложность ComWrappers дизайна.
В отличие от оболочки управляемого объекта, который управляется вызовами его AddRef() и Release() методов, временной цикл оболочки нативного объекта недетерминированно обрабатывается GC. Когда оболочка для родного объекта вызывает Release() на IntPtr, представляющий экземпляр COM? Существует два общих контейнера:
Средство завершения обёртки нативного объекта отвечает за вызов метода COM-экземпляра
Release(). Это единственный момент, когда можно безопасно вызывать этот метод. На этом этапе в среде выполнения .NET нет других ссылок на оболочку машинного объекта. Здесь могут возникнуть сложности, если вы правильно поддерживаете COM Apartments. Для получения дополнительной информации, см. раздел «Дополнительные рекомендации».Обертка родного объекта реализует
IDisposableи вызываетRelease()вDispose().
Замечание
Шаблон IDisposable должен поддерживаться только в том случае, если во время CreateObject() вызова был передан флаг CreateObjectFlags.UniqueInstance. Если это требование не выполняется, возможно повторное использование удалённых оболочек собственных объектов после их удаления.
Использование подкласса ComWrappers
Теперь у вас есть ComWrappers подкласс, который можно протестировать. Чтобы избежать создания собственной библиотеки, которая возвращает экземпляр COM, реализующий IDemoGetType и IDemoStoreType, вы будете использовать оболочку управляемого объекта и рассматривать ее как COM-экземпляр. Это необходимо сделать так, чтобы передать его COM в любом случае.
Сначала создадим оболочку управляемого объекта. Создайте DemoImpl экземпляр и отобразите текущее строковое состояние.
var demo = new DemoImpl();
string? value = demo.GetString();
Console.WriteLine($"Initial string: {value ?? "<null>"}");
Теперь можно создать экземпляр DemoComWrappers и управляемую оболочку объекта, которую затем можно передать в COM-среду.
var cw = new DemoComWrappers();
IntPtr ccw = cw.GetOrCreateComInterfaceForObject(demo, CreateComInterfaceFlags.None);
Вместо передачи оболочки управляемого объекта в COM-среду представьте, что вы только что получили этот COM-экземпляр, поэтому создайте для него оболочку нативного объекта.
var rcw = cw.GetOrCreateObjectForComInstance(ccw, CreateObjectFlags.UniqueInstance);
С помощью оболочки нативного объекта вы сможете привести его к одному из желательных интерфейсов и использовать его в качестве обычного управляемого объекта. Вы можете проверить DemoImpl экземпляр и наблюдать за воздействием операций на оболочку для нативного объекта, которая заключает оболочку управляемого объекта, а та, в свою очередь, заключает управляемый экземпляр.
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}");
Так как подкласс ComWrapper был разработан для поддержки CreateObjectFlags.UniqueInstance, вы можете очистить оболочку машинного объекта сразу же, а не ожидать возникновения GC.
(rcw as IDisposable)?.Dispose();
Активация COM с помощью ComWrappers
Создание COM-объектов обычно выполняется с помощью активации COM — сложного сценария за пределами области этого документа. Чтобы следовать за концептуальным шаблоном, мы представляем CoCreateInstance() API, используемый для активации COM, и иллюстрируем, как его можно использовать с ComWrappers.
Предположим, что в приложении есть следующий код C#. В приведенном ниже примере используется CoCreateInstance() для активации класса COM и встроенной системы взаимодействия COM для маршалирования экземпляра COM в соответствующий интерфейс. Обратите внимание, что использование typeof(I).GUID ограничено утверждением и является случаем использования отражения, которое может влиять на совместимость кода с 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);
Процесс преобразования приведенного выше для использования ComWrappers требует удаления MarshalAs(UnmanagedType.Interface) из CoCreateInstance() P/Invoke и выполнения маршаллинга вручную.
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);
Кроме того, можно абстрагировать функции фабричного стиля, например ActivateClass<I>, включив логику активации в конструктор класса для нативной оболочки объекта. Конструктор может использовать ComWrappers.GetOrRegisterObjectForComInstance() API для связывания созданного управляемого объекта с активированным COM-экземпляром.
Дополнительные рекомендации
Нативная AOT — заранее (AOT) компиляция обеспечивает улучшенное время запуска, поскольку избегается JIT-компиляция. Удаление необходимости компиляции JIT также часто требуется на некоторых платформах. Поддержка AOT была целью API ComWrappers, но любая реализация оболочки должна быть тщательной, чтобы непреднамеренно не создавать случаев, когда AOT выходит из строя, например, при использовании отражения. Это свойство Type.GUID является примером того, где используется рефлексия, но неочевидным образом. Свойство Type.GUID использует отражение для проверки атрибутов типа, а затем, потенциально, имени типа и сборки, в которой оно содержится, для создания его значения.
Генерация исходного кода — большая часть кода, необходимого для взаимодействия COM, и ComWrappers реализация, скорее всего, могут быть автоматически сгенерированы с помощью некоторых инструментов. Источник для обоих типов оболочки можно создать с учетом соответствующих определений COM, например библиотеки типов (TLB), IDL или основной сборки взаимодействия (PIA).
Глобальная регистрация . Так как ComWrappers API был разработан в качестве нового этапа взаимодействия COM, он должен иметь некоторый способ частично интегрироваться с существующей системой. Существуют статические методы глобального действия в ComWrappers API, позволяющие зарегистрировать глобальный экземпляр для различной поддержки. Эти методы предназначены для ComWrappers экземпляров, которые ожидают предоставления комплексной поддержки COM-взаимодействия во всех случаях — это похоже на встроенную систему взаимодействия COM.
Поддержка средства отслеживания ссылок — эта поддержка в первую очередь используется для сценариев WinRT и представляет собой сложный сценарий. Для большинства ComWrapper реализаций, либо CreateComInterfaceFlags.TrackerSupport, либо CreateObjectFlags.TrackerObject флаг должен вызвать NotSupportedException исключение. Если вы хотите включить эту поддержку, возможно, на платформе Windows или даже не в Windows, настоятельно рекомендуется ссылаться на цепочку инструментов C#/WinRT.
Помимо времени жизни, системы типов и функциональных возможностей, которые ранее обсуждались, реализация ComWrappers, совместимая с COM, требует дополнительных соображений. Для любой реализации, которая будет использоваться на платформе Windows, существуют следующие рекомендации.
Апартаменты — организационная структура COM для потоков называется "Апартаменты" и имеет строгие правила, которые должны соблюдаться для стабильного функционирования. В этом руководстве не реализованы обертки для нативных объектов с поддержкой квартирных моделей, но любая реализация, готовая к использованию, должна поддерживать работу с квартирными моделями. Для этого мы рекомендуем использовать API
RoGetAgileReference, введённый в Windows 8. Для версий до Windows 8 рассмотрим глобальную таблицу интерфейсов.Безопасность — COM предоставляет богатую модель безопасности для активации классов и делегированных разрешений.