Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
.NET 8 представляет генератор источников, который создает реализацию API ComWrappers для вас. Генератор распознает GeneratedComInterfaceAttribute.
Встроенная в среду выполнения .NET (а не создание из исходного кода) система взаимодействия COM, доступная только для Windows, генерирует IL-заглушку — поток инструкций IL, которые компилируются во время выполнения с использованием технологии JIT — чтобы облегчить переход от управляемого кода к 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.StringMarshalling и GeneratedComInterfaceAttribute.StringMarshallingCustomType применяются ко всем параметрам и возвращаемым типам типа 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. Интерфейсы со значением, отличным от InterfaceIsIUnknown, не поддерживаются в сгенерированном COM-коде. Любые интерфейсы без InterfaceTypeAttribute предполагается наследовать от IUnknown. Это отличается от встроенного COM, где используется InterfaceIsDualзначение по умолчанию.
Маршаллирование по умолчанию и поддержка
COM, сгенерированный из исходного кода, имеет несколько отличающихся по умолчанию поведения маршалинга по сравнению с встроенным COM.
Во встроенной системе COM все типы имеют неявный атрибут
[In], за исключением массивов управляемых элементов, которые имеют неявный атрибут[In, Out]. В сгенерированном из исходного кода COM, все типы, включая массивы элементарных типов данных, имеют[In]семантику.[In]и[Out]атрибуты разрешены только в массивах. Если требуется поведение[Out]или[In, 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.