Generowanie źródła dla comWrappers
Platforma .NET 8 wprowadza generator źródła, który tworzy implementację interfejsu API ComWrappers . Generator rozpoznaje element GeneratedComInterfaceAttribute.
Wbudowane środowisko uruchomieniowe platformy .NET (nie generowane źródło), tylko system międzyoperacyjności com systemu Windows generuje wycinkę IL — strumień instrukcji IL, które są JIT-ed — w czasie wykonywania w celu ułatwienia przejścia z kodu zarządzanego do modelu COM i odwrotnie. Ponieważ ten wycink il jest generowany w czasie wykonywania, jest niezgodny z nativeAOT i IL przycinanie. Generowanie wycinków w czasie wykonywania może również utrudnić diagnozowanie problemów z marshalling.
Wbudowane międzyoperajności używają atrybutów, takich jak ComImport
lub DllImport
, które polegają na generowaniu kodu w czasie wykonywania. Poniższy kod przedstawia przykład tego:
[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);
Interfejs ComWrappers
API umożliwia interakcję z com w języku C# bez korzystania z wbudowanego systemu COM, ale wymaga znacznej kotłowej i ręcznie napisanej niebezpiecznej kodu. Generator interfejsu COM automatyzuje ten proces i sprawia ComWrappers
, że jest tak łatwy, jak wbudowany com, ale dostarcza go w sposób przyjazny dla przycinania i AOT.
Podstawowy sposób użycia
Aby użyć generatora interfejsu COM, dodaj GeneratedComInterfaceAttribute atrybuty i GuidAttribute w definicji interfejsu, z której chcesz zaimportować plik COM lub uwidocznić go. Typ musi być oznaczony partial
i mieć internal
widoczność wygenerowanego kodu, public
aby mógł uzyskać do niego dostęp.
[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
internal partial interface IFoo
{
void Method(int i);
}
Następnie, aby uwidocznić klasę, która implementuje interfejs do modelu COM, dodaj element GeneratedComClassAttribute do klasy implementowania. Ta klasa musi również mieć wartość partial
i internal
lub public
.
[GeneratedComClass]
internal partial class Foo : IFoo
{
public void Method(int i)
{
// Do things
}
}
W czasie kompilacji generator tworzy implementację interfejsu API ComWrappers i można użyć StrategyBasedComWrappers typu lub niestandardowego typu pochodnego do użycia lub uwidocznienia interfejsu 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);
Dostosowywanie marshalingu
Generator interfejsu COM uwzględnia MarshalUsingAttribute atrybut i niektóre użycia atrybutu MarshalAsAttribute w celu dostosowania marshalling parametrów. Aby uzyskać więcej informacji, zobacz dostosowywanie marshalingu generowanego przez źródło za pomocą atrybutu MarshalUsing
i dostosowywanie marshaling parametrów za pomocą atrybutu MarshalAs
. Właściwości GeneratedComInterfaceAttribute.StringMarshalling i GeneratedComInterfaceAttribute.StringMarshallingCustomType mają zastosowanie do wszystkich parametrów i zwracanych typów typu string
w interfejsie, jeśli nie mają innych atrybutów marshalling.
Niejawne wartości HRESULTs i PreserveSig
Metody COM w języku C# mają inny podpis niż metody natywne. Standard COM ma zwracany typ , typ liczby całkowitej HRESULT
4 bajtów reprezentujący stan błędu i powodzenia. Ta HRESULT
wartość zwracana jest domyślnie ukryta w podpisie języka C# i konwertowana na wyjątek, gdy zwracana jest wartość błędu. Ostatni parametr "out" natywnego podpisu COM może zostać opcjonalnie przekonwertowany na zwrot w podpisie języka C#.
Na przykład poniższe fragmenty kodu pokazują podpisy metod języka C# i odpowiadający im podpis natywny wnioskowania generatora.
void Method1(int i);
int Method2(float i);
HRESULT Method1(int i);
HRESULT Method2(float i, _Out_ int* returnValue);
Jeśli chcesz samodzielnie obsłużyć HRESULT
tę funkcję, możesz użyć PreserveSigAttribute metody w metodzie , aby wskazać, że generator nie powinien wykonywać tej transformacji. Poniższe fragmenty kodu pokazują, jakiego podpisu natywnego oczekuje generator po [PreserveSig]
zastosowaniu. Metody COM muszą zwracać HRESULT
wartość , więc zwracana wartość dowolnej metody z wartością PreserveSig
powinna mieć wartość int
.
[PreserveSig]
int Method1(int i, out int j);
[PreserveSig]
int Method2(float i);
HRESULT Method1(int i, int* j);
HRESULT Method2(float i);
Aby uzyskać więcej informacji, zobacz Niejawne tłumaczenia podpisów metod w międzyoperacyjności platformy .NET
Niezgodności i różnice w wbudowanym modelu COM
IUnknown
tylko
Jedyną obsługiwaną bazą interfejsu jest IUnknown
. Interfejsy z wartością InterfaceTypeAttribute , która ma wartość inną niż InterfaceIsIUnknown nie są obsługiwane w modelu COM generowanym przez źródło. Przyjmuje się, że wszystkie interfejsy bez elementu InterfaceTypeAttribute
pochodzą z IUnknown
klasy . Różni się to od wbudowanego modelu COM, w którym wartość domyślna to InterfaceIsDual.
Ustawienia domyślne i obsługa marshallingu
Wygenerowany w źródle com ma inne domyślne zachowania marshalingu z wbudowanego modelu COM.
We wbudowanym systemie COM wszystkie typy mają niejawny
[In]
atrybut z wyjątkiem tablic elementów, które mają niejawne[In, Out]
atrybuty. W modelu COM generowanym przez źródło wszystkie typy, w tym tablice elementów w formie tabeli blittable, mają[In]
semantykę.[In]
atrybuty i[Out]
są dozwolone tylko w tablicach. Jeśli[Out]
w innych typach wymagane jest zachowanie lub[In, Out]
, użyjin
modyfikatorów parametrów iout
.
Interfejsy pochodne
W wbudowanym systemie COM, jeśli masz interfejsy pochodzące z innych interfejsów COM, musisz zadeklarować metodę cieniowania dla każdej metody podstawowej na interfejsach bazowych za pomocą słowa kluczowego new
. Aby uzyskać więcej informacji, zobacz Dziedziczenie interfejsu COM i .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);
}
Generator interfejsu COM nie oczekuje żadnych cieni metod podstawowych. Aby utworzyć metodę dziedziczą po innej, po prostu wskaż interfejs podstawowy jako interfejs podstawowy języka C# i dodaj metody interfejsu pochodnego. Aby uzyskać więcej informacji, zobacz dokument projektowy.
[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);
}
Należy pamiętać, że interfejs z atrybutem GeneratedComInterface
może dziedziczyć tylko z jednego interfejsu podstawowego GeneratedComInterface
, który ma atrybut.
Interfejsy pochodne w granicach zestawów
W programie .NET 8 nie jest obsługiwane definiowanie interfejsu GeneratedComInterfaceAttribute z atrybutem pochodzącym z interfejsu przypisanego zdefiniowanego GeneratedComInterface
w innym zestawie.
W programie .NET 9 i nowszych wersjach ten scenariusz jest obsługiwany z następującymi ograniczeniami:
- Typ interfejsu podstawowego musi być skompilowany dla tej samej platformy docelowej co typ pochodny.
- Typ interfejsu podstawowego nie może wycienić żadnych elementów członkowskich interfejsu podstawowego, jeśli ma jeden.
Ponadto wszelkie zmiany wszelkich wygenerowanych przesunięć metody wirtualnej w łańcuchu interfejsu podstawowego zdefiniowanego w innym zestawie nie będą uwzględniane w interfejsach pochodnych do czasu odbudowy projektu.
Uwaga
W programie .NET 9 i nowszych wersjach ostrzeżenie jest emitowane podczas dziedziczenia wygenerowanych interfejsów COM przez granice zestawów w celu poinformowania o ograniczeniach i pułapkach użycia tej funkcji. To ostrzeżenie można wyłączyć, aby potwierdzić ograniczenia i dziedziczyć granice zestawów.
Przeprowadzanie marshalingu interfejsów API
Niektóre interfejsy API w systemie Marshal nie są zgodne ze źródłowym interfejsem COM. Zastąp te metody odpowiednimi metodami implementacji ComWrappers
.