Share via


In aanmerking komende .NET-typen voor COM-interoperation

.NET-typen beschikbaar maken voor COM

Als u typen in een assembly beschikbaar wilt maken voor COM-toepassingen, moet u rekening houden met de vereisten van COM-interop tijdens het ontwerp. Beheerde typen (klasse, interface, structuur en opsomming) kunnen naadloos worden geïntegreerd met COM-typen wanneer u voldoet aan de volgende richtlijnen:

  • Klassen moeten expliciet interfaces implementeren.

    Hoewel COM-interoperabiliteit een mechanisme biedt voor het automatisch genereren van een interface met alle leden van de klasse en de leden van de basisklasse, is het veel beter om expliciete interfaces te bieden. De automatisch gegenereerde interface wordt de klasse-interface genoemd. Zie Inleiding tot de klasse-interface voor richtlijnen.

    U kunt Visual Basic, C# en C++ gebruiken om interfacedefinities in uw code op te nemen in plaats van dat u Interface Definition Language (IDL) of het equivalent ervan moet gebruiken. Zie de taaldocumentatie voor syntaxisdetails.

  • Beheerde typen moeten openbaar zijn.

    Alleen openbare typen in een assembly worden geregistreerd en geëxporteerd naar de typebibliotheek. Hierdoor zijn alleen openbare typen zichtbaar voor COM.

    Beheerde typen maken functies beschikbaar voor andere beheerde code die mogelijk niet beschikbaar zijn voor COM. Zo worden geparameteriseerde constructors, statische methoden en constante velden niet blootgesteld aan COM-clients. Als de runtime marshals gegevens in en uit een type zijn, kunnen de gegevens worden gekopieerd of getransformeerd.

  • Methoden, eigenschappen, velden en gebeurtenissen moeten openbaar zijn.

    Leden van openbare typen moeten ook openbaar zijn als ze zichtbaar zijn voor COM. U kunt de zichtbaarheid van een assembly, een openbaar type of openbare leden van een openbaar type beperken door de ComVisibleAttribute. Standaard zijn alle openbare typen en leden zichtbaar.

  • Typen moeten een openbare parameterloze constructor hebben die moet worden geactiveerd vanuit COM.

    Beheerde, openbare typen zijn zichtbaar voor COM. Zonder een openbare parameterloze constructor (een constructor zonder argumenten) kunnen COM-clients het type echter niet maken. COM-clients kunnen het type nog steeds gebruiken als het op een andere manier wordt geactiveerd.

  • Typen kunnen niet abstract zijn.

    COM-clients en .NET-clients kunnen geen abstracte typen maken.

Wanneer deze wordt geëxporteerd naar COM, wordt de overnamehiërarchie van een beheerd type afgevlakt. Versiebeheer verschilt ook tussen beheerde en onbeheerde omgevingen. Typen die aan COM worden blootgesteld, hebben niet dezelfde versiebeheerkenmerken als andere beheerde typen.

COM-typen van .NET gebruiken

Als u COM-typen van .NET wilt gebruiken en u geen hulpprogramma's zoals Tlbimp.exe (Type Library Importer) wilt gebruiken, moet u de volgende richtlijnen volgen:

  • Interfaces moeten de ComImportAttribute toegepaste interfaces hebben.
  • Interfaces moeten zijn GuidAttribute toegepast met de interface-id voor de COM-interface.
  • Interfaces moeten worden InterfaceTypeAttribute toegepast om het basisinterfacetype van deze interface (IUnknown, IDispatchof) op IInspectablete geven.
    • De standaardoptie is om het basistype van IDispatch de gedeclareerde methoden toe te voegen aan de verwachte virtuele functietabel voor de interface.
    • Alleen .NET Framework biedt ondersteuning voor het opgeven van een basistype.IInspectable

Deze richtlijnen bieden de minimale vereisten voor veelvoorkomende scenario's. Er zijn nog veel meer aanpassingsopties en worden beschreven in Interop-kenmerken toepassen.

COM-interfaces definiëren in .NET

Wanneer .NET-code een methode op een COM-object probeert aan te roepen via een interface met het ComImportAttribute kenmerk, moet er een virtuele functietabel (ook wel vtable of vftable genoemd) worden gemaakt om de .NET-definitie van de interface te vormen om de systeemeigen code te bepalen die moet worden aangeroepen. Dit proces is complex. In de volgende voorbeelden ziet u enkele eenvoudige gevallen.

Overweeg een COM-interface met een paar methoden:

struct IComInterface : public IUnknown
{
    STDMETHOD(Method)() = 0;
    STDMETHOD(Method2)() = 0;
};

Voor deze interface beschrijft de volgende tabel de indeling van de virtuele functietabel:

IComInterface virtuele-functietabelsite Methodenaam
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2

Elke methode wordt toegevoegd aan de virtuele functietabel in de volgorde waarin deze is gedeclareerd. De specifieke volgorde wordt gedefinieerd door de C++-compiler, maar voor eenvoudige gevallen zonder overbelasting definieert de declaratievolgorde de volgorde in de tabel.

Declareer als volgt een .NET-interface die overeenkomt met deze interface:

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid(/* The IID for IComInterface */)]
interface IComInterface
{
    void Method();
    void Method2();
}

Hiermee InterfaceTypeAttribute geeft u de basisinterface op. Het biedt een aantal opties:

ComInterfaceType Waarde Type basisinterface Gedrag voor leden op de toegewezen interface
InterfaceIsIUnknown IUnknown De tabel met virtuele functies heeft eerst de leden van IUnknownen vervolgens de leden van deze interface in declaratievolgorde.
InterfaceIsIDispatch IDispatch Leden worden niet toegevoegd aan de virtuele functietabel. Ze zijn alleen toegankelijk via IDispatch.
InterfaceIsDual IDispatch De tabel met virtuele functies heeft eerst de leden van IDispatchen vervolgens de leden van deze interface in declaratievolgorde.
InterfaceIsIInspectable IInspectable De tabel met virtuele functies heeft eerst de leden van IInspectableen vervolgens de leden van deze interface in declaratievolgorde. Alleen ondersteund op .NET Framework.

COM-interfaceovername en .NET

Het COM-interoperabiliteitssysteem dat gebruikmaakt van de ComImportAttribute interface-overname, zodat dit onverwacht gedrag kan veroorzaken, tenzij er enkele beperkende stappen worden ondernomen.

De COM-brongenerator die gebruikmaakt van het System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute kenmerk communiceert wel met interfaceovername, dus gedraagt het zich meer zoals verwacht.

COM-interfaceovername in C++

In C++kunnen ontwikkelaars COM-interfaces declareren die zijn afgeleid van andere COM-interfaces:

struct IComInterface : public IUnknown
{
    STDMETHOD(Method)() = 0;
    STDMETHOD(Method2)() = 0;
};

struct IComInterface2 : public IComInterface
{
    STDMETHOD(Method3)() = 0;
};

Deze declaratiestijl wordt regelmatig gebruikt als mechanisme om methoden toe te voegen aan COM-objecten zonder bestaande interfaces te wijzigen, wat een belangrijke wijziging zou zijn. Dit overnamemechanisme resulteert in de volgende indelingen voor virtuele functietabellen:

IComInterface virtuele-functietabelsite Methodenaam
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
IComInterface2 virtuele-functietabelsite Methodenaam
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
5 IComInterface2::Method3

Hierdoor is het eenvoudig om een methode aan te roepen die is gedefinieerd IComInterface vanuit een IComInterface2*. Het aanroepen van een methode op een basisinterface vereist geen aanroep om QueryInterface een aanwijzer naar de basisinterface te krijgen. Daarnaast staat C++ een impliciete conversie toe van IComInterface2* naar IComInterface*, wat goed is gedefinieerd en waarmee u kunt voorkomen dat u een QueryInterface opnieuw aanroept. Als gevolg hiervan hoeft u in C of C++ nooit aan te roepen QueryInterface om naar het basistype te gaan als u dat niet wilt, wat prestatieverbeteringen kan toestaan.

Notitie

WinRT-interfaces volgen dit overnamemodel niet. Ze worden gedefinieerd om hetzelfde model te volgen als het [ComImport]com-interop-model op basis van .NET.

Overname van interface met ComImportAttribute

In .NET is C#-code die lijkt op overname van interface niet daadwerkelijk overname van interface. Kijk eens naar de volgende code:

interface I
{
    void Method1();
}
interface J : I
{
    void Method2();
}

Deze code zegt niet: 'J implementeert I'. De code zegt eigenlijk: "elk type dat wordt geïmplementeerd J , moet ook worden geïmplementeerd I." Dit verschil leidt tot de fundamentele ontwerpbeslissing die interfaceovername maakt in ComImportAttribute-based interop unergonomic. Interfaces worden altijd zelfstandig beschouwd; De basisinterfacelijst van een interface heeft geen invloed op berekeningen om een virtuele functietabel voor een bepaalde .NET-interface te bepalen.

Als gevolg hiervan leidt het natuurlijke equivalent van het vorige C++ COM-interfacevoorbeeld tot een andere indeling voor virtuele functietabellen.

C#-code:

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
    void Method();
    void Method2();
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
    void Method3();
}

Indelingen voor virtuele functietabellen:

IComInterface virtuele-functietabelsite Methodenaam
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
IComInterface2 virtuele-functietabelsite Methodenaam
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface2::Method3

Aangezien deze virtuele functietabellen verschillen van het C++-voorbeeld, leidt dit tot ernstige problemen tijdens runtime. De juiste definitie van deze interfaces in .NET met ComImportAttribute is als volgt:

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
    void Method();
    void Method2();
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
    new void Method();
    new void Method2();
    void Method3();
}

Op metagegevensniveau wordt niet geïmplementeerd, IComInterface2 maar wordt alleen aangegeven dat implementeerfuncties van IComInterface2 ook moeten worden geïmplementeerdIComInterface.IComInterface Daarom moet elke methode van de basisinterfacetypen opnieuw worden aangegeven.

Overname van interface met GeneratedComInterfaceAttribute (.NET 8 en hoger)

De COM-brongenerator die wordt geactiveerd door de implementatie van de GeneratedComInterfaceAttribute C#-interfaceovername als COM-interfaceovername, zodat de virtuele functietabellen worden ingedeeld zoals verwacht. Als u het vorige voorbeeld gebruikt, is de juiste definitie van deze interfaces in .NET System.Runtime.InteropServices.Marshalling.GeneratedComInterfaceAttribute als volgt:

[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface
{
    void Method();
    void Method2();
}

[GeneratedComInterface]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IComInterface2 : IComInterface
{
    void Method3();
}

De methoden van de basisinterfaces hoeven niet opnieuw te worden aangegeven en mogen niet opnieuw worden aangegeven. In de volgende tabel worden de resulterende virtuele functietabellen beschreven:

IComInterface virtuele-functietabelsite Methodenaam
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
IComInterface2 virtuele-functietabelsite Methodenaam
0 IUnknown::QueryInterface
1 IUnknown::AddRef
2 IUnknown::Release
3 IComInterface::Method
4 IComInterface::Method2
5 IComInterface2::Method3

Zoals u ziet, komen deze tabellen overeen met het C++-voorbeeld, zodat deze interfaces correct werken.

Zie ook