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.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);

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.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);

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ą atrybutuMarshalUsing 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 HRESULT4 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ć HRESULTwartość , 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 IUnknownklasy . 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żyj in modyfikatorów parametrów i out .

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.

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 .

Zobacz też