Руководство. Использование 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 являются:

Эти эффективность выполняются путем создания и приобретения оболочки для прохождения через 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);
    }
}

В этом примере важно отметить два важных элемента:

  1. Атрибут DynamicInterfaceCastableImplementationAttribute. Этот атрибут требуется для любого типа, возвращаемого методом IDynamicInterfaceCastable . Он имеет дополнительное преимущество, что упрощает обрезку IL, что означает, что сценарии AOT более надежны.
  2. Приведение к DemoNativeDynamicWrapper. Это часть динамической природы IDynamicInterfaceCastable. Тип, возвращаемый из IDynamicInterfaceCastable.GetInterfaceImplementation() , используется для "одеяла" типа, реализующего IDynamicInterfaceCastable. Здесь указатель не то this , что он притворяется, потому что мы разрешаем дело от DemoNativeDynamicWrapperIDemoStoreTypeNativeWrapper.

Переадресация вызовов экземпляра 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? Существует два общих контейнера:

  1. Средство завершения оболочки собственного объекта отвечает за вызов метода COM-экземпляра Release() . Это единственный раз, когда он безопасно вызывать этот метод. На этом этапе в среде выполнения .NET нет других ссылок на оболочку машинного объекта. Здесь может быть сложность, если вы правильно поддерживаете COM-квартиры; Дополнительные сведения см. в разделе "Дополнительные рекомендации".

  2. Оболочка машинного объекта реализует 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.TrackerSupportCreateObjectFlags.TrackerObject либо флаг должен вызвать NotSupportedExceptionисключение. Если вы хотите включить эту поддержку, возможно, на платформе Windows или даже не в Windows, настоятельно рекомендуется ссылаться на цепочку инструментов C#/WinRT.

Помимо времени существования, системы типов и функциональных функций, которые ранее обсуждались, реализация ComWrappers , совместимая с COM, требует дополнительных рекомендаций. Для любой реализации, которая будет использоваться на платформе Windows, существуют следующие рекомендации.

  • Квартиры — организационная структура COM для потоков называется "Квартиры " и имеет строгие правила, которые должны соблюдаться для стабильных операций. В этом руководстве не реализованы оболочки собственных объектов с поддержкой квартир, но любая реализация, готовая к работе, должна быть осведомлена о квартире. Для этого рекомендуется использовать API, представленный RoGetAgileReference в Windows 8. Для версий до Windows 8 рассмотрим глобальную таблицу интерфейсов.

  • Безопасность — COM предоставляет многофункциональную модель безопасности для активации класса и разрешения на прокси-сервер.