In this topic
About Interoperating with COM
Code running under the control of the .NET Framework is called managed code; conversely, code executing outside the .NET Framework is called unmanaged code. Component Object Model (COM) is one example of unmanaged code. The .NET Framework interacts with COM via a technology known as COM Interop.
For COM Interop to work, the Common Language Runtime (CLR) requires metadata for all the COM types. This means that the COM type definitions normally stored in the type libraries need to be converted to .NET metadata. This is easily accomplished with the Type Library Importer utility (tlbimp.exe) that ships with the .NET Framework Software Developer Kit (SDK). This utility generates interop assemblies containing the metadata for all the COM definitions in a type library. Once metadata is available, .NET clients can seamlessly create instances of COM types and call its methods as though they were native .NET instances.
Primary interop assemblies
Primary interop assemblies (PIAs) are the official, vendor-supplied .NET type definitions for interoperating with underlying COM types. PIAs are strongly named by the COM library publisher to guarantee uniqueness.
ESRI provides primary interop assemblies for all the ArcObjects type libraries that are implemented with COM. ArcGIS .NET developers should only use the PIAs that are installed in the Global Assembly Cache (GAC) during install if version 1.1 of the .NET Framework is detected. ESRI only supports the interop assemblies that ship with ArcGIS. You can identify a valid ESRI assembly by its public key (8FC3CC631E44AD86).
The .NET runtime provides wrapper classes to make both managed and unmanaged clients believe they are communicating with objects within their respective environment. When managed clients call a method on a COM object, the runtime creates a runtime-callable wrapper (RCW) that handles the marshaling between the two environments. Similarly, the .NET runtime creates COM-callable wrappers (CCWs) for the reverse case, in which COM clients communicate with .NET components. The following illustration outlines this process:
Exposing .NET objects to COM
When creating .NET components for COM clients to use, observe the following guidelines to ensure interoperability:
- Avoid using parameterized constructors.
- Avoid using static methods.
- Define event source interfaces in managed code.
- Include HRESULTs in user-defined exceptions.
- Supply globally unique identifiers (GUIDs) for types that require them.
- Expect inheritance differences.
Performance considerations
COM Interop adds a new layer of overhead to applications, but the overall cost of interoperating between COM and .NET is small and often unnoticeable. However, the cost of creating wrappers and having them marshal between environments does add up. If you suspect COM Interop is the bottleneck in your application's performance, try creating a COM worker class that wraps all the chatty COM calls into one function that managed code can invoke. This improves performance by limiting the marshaling between the two environments. For more information, see
Performance of ArcObjects in .NET.
COM to .NET type conversion
Generally speaking, the type library importer imports types with the name they originally had in COM. All imported types are also added to a namespace that has the following naming convention: ESRI.ArcGIS.<name of the library>. For example, the namespace for the Geometry library is ESRI.ArcGIS.Geometry. All types are identified by their complete namespace and type name.
Classes, interfaces, and members
All COM coclasses are converted to managed classes; the managed classes have the same name as the original with "Class" appended. For example, the runtime-callable wrapper for the Point coclass is called PointClass.
All classes also have an interface with the same name as the coclass that corresponds to the default interface for the coclass. For example, the PointClass has a Point interface. The type library importer adds this interface so clients can register as event sinks. See the following
Class interfaces section for more information.
The .NET classes also have class members that .NET supports, but COM does not. Each member of each interface the class implements is added as a class member. Any property or method a class implements can be accessed directly from the class rather than having to cast to a specific interface. Since interface member names are not unique, name conflicts are resolved by adding the interface name and an underscore as a prefix to the name of each conflicting member. When member names conflict, the first interface listed with the coclass remains unchanged. See the following code example:
[C#]
ESRI.ArcGIS.Geometry.PointClass thePt = new ESRI.ArcGIS.Geometry.PointClass();
thePt.PutCoords(10.0F,8.0F);
ESRI.ArcGIS.Geometry.IGeometry geom = thePt.Buffer(2.0F);
MessageBox.Show(geom.Dimension.ToString());
[VB.NET]
Dim thePoint As New PointClass
thePoint.PutCoords(10.0, 11.9)
Dim geom As IGeometry = thePoint.Buffer(2.0F)
MessageBox.Show(geom.Dimension.ToString())
Since interface member names are not unique, name conflicts are resolved by prefixing the interface name and an underscore to the name of each conflicting member. When member names conflict, the first interface listed with the coclass remains unchanged. For example, the MapClass has members called AreaOfInterest and IBasicMap_AreaOfInterest.
Properties in C# that have by-reference or multiple parameters are not supported with the regular property syntax. In these cases, it is necessary to use the accessory methods instead. See the following code example:
[C#]
ILayer layer = mapControl.get_Layer(0);
MessageBox.Show(layer.Name);
The type library importer creates several types that enable managed applications to sink to events fired by COM classes. The first type is a delegate named after the event interface plus an underscore followed by the event name, then the word EventHandler. For example, the SelectionChanged event defined on the IActiveViewEvents interface has the following delegate defined: IActiveViewEvents_SelectionChangedEventHandler. The importer also creates an event interface with an "_Event" suffix added to the original interface name. For example, IActiveViewEvents generates IActiveViewEvents_Event. Use the event interfaces to set up event sinks.
Non-OLE automation-compliant types
COM types that are not OLE automation compliant generally do not work in .NET. ArcGIS contains a few noncompliant methods, and these cannot be used in .NET. However, in most cases, supplemental interfaces have been added that have the offending members rewritten compliantly. For example, when defining an envelope via a point array, you can’t use IEnvelope.DefineFromPoints; instead, you must use IEnvelopeGEN.DefineFromPoints. See the following code example:
[VB.NET]
Dim pointArray(1) As IPoint
pointArray(0) = New PointClass
pointArray(1) = New PointClass
pointArray(0).PutCoords(0, 0)
pointArray(1).PutCoords(100, 100)
Dim env As IEnvelope
Dim envGEN As IEnvelopeGEN
env = New EnvelopeClass
envGEN = New EnvelopeClass
'Won't compile.
env.DefineFromPoints(2, pointArray)
'Doesn't work.
env.DefineFromPoints(2, pointArray(0))
'Works.
envGEN.DefineFromPoints(pointArray)
[C#]
IPoint[] pointArray = new IPoint[2];
pointArray[0] = new PointClass();
pointArray[1] = new PointClass();
pointArray[0].PutCoords(0,0);
pointArray[1].PutCoords(100,100);
IEnvelope env = new EnvelopeClass();
IEnvelopeGEN envGEN = new EnvelopeClass();
//Won't compile.
env.DefineFromPoints(3, ref pointArray);
//Doesn't work.
env.DefineFromPoints(3, ref pointArray[0]);
//Works.
envGEN.DefineFromPoints(ref pointArray);
Class interfaces are created to help Visual Basic programmers transition to .NET; they are also commonly used in code produced by the Visual Basic .NET Upgrade wizard or the code snippet converter in Visual Studio .NET.
However, it is recommended that you avoid using the class interfaces in the ESRI interop assemblies, as they may change in future versions of ArcGIS. This section explains a little more about class interfaces.
In Visual Basic 6, the details of default interfaces were hidden from the user, and a programmer could instantiate a variable and access the members of its default interface without performing a specific query interface (QI) for that interface. For example, the following Visual Basic 6 code instantiates the StdFont class and sets a variable equal to the default interface (IStdFont) of that class:
[Visual Basic 6.0]
Dim fnt As New Stdole.StdFont
However, .NET does not provide this same ability. To allow Visual Basic developers a more seamless introduction to .NET, the type library importer in .NET adds "class interfaces" to each interop assembly, allowing COM objects to be used with this same syntax inside .NET. When an object library is imported, a class interface RCW is created for each COM class; the name of the class interface is the same as the COM class, for example, Envelope. All the members of the default interface of the COM class are added to this class interface. If the COM class has a source interface (is the source of events), then the class interface will also include all the events of this interface, which helps a programmer to link up events.
A second RCW is created that represents the underlying COM class; the name of this is the same as the COM class with a suffix of "Class," for example, EnvelopeClass. The class interface is linked to the class by an attribute that indicates the class to which it belongs. This attribute is recognized by the .NET compilers, which allow a programmer to instantiate a class by using its class interface.
The exception is classes that have a default interface of IUnknown or IDispatch, which are never exposed on RCW classes because the members are called internally by the .NET Framework runtime. In this case, the next implemented interface is exposed on the class interface instead. As most ArcObjects define IUnknown as their default interface, this affects most ArcObjects classes. For example, the Point COM class in the esriGeometry object library lists the IPoint interface as its first implemented interface. In .NET, this class is accessed by using the Point class interface, which inherits the IPoint interface, and the PointClass class.
The following code example shows that by declaring a variable type as a Point class interface, that variable can be used to access the IPoint.PutCoords method from this class interface:
[C#]
ESRI.ArcGIS.Geometry.Point thePt = new ESRI.ArcGIS.Geometry.Point();
thePt.PutCoords(10,8);
[VB.NET]
Dim thePt As ESRI.ArcGIS.Geometry.Point = New ESRI.ArcGIS.Geometry.Point()
thePt.PutCoords(10,8)
The inherited interface of a class interface is not guaranteed to remain the same between versions of ArcGIS; therefore, it is recommended that you avoid using the previous syntax. You can view these types in the Visual Basic .NET Object Browser as shown in the following screen shot. When using Visual Basic .NET, PointClass is not shown by default but can be made visible by selecting the Show Hidden Members option.
In the C# Object Browser, you can see more clearly the class interface Point, its inherited interface IPoint, and the class PointClass as shown in the following screen shot:
See Also:
Binary compatibility