Создание источника для 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")]
static nint GetPointerToComInterface(); // C definition - IUnknown* GetPointerToComInterface();
[DllImport("MyComObjectProvider")]
static void GivePointerToComInterface(nint comObject); // C definition - void GivePointerToComInterface(IUnknown* pUnk);
// 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
и либо internal
public
.
[GeneratedComClass]
internal partial class Foo : IFoo
{
public void Method(int i)
{
// Do things
}
}
Во время компиляции генератор создает реализацию API ComWrappers, и вы можете использовать StrategyBasedComWrappers тип или пользовательский производный тип для использования или предоставления com-интерфейса.
[LibraryImport("MyComObjectProvider")]
private static partial nint GetPointerToComInterface(); // C definition - IUnknown* GetPointerToComInterface();
[LibraryImport("MyComObjectProvider")]
private static partial void GivePointerToComInterface(nint comObject); // C definition - void GivePointerToComInterface(IUnknown* pUnk);
// 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.GetOrCreateObjectForComInstance(ptr, CreateObjectFlags.None);
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, CreateComInterfaceFlags.None);
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
атрибут.
Производные интерфейсы через границы сборки
В .NET 8 не поддерживается определение интерфейса с GeneratedComInterfaceAttribute атрибутом, производным от GeneratedComInterface
интерфейса-атрибута, определенного в другой сборке.
В .NET 9 и более поздних версиях этот сценарий поддерживается со следующими ограничениями:
- Базовый тип интерфейса должен быть скомпилирован для той же целевой платформы, что и производный тип.
- Базовый тип интерфейса не должен тенеть членов базового интерфейса, если он имеет один.
Кроме того, любые изменения в созданных виртуальных методах смещения в цепочке базовых интерфейсов, определенной в другой сборке, не будут учитываться в производных интерфейсах, пока проект не будет перестроен.
Примечание.
В .NET 9 и более поздних версиях предупреждение создается при наследовании созданных COM-интерфейсов через границы сборки, чтобы сообщить об ограничениях и ошибках использования этой функции. Это предупреждение можно отключить, чтобы подтвердить ограничения и наследовать между границами сборки.
Маршал API
Некоторые API-интерфейсы несовместимы Marshal с созданными источником COM. Замените эти методы соответствующими методами ComWrappers
реализации.