Källgenerering för ComWrappers
.NET 8 introducerar en källgenerator som skapar en implementering av ComWrappers API åt dig. Generatorn känner igen GeneratedComInterfaceAttribute.
.NET-körningens inbyggda (inte källgenererade), Endast Windows- och COM-interop-system genererar en IL-stub – en ström av IL-instruktioner som är JIT-ed – vid körning för att underlätta övergången från hanterad kod till COM och vice versa. Eftersom den här IL-stuben genereras vid körning är den inte kompatibel med NativeAOT - och IL-trimning. Stub-generering vid körning kan också göra det svårt att diagnostisera marshallingproblem.
Inbyggda interop använder attribut som ComImport
eller DllImport
, som förlitar sig på kodgenerering vid körning. Följande kod visar ett exempel på detta:
[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);
API:et ComWrappers
möjliggör interaktion med COM i C# utan att använda det inbyggda COM-systemet, men kräver omfattande pannplåt och handskriven osäker kod. COM-gränssnittsgeneratorn automatiserar den här processen och gör ComWrappers
den lika enkel som inbyggd COM, men levererar den på ett trimmat och AOT-vänligt sätt.
Grundläggande användning
Om du vill använda COM-gränssnittsgeneratorn lägger du till attributen GeneratedComInterfaceAttribute och GuidAttribute i den gränssnittsdefinition som du vill importera från eller exponera för COM. Typen måste vara markerad partial
och ha internal
eller public
synlighet för att den genererade koden ska kunna komma åt den.
[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
internal partial interface IFoo
{
void Method(int i);
}
Lägg sedan till i implementeringsklassen GeneratedComClassAttribute för att exponera en klass som implementerar ett gränssnitt för COM. Den här klassen måste också vara partial
och antingen internal
eller public
.
[GeneratedComClass]
internal partial class Foo : IFoo
{
public void Method(int i)
{
// Do things
}
}
Vid kompileringen skapar generatorn en implementering av ComWrappers-API:et och du kan använda StrategyBasedComWrappers typen eller en anpassad härledd typ för att använda eller exponera COM-gränssnittet.
[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);
Anpassa marshalling
COM-gränssnittsgeneratorn respekterar MarshalUsingAttribute attributet och vissa användningar av MarshalAsAttribute attributet för att anpassa rangering av parametrar. Mer information finns i hur du anpassar källgenererad marshalling med MarshalUsing
attributet och anpassar parameterr marshalling med MarshalAs
attributet. Egenskaperna GeneratedComInterfaceAttribute.StringMarshalling och GeneratedComInterfaceAttribute.StringMarshallingCustomType gäller för alla parametrar och returnerar typer av typ string
i gränssnittet om de inte har andra marshallingattribut.
Implicita HRESULTs och PreserveSig
COM-metoder i C# har en annan signatur än de inbyggda metoderna. Standard COM har en returtyp av , en heltalstyp HRESULT
på 4 byte som representerar fel- och framgångstillstånd. Det här HRESULT
returvärdet är dolt som standard i C#-signaturen och konverteras till ett undantag när ett felvärde returneras. Den sista "out"-parametern för den interna COM-signaturen kan eventuellt konverteras till returen i C#-signaturen.
Följande kodfragment visar till exempel C#-metodsignaturer och motsvarande interna signatur som generatorn härleder.
void Method1(int i);
int Method2(float i);
HRESULT Method1(int i);
HRESULT Method2(float i, _Out_ int* returnValue);
Om du vill hantera HRESULT
dig själv kan du använda PreserveSigAttribute metoden på för att ange att generatorn inte ska göra den här omvandlingen. Följande kodfragment visar vilken intern signatur generatorn förväntar sig när [PreserveSig]
den tillämpas. COM-metoder måste returnera HRESULT
, så returvärdet för alla metoder med PreserveSig
ska vara int
.
[PreserveSig]
int Method1(int i, out int j);
[PreserveSig]
int Method2(float i);
HRESULT Method1(int i, int* j);
HRESULT Method2(float i);
Mer information finns i Implicita metodsignaturöversättningar i .NET interop
Inkompatibiliteter och skillnader i inbyggd COM
IUnknown
bara
Den enda gränssnittsbas som stöds är IUnknown
. Gränssnitt med ett InterfaceTypeAttribute som har ett annat värde än InterfaceIsIUnknown stöds inte i källgenererad COM. Alla gränssnitt utan ett InterfaceTypeAttribute
antas härledas från IUnknown
. Detta skiljer sig från inbyggd COM där standardvärdet är InterfaceIsDual.
Standardinställningar och stöd för marshalling
Källgenererad COM har vissa olika standardbeteenden för marshalling från inbyggd COM.
I det inbyggda COM-systemet har alla typer ett implicit
[In]
attribut förutom matriser med blittable-element som har implicita[In, Out]
attribut. I källgenererad COM har[In]
alla typer, inklusive matriser med blittable-element, semantik.[In]
och[Out]
attribut tillåts endast i matriser. Om[Out]
eller[In, Out]
beteende krävs för andra typer använder duin
parametermodifierarna ochout
.
Härledda gränssnitt
Om du har gränssnitt som härleds från andra COM-gränssnitt i det inbyggda COM-systemet måste du deklarera en skuggningsmetod för varje basmetod i basgränssnitten med nyckelordet new
. Mer information finns i COM-gränssnittsarv och .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-gränssnittsgeneratorn förväntar sig ingen skuggning av basmetoder. Om du vill skapa en metod som ärver från en annan anger du bara basgränssnittet som ett C#-basgränssnitt och lägger till metoderna för det härledda gränssnittet. Mer information finns i designdokumentet.
[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);
}
Observera att ett gränssnitt med GeneratedComInterface
attributet bara kan ärva från ett basgränssnitt som har attributet GeneratedComInterface
.
Härledda gränssnitt över sammansättningsgränser
I .NET 8 stöds det inte att definiera ett gränssnitt med attributet GeneratedComInterfaceAttribute som härleds från ett GeneratedComInterface
-attributgränssnitt som definieras i en annan sammansättning.
I .NET 9 och senare versioner stöds det här scenariot med följande begränsningar:
- Basgränssnittstypen måste kompileras med samma målramverk som den härledda typen.
- Basgränssnittstypen får inte skugga några medlemmar i basgränssnittet, om den har en.
Dessutom redovisas inte eventuella ändringar av eventuella genererade virtuella metodförskjutningar i basgränssnittskedjan som definieras i en annan sammansättning i de härledda gränssnitten förrän projektet återskapas.
Kommentar
I .NET 9 och senare versioner genereras en varning vid ärvning av genererade COM-gränssnitt över sammansättningsgränser för att informera dig om begränsningarna och fallgroparna för att använda den här funktionen. Du kan inaktivera den här varningen för att bekräfta begränsningarna och ärver över sammansättningsgränser.
API:er för marskalk
Vissa API:er i Marshal är inte kompatibla med källgenererad COM. Ersätt dessa metoder med motsvarande metoder för en ComWrappers
implementering.