Brongeneratie voor ComWrappers
.NET 8 introduceert een brongenerator die een implementatie van de ComWrappers-API voor u maakt. De generator herkent de GeneratedComInterfaceAttribute.
Het ingebouwde .NET Runtime-systeem (niet door bron gegenereerd), Windows-only, COM-interop-systeem genereert een IL-stub( een stroom IL-instructies die JIT-ed is) tijdens runtime om de overgang van beheerde code naar COM te vergemakkelijken en vice versa. Omdat deze IL-stub tijdens runtime wordt gegenereerd, is deze niet compatibel met NativeAOT en IL-bijsnijding. Stub generatie tijdens runtime kan ook het diagnosticeren van marshalling problemen moeilijk maken.
Ingebouwde interoperabiliteit maakt gebruik van kenmerken zoals ComImport
of DllImport
, die afhankelijk zijn van het genereren van code tijdens runtime. In de volgende code ziet u een voorbeeld hiervan:
[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);
De ComWrappers
API maakt interactie mogelijk met COM in C# zonder gebruik te maken van het ingebouwde COM-systeem, maar vereist aanzienlijke standaard en handgeschreven onveilige code. De COM-interfacegenerator automatiseert dit proces en maakt ComWrappers
het net zo eenvoudig als ingebouwde COM, maar levert het op een inkortbare en AOT-vriendelijke manier.
Basaal gebruik
Als u de COM-interfacegenerator wilt gebruiken, voegt u de GeneratedComInterfaceAttribute en GuidAttribute kenmerken toe aan de interfacedefinitie waaruit u wilt importeren of beschikbaar wilt maken voor COM. Het type moet worden gemarkeerd partial
en moet de gegenereerde code of public
zichtbaarheid hebben internal
om er toegang toe te krijgen.
[GeneratedComInterface]
[Guid("3faca0d2-e7f1-4e9c-82a6-404fd6e0aab8")]
internal partial interface IFoo
{
void Method(int i);
}
Als u vervolgens een klasse beschikbaar wilt maken die een interface implementeert op COM, voegt u de GeneratedComClassAttribute klasse toe aan de implementatieklasse. Deze klasse moet ook en partial
of internal
public
.
[GeneratedComClass]
internal partial class Foo : IFoo
{
public void Method(int i)
{
// Do things
}
}
Tijdens het compileren maakt de generator een implementatie van de ComWrappers-API en kunt u het StrategyBasedComWrappers type of een aangepast afgeleid type gebruiken om de COM-interface te gebruiken of beschikbaar te maken.
[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);
Marshalling aanpassen
De COM-interfacegenerator respecteert het MarshalUsingAttribute kenmerk en sommige gebruiksrechten van het MarshalAsAttribute kenmerk om marshalling van parameters aan te passen. Zie voor meer informatie hoe u door de bron gegenereerde marshalling kunt aanpassen met het MarshalUsing
kenmerk en het aanpassen van parameter marshalling met het MarshalAs
kenmerk. De GeneratedComInterfaceAttribute.StringMarshalling eigenschappen zijn GeneratedComInterfaceAttribute.StringMarshallingCustomType van toepassing op alle parameters en retourtypen in string
de interface als ze geen andere marshallingkenmerken hebben.
Impliciete HRESULTs en PreserveSig
COM-methoden in C# hebben een andere handtekening dan de systeemeigen methoden. Standaard COM heeft een retourtype, een geheel getal van HRESULT
4 byte dat fout- en successtatussen vertegenwoordigt. Deze HRESULT
retourwaarde is standaard verborgen in de C#-handtekening en geconverteerd naar een uitzondering wanneer een foutwaarde wordt geretourneerd. De laatste out-parameter van de systeemeigen COM-handtekening kan eventueel worden geconverteerd naar de return in de C#-handtekening.
In de volgende fragmenten worden bijvoorbeeld C#-methodehandtekeningen en de bijbehorende systeemeigen handtekening weergegeven die door de generator wordt afgeleid.
void Method1(int i);
int Method2(float i);
HRESULT Method1(int i);
HRESULT Method2(float i, _Out_ int* returnValue);
Als u de HRESULT
uzelf wilt afhandelen, kunt u de PreserveSigAttribute methode gebruiken om aan te geven dat de generator deze transformatie niet mag uitvoeren. De volgende codefragmenten laten zien welke systeemeigen handtekening de generator verwacht wanneer [PreserveSig]
deze wordt toegepast. COM-methoden moeten worden geretourneerd HRESULT
, dus de retourwaarde van een methode met PreserveSig
moet zijn int
.
[PreserveSig]
int Method1(int i, out int j);
[PreserveSig]
int Method2(float i);
HRESULT Method1(int i, int* j);
HRESULT Method2(float i);
Zie Impliciete vertalingen van methodehandtekening in .NET-interop voor meer informatie
Incompatibiliteit en verschillen in ingebouwde COM
IUnknown
alleen
De enige ondersteunde interfacebasis is IUnknown
. Interfaces met een InterfaceTypeAttribute andere waarde dan InterfaceIsIUnknown worden niet ondersteund in COM die door de bron wordt gegenereerd. Alle interfaces zonder een InterfaceTypeAttribute
worden ervan uitgegaan dat ze worden afgeleid van IUnknown
. Dit verschilt van ingebouwde COM waar de standaardwaarde is InterfaceIsDual.
Standaardinstellingen en ondersteuning van marshalling
Door de bron gegenereerde COM heeft een aantal verschillende standaard marshallinggedrag van ingebouwde COM.
In het ingebouwde COM-systeem hebben alle typen een impliciet
[In]
kenmerk, met uitzondering van matrices met belichte elementen, die impliciete[In, Out]
kenmerken hebben. In bron gegenereerde COM hebben[In]
alle typen, waaronder matrices van belichte elementen, semantiek.[In]
en[Out]
kenmerken zijn alleen toegestaan voor matrices. Als[Out]
of[In, Out]
gedrag vereist is voor andere typen, gebruikt u dein
enout
parameteraanpassingen.
Afgeleide interfaces
Als u in het ingebouwde COM-systeem interfaces hebt die zijn afgeleid van andere COM-interfaces, moet u een schaduwmethode declareren voor elke basismethode op de basisinterfaces met het new
trefwoord. Zie COM-interfaceovername en .NET voor meer informatie.
[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);
}
De COM-interfacegenerator verwacht geen schaduw van basismethoden. Als u een methode wilt maken die van een andere methode overkomt, geeft u de basisinterface aan als een C#-basisinterface en voegt u de methoden van de afgeleide interface toe. Zie het ontwerpdocument voor meer informatie.
[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);
}
Houd er rekening mee dat een interface met het GeneratedComInterface
kenmerk slechts kan overnemen van één basisinterface die het GeneratedComInterface
kenmerk heeft.
Afgeleide interfaces over assemblygrenzen
In .NET 8 wordt het niet ondersteund om een interface te definiëren met het GeneratedComInterfaceAttribute kenmerk dat is afgeleid van een GeneratedComInterface
-toegeschreven interface die is gedefinieerd in een andere assembly.
In .NET 9 en latere versies wordt dit scenario ondersteund met de volgende beperkingen:
- Het type basisinterface moet worden gecompileerd met hetzelfde doelframework als het afgeleide type.
- Het type basisinterface mag geen leden van de basisinterface schaduwen, als deze er een heeft.
Bovendien worden eventuele wijzigingen in gegenereerde virtuele methode-offsets in de basisinterfaceketen die in een andere assembly is gedefinieerd, pas in de afgeleide interfaces verwerkt als het project opnieuw wordt opgebouwd.
Notitie
In .NET 9 en latere versies wordt een waarschuwing verzonden bij het overnemen van gegenereerde COM-interfaces over de grenzen van de assembly om u te informeren over de beperkingen en valkuilen van het gebruik van deze functie. U kunt deze waarschuwing uitschakelen om de beperkingen te erkennen en over te nemen over de grenzen van de assembly.
Marshal-API's
Sommige API's in Marshal zijn niet compatibel met door de bron gegenereerde COM. Vervang deze methoden door de bijbehorende methoden voor een ComWrappers
implementatie.