System.__ComObject and casting to strongly typed RCWs


Summary This topic describes the System.__ComObject type and some issues related to it including why casting a variable sometimes fails when it appears it should succeed and creating the AppRef class in .NET.

In this topic


Types and runtime callable wrappers

In .NET, each class, interface, enumeration, and so on, is described by its type. The Type class, which is part of the .NET Framework, holds information about the data and function members of a data type. When you create a new Component Object Model (COM) object in .NET via interop, you get a reference to your object that is wrapped in a strongly typed runtime callable wrapper (RCW). An RCW can hold a reference to a COM object inside a .NET application.
 
In the following code example, a variable called sym is declared as the ISimpleMarkerSymbol interface type and is set to a new SimpleMarkerSymbolClass. The type of the variable sym is retrieved and written to the debug window. If you were to run this code, you would find that the sym type is SimpleMarkerSymbolClass, as you might expect; the variable holds a reference to the ISimpleMarkerSymbol interface of the SimpleMarkerSymbolClass RCW.
 

[C#]
ESRI.ArcGIS.Display.ISimpleMarkerSymbol sym = new
  ESRI.ArcGIS.Display.SimpleMarkerSymbolClass();
Debug.WriteLine(sym.GetType().FullName);

[VB.NET]
Dim sym As ESRI.ArcGIS.Display.ISimpleMarkerSymbol = New ESRI.ArcGIS.Display.SimpleMarkerSymbolClass
Debug.WriteLine(CType(sym, Object).GetType.FullName)
In a different coding situation, you could get a reference to an RCW from another property or method. For example, in the similar code that follows, the Symbol property of a renderer (ISimpleRenderer interface) is retrieved, where the renderer uses a single SimpleMarkerSymbol to draw.
 

[C#]
ESRI.ArcGIS.Display.ISimpleMarkerSymbol sym = rend.Symbol as
  ESRI.ArcGIS.Display.ISimpleMarkerSymbol;
Debug.WriteLine(sym.GetType().FullName);

[VB.NET]
Dim sym As ESRI.ArcGIS.Display.ISimpleMarkerSymbol = rend.Symbol
Debug.WriteLine(CType(sym, Object).GetType.FullName)
Although you might expect to get the same output as before, you will actually find that the reported type of sym is System.__ComObject.
 

The System.__ComObject type

The difference between the two previous excerpts of code is that, in the first, you create the symbol using the New (or new) keyword and the type SimpleMarkerSymbolClass. When the code is compiled, the exact type of the variable is discovered by the compiler using Reflection, and metadata about that type is stored in the compiled code. When the code runs, the runtime then has all the information (the metadata) that describes the exact type of the variable.
 
However, in the second example, you set the sym variable from the Symbol property of the ISimpleRenderer interface. When this code is compiled, the only metadata that the compiler can find is that the Symbol property returns an ISymbol reference; the type of the actual class of object cannot be discovered. Although you can perform a cast to get the ISimpleMarkerSymbol interface of the sym variable (or any other interface that the symbol implements), the .NET runtime does not have the metadata required at run time to discover exactly what the type of the variable is. In this case, when you access the Symbol property, the .NET runtime wraps the COM object reference in a generic RCW called System.__ComObject. This is a class internal to the .NET Framework that can be used to hold a reference to any kind of COM object; its purpose is to act as the RCW for an unknown type of COM object.
 

Casting

In the second example, even if you know the exact type of class to which you have a reference, the .NET runtime still does not have the metadata required to cast the variable to a strongly typed RCW. This is shown in the following code example, as attempting a cast to the SimpleMarkerSymbolClass type would fail:
 

[C#]
// The following line would result in sym2 being null, as the cast would fail.
ESRI.ArcGIS.Display.SimpleMarkerSymbolClass sym2 = sym as
  ESRI.ArcGIS.Display.SimpleMarkerSymbolClass;

[VB.NET]
' The following line would result in a runtime error, as the implicit cast would fail.
Dim sym2 As ESRI.ArcGIS.Display.SimpleMarkerSymbol = sym
However, because the System.__ComObject class is specifically designed to work with COM objects, it can always perform a query interface (QI) to any COM interfaces that are implemented by an object. Therefore, casting to specific interfaces (as long as they are implemented on the object) will be successful. See the following code example:
 

[C#]
ESRI.ArcGIS.Display.ISimpleMarkerSymbol sym3 = sym as
  ESRI.ArcGIS.Display.ISimpleMarkerSymbol;

[VB.NET]
Dim sym3 As ESRI.ArcGIS.Display.ISimpleMarkerSymbol = sym

Singletons and System.__ComObject

In the previous examples, a strongly typed RCW is created when you instantiate the COM object by using the new keyword, whereas if the object is preexisting, the type of the RCW is the generic System.__ComObject. Sometimes when you use the new keyword to instantiate a COM object, you are actually getting a reference to an object that already exists; this happens when attempting to instantiate a singleton class that has previously been instantiated.
 
The .NET Framework is unable to wrap in a strongly typed RCW an instance of an object that has previously been wrapped in the generic System.__ComObject RCW. If your code has encountered such a situation, you may receive an error such as  "Unable to cast object of type System.__ComObject to type <Typename>." See the following code example:
 

[C#]
ESRI.ArcGIS.Display.IStyleGallery sg = new
  ESRI.ArcGIS.Framework.StyleGalleryClass();

[VB.NET]
Dim sg As ESRI.ArcGIS.Display.IStyleGallery = = New ESRI.ArcGIS.Framework.StyleGalleryClass
This error can occur even though you have declared your variable using the interface name rather than the class name, as in the previous example. The problem occurs because, when your code instantiates an object, the .NET runtime first attempts to wrap the object in the strongly typed class type (the type stated after the new keyword) before attempting a cast to the interface type. The cast to the strongly typed RCW cannot succeed because the COM object has previously been wrapped in the generic System.__ComObject wrapper. This can occur in situations beyond your control. For example, other ArcObjects tools written in .NET from third parties may wrap an object in the generic wrapper, causing your code to fail.
 
The solution is to use the Activator class (shown as follows) to safely wrap singleton objects in a strongly typed RCW when you first get a reference to them. Additionally, you should generally always declare variables holding RCWs using an interface rather than a class Type.
 

Using the Activator class to create singletons

If you use the CreateInstance method of the Activator class instead of the new keyword to instantiate singletons, you can avoid such errors, as Activator is able to get the required metadata to perform the cast. See the following code example:
 

[C#]
Type t = Type.GetTypeFromProgID("esriFramework.StyleGallery");
System.Object obj = Activator.CreateInstance(t);
IStyleGallery sg = obj as IStyleGallery;

[VB.NET]
Dim t As Type = Type.GetTypeFromProgID("esriFramework.StyleGallery")
Dim obj As System.Object = Activator.CreateInstance(t)
Dim pApp As ESRI.ArcGIS.Display.IStyleGallery = obj
You can use this technique to instantiate the AppRef class; however, the AppRef class can only be created within an ArcGIS application. (The type is the generic System.__ComObject RCW.) See the following code example:
 

[C#]
Type t = Type.GetTypeFromProgID("esriFramework.AppRef");
System.Object obj = Activator.CreateInstance(t);
ESRI.ArcGIS.Framework.IApplication pApp = obj as
  ESRI.ArcGIS.Framework.IApplication;

[VB.NET]
Dim t As Type = Type.GetTypeFromProgID("esriFramework.AppRef")
Dim obj As System.Object = Activator.CreateInstance(t)
Dim pApp As ESRI.ArcGIS.Framework.IApplication = obj
For more information about RCWs and interop, refer to the book by Adam Nathan, .NET and COM–The Complete Interoperability Guide, Sams Publishing, 2002.


See Also:

How to Release COM References
Singleton objects