Руководство. Использование ComWrappers
API
В этом руководстве вы узнаете, как правильно подклассировать ComWrappers
тип для предоставления оптимизированного и удобного для AOT решения взаимодействия COM. Перед началом работы с этим руководством необходимо ознакомиться с COM, его архитектурой и существующими решениями взаимодействия COM.
В этом руководстве описано, как реализовать следующие определения интерфейса. Эти интерфейсы и их реализации демонстрируют:
- Маршаллирование и немарсхолинг типов через границу COM/.NET.
- Два различных подхода к потреблению собственных COM-объектов в .NET.
- Рекомендуемый шаблон для включения пользовательского ВЗАИМОДЕЙСТВИЯ COM в .NET 5 и более позднюю.
Весь исходный код, используемый в этом руководстве, доступен в репозитории dotnet/samples.
Примечание.
В пакете SDK для .NET 8 и более поздних версиях генератор источника предоставляется для автоматического ComWrappers
создания реализации API. Дополнительные сведения см. в разделе 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).
Эти эффективность выполняются путем создания и приобретения оболочки для прохождения через ComWrappers
API.
ComWrappers
Так как API имеет так мало обязанностей, это означает, что большая часть работы взаимодействия должна обрабатываться потребителем - это верно. Однако дополнительная работа является в значительной степени механическим и может выполняться решением для создания источника. Например, цепочка инструментов C#/WinRT — это решение для создания источников, созданное на основе ComWrappers
поддержки взаимодействия WinRT.
Реализация подкласса ComWrappers
Предоставление подкласса означает предоставление достаточной ComWrappers
информации среде выполнения .NET для создания и записи оболочки управляемых объектов, проецируемых в объекты COM и COM, проецируемых в .NET. Прежде чем мы рассмотрим структуру подкласса, мы должны определить некоторые термины.
Оболочка управляемых объектов — управляемые объекты .NET требуют оболочки для включения использования из среды non-.NET. Эти оболочки исторически называются вызываемыми COM-оболочками (CCW).
Оболочка собственных объектов — COM-объекты, реализованные на языке non-.NET, требуют оболочки для включения использования из .NET. Эти оболочки исторически называются вызываемыми оболочками среды выполнения (RCW).
Шаг 1. Определение методов для реализации и понимания их намерения
Чтобы расширить ComWrappers
тип, необходимо реализовать следующие три метода. Каждый из этих методов представляет участие пользователя в создании или удалении типа оболочки. CreateObject()
Методы ComputeVtables()
создают оболочку управляемого объекта и оболочку собственного объекта соответственно. Метод ReleaseObjects()
используется средой выполнения для выполнения запроса для предоставленной коллекции оболочки, которую нужно освободить из базового собственного объекта. В большинстве случаев текст ReleaseObjects()
метода может просто вызываться NotImplementedException, так как он вызывается только в расширенном сценарии с использованием платформы Отслеживания ссылок.
// 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-квартиры; Дополнительные сведения см. в разделе "Дополнительные рекомендации".Оболочка машинного объекта реализует
IDisposable
и вызываетRelease()
вDispose()
.
Примечание.
Шаблон IDisposable
должен поддерживаться только в том случае, CreateObjectFlags.UniqueInstance
если во время CreateObject()
вызова флаг был передан. Если это требование не следует, можно повторно использовать удаленные оболочки собственных объектов после удаления.
Использование подкласса 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 предоставляет многофункциональную модель безопасности для активации класса и разрешения на прокси-сервер.