Создание источника для ComWrappers

.NET 8 представляет генератор источников, который создает реализацию API ComWrappers для вас. Генератор распознает GeneratedComInterfaceAttribute.

Встроенная среда выполнения .NET (не созданная из источника), система взаимодействия COM только для Windows создает заглушку IL — поток инструкций IL, которые JIT-ed — во время выполнения, чтобы упростить переход с управляемого кода на COM и наоборот. Так как заглушка IL создается во время выполнения, она несовместима с обрезкой NativeAOT и IL. Создание заглушки во время выполнения также может затруднить диагностику проблем маршаллинга.

Встроенное взаимодействие использует такие атрибуты, как ComImport или DllImport, которые зависят от создания кода во время выполнения. В приведенном ниже коде показан соответствующий пример:

[ComImport]
interface IFoo
{
    void Method(int i);
}

[DllImport("MyComObjectProvider.dll")]
static nint GetPointerToComInterface();

[DllImport("MyComObjectProvider.dll")]
static void GivePointerToComInterface(nint comObject);

// Use the system to create a Runtime Callable Wrapper to use in managed code
nint ptr = GetPointerToComInterface();
IFoo foo = (IFoo)Marshal.GetObjectForIUnknown(ptr);
foo.Method(0);
...
// Use the system to create a COM Callable Wrapper to pass to unmanaged code
IFoo foo = GetManagedIFoo();
nint ptr = Marshal.GetIUnknownForObject(foo);
GivePointerToComInterface(ptr);

ComWrappers API позволяет взаимодействовать с COM в C# без использования встроенной com-системы, но требует существенного стандартного и рукописного кода. Генератор интерфейсов COM автоматизирует этот процесс и упрощает ComWrappers процесс, как встроенный COM, но обеспечивает его в обрезке и удобном для AOT способе.

Базовое использование

Чтобы использовать генератор COM-интерфейса, добавьте GeneratedComInterfaceAttribute атрибуты в GuidAttribute определение интерфейса, из которого необходимо импортировать или предоставить com. Тип должен быть помечен partial и internal иметь или public видимость для созданного кода, чтобы иметь доступ к нему.

[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
internal partial interface IFoo
{
    void Method(int i);
}

Затем, чтобы предоставить класс, реализующий интерфейс к COM, добавьте GeneratedComClassAttribute его в класс реализации. Этот класс также должен быть partial и либо internalpublic.

[GeneratedComClass]
internal partial class Foo : IFoo
{
    public void Method(int i)
    {
        // Do things
    }
}

Во время компиляции генератор создает реализацию API ComWrappers, и вы можете использовать StrategyBasedComWrappers тип или пользовательский производный тип для использования или предоставления com-интерфейса.

[LibraryImport("MyComObjectProvider.dll")]
static nint GetPointerToComInterface();

[LibraryImport("MyComObjectProvider.dll")]
static void GivePointerToComInterface(nint comObject);

// Use the ComWrappers API to create a Runtime Callable Wrapper to use in managed code
ComWrappers cw = new StrategyBasedComWrappers();
nint ptr = GetPointerToComInterface();
IFoo foo = (IFoo)cw.GetOrCreateObjectForComInterface(ptr);
foo.Method(0);
...
// Use the system to create a COM Callable Wrapper to pass to unmanaged code
ComWrappers cw = new StrategyBasedComWrappers();
Foo foo = new();
nint ptr = cw.GetOrCreateComInterfaceForObject(foo);
GivePointerToComInterface(ptr);

Настройка маршаллинга

Генератор com-интерфейса учитывает MarshalUsingAttribute атрибут и некоторые виды использования атрибута MarshalAsAttribute для настройки маршаллинга параметров. Дополнительные сведения см. в статье о настройке маршалинга, созданного MarshalUsing источником, с помощью атрибута и настройке маршалинга параметров с помощью атрибутаMarshalAs. GeneratedComInterfaceAttribute.StringMarshallingCustomType Свойства GeneratedComInterfaceAttribute.StringMarshalling применяются ко всем параметрам и типам возвращаемых типов string в интерфейсе, если они не имеют других атрибутов маршаллинга.

Неявные HRESULTs и preserveSig

Методы COM в C# имеют другую сигнатуру, отличную от собственных методов. Стандартный COM имеет тип возвращаемого значения HRESULT, 4 байтового целочисленного типа, представляющего состояния ошибок и успешности. Это HRESULT возвращаемое значение по умолчанию скрыто в сигнатуре C# и преобразуется в исключение при возврате значения ошибки. Последний параметр out собственной сигнатуры COM может быть преобразован в возвращаемый в сигнатуре C#.

Например, в следующих фрагментах кода показаны подписи методов C# и соответствующая собственная подпись генератора.

void Method1(int i);

int Method2(float i);
HRESULT Method1(int i);

HRESULT Method2(float i, _Out_ int* returnValue);

Если вы хотите обработать HRESULT себя, можно использовать PreserveSigAttribute метод в методе, чтобы указать, что генератор не должен делать это преобразование. В следующих фрагментах кода показано, какую собственную сигнатуру ожидает генератор при [PreserveSig] применении. Методы COM должны возвращать HRESULT, поэтому возвращаемое значение любого метода PreserveSig должно быть int.

[PreserveSig]
int Method1(int i, out int j);

[PreserveSig]
int Method2(float i);
HRESULT Method1(int i, int* j);

HRESULT Method2(float i);

Дополнительные сведения см. в разделе "Неявные переводы сигнатур метода" в взаимодействиях .NET

Несовместимость и различия со встроенным COM

Только IUnknown

Единственной поддерживаемой базой интерфейса является IUnknown. Интерфейсы с InterfaceTypeAttribute значением, отличным InterfaceIsIUnknown от того, которые не поддерживаются в исходном коде COM. Любые интерфейсы без InterfaceTypeAttribute предполагается наследовать.IUnknown Это отличается от встроенного COM, где используется InterfaceIsDualзначение по умолчанию.

Маршаллирование по умолчанию и поддержка

Сгенерированный источником COM имеет несколько различных поведения маршалинга по умолчанию от встроенного COM.

  • Во встроенной системе COM все типы имеют неявный атрибут, за исключением массивов неявных элементов, которые имеют неявные [In][In, Out] атрибуты. В исходном коде COM все типы, включая массивы элементов, которые можно семантикой, имеют [In] семантику.

  • [In] и [Out] атрибуты разрешены только в массивах. [In, Out] Если [Out] или поведение требуется для других типов, используйте in модификаторы параметров и out параметров.

Производные интерфейсы

В встроенной системе COM, если у вас есть интерфейсы, производные от других COM-интерфейсов, необходимо объявить теневой метод для каждого базового метода в базовых интерфейсах с new ключевое слово. Дополнительные сведения см. в статье о наследовании интерфейсов COM и .NET.

[ComImport]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IBase
{
    void Method1(int i);
    void Method2(float i);
}

[ComImport]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IDerived : IBase
{
    new void Method1(int i);
    new void Method2(float f);
    void Method3(long l);
    void Method4(double d);
}

Генератор интерфейсов COM не ожидает тени базовых методов. Чтобы создать метод, наследуемый от другого, просто укажите базовый интерфейс как базовый интерфейс C# и добавьте методы производного интерфейса. Дополнительные сведения см . в документации по проектированию.

[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IBase
{
    void Method1(int i);
    void Method2(float i);
}

[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
interface IDerived : IBase
{
    void Method3(long l);
    void Method4(double d);
}

Обратите внимание, что интерфейс с GeneratedComInterface атрибутом может наследоваться только от одного базового интерфейса, имеющего GeneratedComInterface атрибут.

Маршал API

Некоторые API-интерфейсы несовместимы Marshal с созданными источником COM. Замените эти методы соответствующими методами ComWrappers реализации.

См. также