Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
När en COM-klient anropar ett .NET-objekt skapar den gemensamma språkkörningen det hanterade objektet och en COM-anropsbar omslutning (CCW) för objektet. Det går inte att referera till ett .NET-objekt direkt, COM-klienter använder CCW som proxy för det hanterade objektet.
Körningen skapar exakt en CCW för ett hanterat objekt, oavsett antalet COM-klienter som begär dess tjänster. Som följande bild visar kan flera COM-klienter ha en referens till CCW som exponerar INew-gränssnittet. CCW innehåller i sin tur en enda referens till det hanterade objektet som implementerar gränssnittet och är skräpinsamling. Både COM- och .NET-klienter kan göra begäranden på samma hanterade objekt samtidigt.
COM-anropsbara omslutningar är osynliga för andra klasser som körs i .NET-runtime. Deras främsta syfte är att hantera anrop mellan hanterad och ohanterad kod; dock hanterar CCWs även objektidentiteten och objektlivslängden för de hanterade objekt som de omsluter.
Objektidentitet
Körmiljön allokerar minne för .NET-objektet från sin skräpinsamlande heap, vilket gör att körmiljön kan flytta objektet i minnet efter behov. Körmiljön allokerar däremot minne för CCW från en icke-samlad heap, vilket gör det möjligt för COM-klienter att referera till wrappern direkt.
Objektets livslängd
Till skillnad från .NET-klienten som den omsluter, räknas referenserna till CCW på traditionellt COM-sätt. När referensantalet på CCW når noll släpper omslutningen sin referens för det hanterade objektet. Ett hanterat objekt utan återstående referenser samlas in under nästa skräpinsamlingscykel.
Simulera COM-gränssnitt
CCW exponerar alla offentliga, COM-synliga gränssnitt, datatyper och returnerar värden till COM-klienter på ett sätt som är förenligt med COM:s tillämpning av gränssnittsbaserad interaktion. För en COM-klient är anropande metoder på ett .NET-objekt identiskt med att anropa metoder på ett COM-objekt.
För att skapa den här sömlösa metoden tillverkar CCW traditionella COM-gränssnitt, till exempel IUnknown och IDispatch. Som följande bild visar behåller CCW en enda referens för det .NET-objekt som den omsluter. Både COM-klienten och .NET-objektet interagerar med varandra via proxy- och stub-konstruktionen av CCW.
Förutom att exponera de gränssnitt som uttryckligen implementeras av en klass i den hanterade miljön tillhandahåller .NET-körningen implementeringar av COM-gränssnitten som anges i följande tabell för objektets räkning. En .NET-klass kan åsidosätta standardbeteendet genom att tillhandahålla en egen implementering av dessa gränssnitt. Körningen tillhandahåller dock alltid implementeringen för gränssnitten IUnknown och IDispatch.
| Gränssnitt | Beskrivning |
|---|---|
| IDispatch | Tillhandahåller en mekanism för sen bindning till typer. |
| IErrorInfo | Innehåller en textbeskrivning av felet, dess källa, en hjälpfil, hjälpkontext och GUID för gränssnittet som definierade felet (alltid GUID_NULL för .NET-klasser). |
| IProvideClassInfo | Gör att COM-klienter kan få åtkomst till ITypeInfo-gränssnittet som implementeras av en hanterad klass. Returnerar COR_E_NOTSUPPORTED på .NET Core för typer som inte importerats från COM. |
| ISupportErrorInfo | Gör att en COM-klient kan avgöra om det hanterade objektet stöder IErrorInfo-gränssnittet . I så fall kan klienten hämta en pekare till det senaste undantagsobjektet. Alla hanterade typer stöder IErrorInfo-gränssnittet . |
| ITypeInfo (endast .NET Framework) | Innehåller typinformation för en klass som är exakt samma som typinformationen som produceras av Tlbexp.exe. |
| IUnknown | Tillhandahåller standardimplementeringen av IUnknown-gränssnittet med vilken COM-klienten hanterar livscykeln för CCW och möjliggör typomvandling. |
En hanterad klass kan också tillhandahålla COM-gränssnitten som beskrivs i följande tabell.
| Gränssnitt | Beskrivning |
|---|---|
| Klassgränssnittet (_classname) | Ett gränssnitt som exponeras av körningsmiljön och inte uttryckligen definierats, och som kommer åt alla offentliga gränssnitt, metoder, egenskaper och fält som uttryckligen är tillgängliga på hanterade objekt. |
| IConnectionPoint och IConnectionPointContainer | Gränssnitt för objekt som hanterar händelser baserade på delegering (ett gränssnitt för registrering av händelseprenumeranter). |
| IDispatchEx (.NET Framework endast) | Gränssnitt som tillhandahålls av runtime-miljön om klassen implementerar IExpando. IDispatchEx-gränssnittet är en förlängning av IDispatch-gränssnittet som, till skillnad från IDispatch, möjliggör uppräkning, tillägg, borttagning och skiftlägeskänsliga anrop av medlemmar. |
| IEnumVARIANT | Gränssnitt för klasser av samlingstyp, som räknar upp objekten i samlingen om klassen implementerar IEnumerable. |
Introduktion till klassgränssnittet
Klassgränssnittet, som inte uttryckligen definieras i hanterad kod, är ett gränssnitt som exponerar alla offentliga metoder, egenskaper, fält och händelser som uttryckligen exponeras för .NET-objektet. Gränssnittet kan vara antingen ett dubbelgränssnitt eller endast avsett för utsändning. Klassgränssnittet tar emot namnet på själva .NET-klassen, som föregås av ett understreck. För klassen Däggdjur är till exempel klassgränssnittet _Mammal.
För härledda klasser exponerar klassgränssnittet även alla offentliga metoder, egenskaper och fält i basklassen. Den härledda klassen exponerar också ett klassgränssnitt för varje basklass. Om klassen Mammal till exempel utökar klassen MammalSuperclass, som i sig utökar System.Object, exponerar .NET-objektet för COM-klienterna tre klassgränssnitt med namnet _Mammal, _MammalSuperclass och _Object.
Tänk till exempel på följande .NET-klass:
' Applies the ClassInterfaceAttribute to set the interface to dual.
<ClassInterface(ClassInterfaceType.AutoDual)> _
' Implicitly extends System.Object.
Public Class Mammal
Sub Eat()
Sub Breathe()
Sub Sleep()
End Class
// Applies the ClassInterfaceAttribute to set the interface to dual.
[ClassInterface(ClassInterfaceType.AutoDual)]
// Implicitly extends System.Object.
public class Mammal
{
public void Eat() {}
public void Breathe() {}
public void Sleep() {}
}
COM-klienten kan hämta en pekare till ett klassgränssnitt med namnet _Mammal. På .NET Framework kan du använda verktyget Typbiblioteksexportör (Tlbexp.exe) för att generera ett typbibliotek som innehåller gränssnittsdefinitionen _Mammal . Typbiblioteksexportören stöds inte på .NET Core.
Mammal Om klassen implementerade ett eller flera gränssnitt visas gränssnitten under samklassen.
[odl, uuid(…), hidden, dual, nonextensible, oleautomation]
interface _Mammal : IDispatch
{
[id(0x00000000), propget] HRESULT ToString([out, retval] BSTR*
pRetVal);
[id(0x60020001)] HRESULT Equals([in] VARIANT obj, [out, retval]
VARIANT_BOOL* pRetVal);
[id(0x60020002)] HRESULT GetHashCode([out, retval] short* pRetVal);
[id(0x60020003)] HRESULT GetType([out, retval] _Type** pRetVal);
[id(0x6002000d)] HRESULT Eat();
[id(0x6002000e)] HRESULT Breathe();
[id(0x6002000f)] HRESULT Sleep();
}
[uuid(…)]
coclass Mammal
{
[default] interface _Mammal;
}
Det är valfritt att generera klassgränssnittet. Som standard genererar COM interop ett dispatch-only-gränssnitt för varje klass som du exporterar till ett typbibliotek. Du kan förhindra eller ändra det automatiska skapandet av det här gränssnittet genom att tillämpa ClassInterfaceAttribute på klassen. Även om klassgränssnittet kan underlätta uppgiften att exponera hanterade klasser för COM, är dess användning begränsad.
Försiktighet
Att använda klassgränssnittet istället för att uttryckligen definiera ditt eget kan komplicera den framtida versioneringen av din hanterade klass. Läs följande riktlinjer innan du använder klassgränssnittet.
Definiera ett explicit gränssnitt för COM-klienter som ska användas i stället för att generera klassgränssnittet.
Eftersom COM interop genererar klassens gränssnitt automatiskt kan ändringar i klassen efter versionen ändra layouten för det klassgränssnitt som exponeras av Common Language Runtime (CLR). Eftersom COM-klienter vanligtvis är oförberedda för att hantera ändringar i layouten för ett gränssnitt bryts de om du ändrar klassens medlemslayout.
Den här riktlinjen förstärker uppfattningen att gränssnitt som exponeras för COM-klienter måste förbli oföränderliga. Om du vill minska risken för att com-klienter bryts genom att oavsiktligt ändra ordning på gränssnittslayouten isolerar du alla ändringar i klassen från gränssnittslayouten genom att uttryckligen definiera gränssnitt.
Använd ClassInterfaceAttribute för att koppla från den automatiska genereringen av klassgränssnittet och implementera ett explicit gränssnitt för klassen, som följande kodfragment visar:
<ClassInterface(ClassInterfaceType.None)>Public Class LoanApp
Implements IExplicit
Sub M() Implements IExplicit.M
…
End Class
[ClassInterface(ClassInterfaceType.None)]
public class LoanApp : IExplicit
{
int IExplicit.M() { return 0; }
}
Värdet ClassInterfaceType.None förhindrar att klassgränssnittet genereras när klassmetadata exporteras till ett typbibliotek. I föregående exempel kan COM-klienter endast komma åt LoanApp klassen via IExplicit gränssnittet.
Undvik cachelagring av sändningsidentifierare (DispIds)
Att använda klassgränssnittet är ett acceptabelt alternativ för skriptklienter, Microsoft Visual Basic 6.0-klienter eller en sen bunden klient som inte cachelagrar DispId för gränssnittsmedlemmar. DispId identifierar gränssnittsmedlemmar för att aktivera sen bindning.
För klassgränssnittet baseras genereringen av DispIds på medlemmens position i gränssnittet. Om du ändrar ordningen på medlemmen och exporterar klassen till ett typbibliotek ändrar du dispId:erna som genereras i klassgränssnittet.
Om du vill undvika att bryta sent bundna COM-klienter när du använder klassgränssnittet använder du ClassInterfaceAttribute med värdet ClassInterfaceType.AutoDispatch . Det här värdet implementerar ett klassgränssnitt för endast dispatch, men utelämnar gränssnittsbeskrivningen från typbiblioteket. Utan en gränssnittsbeskrivning kan klienterna inte cachelagra DispIds vid kompileringstillfället. Även om det här är standardgränssnittstypen för klassgränssnittet kan du uttryckligen använda attributvärdet.
<ClassInterface(ClassInterfaceType.AutoDispatch)> Public Class LoanApp
Implements IAnother
Sub M() Implements IAnother.M
…
End Class
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class LoanApp
{
public int M() { return 0; }
}
För att hämta en DispId för en gränssnittsmedlem vid körning kan COM-klienter anropa IDispatch.GetIdsOfNames. Om du vill anropa en metod i gränssnittet skickar du det returnerade DispId som ett argument till IDispatch.Invoke.
Begränsa användningen av alternativet med dubbla gränssnitt för klassgränssnittet.
Dubbla gränssnitt möjliggör tidig och sen bindning till gränssnittsmedlemmar från COM-klienter. Vid designtillfället och under testningen kan det vara bra att ställa in klassgränssnittet på dubbla. För en hanterad klass (och dess basklasser) som aldrig kommer att ändras är det här alternativet också acceptabelt. I alla andra fall bör du undvika att ställa in klassgränssnittet på dubbla.
Ett automatiskt genererat dubbelgränssnitt kan vara lämpligt i sällsynta fall. Men oftare skapar det versionsrelaterad komplexitet. Till exempel kan COM-klienter som använder klassgränssnittet för en härledd klass enkelt bryta med ändringar i basklassen. När en tredje part tillhandahåller basklassen är layouten för klassgränssnittet utom din kontroll. Till skillnad från ett dispatch-only-gränssnitt innehåller dessutom ett dubbelt gränssnitt (ClassInterfaceType.AutoDual) en beskrivning av klassgränssnittet i det exporterade typbiblioteket. En sådan beskrivning uppmuntrar klienter med sen bindning att cachelagra DispIds vid kompileringstillfället.
Se till att alla COM-händelsemeddelanden är sena.
Som standard bäddas COM-typinformation in direkt i hanterade sammansättningar, vilket eliminerar behovet av primära interop-sammansättningar (PIA). En av begränsningarna med inbäddad typinformation är dock att den inte stöder leverans av COM-händelsemeddelanden via tidiga vtable-anrop, utan endast stöder sena IDispatch::Invoke anrop.
Om programmet kräver tidiga anrop till COM-händelsegränssnittsmetoder kan du ange egenskapen Embed Interop Types i Visual Studio till trueeller inkludera följande element i projektfilen:
<EmbedInteropTypes>True</EmbedInteropTypes>