Using Late Bound COM Objects - Late Binding in .NET: With a Runtime Callable Wrapper Available
(Page 4 of 6 )
Often, if you have a RCW you would be much better off simply doing an early bind to the object. If you're doing a late bind in this scenario, you are generally attempting to protect yourself from a third party who has made COM programming errors - either allowing the GUID to change when the version hasn't, or incorrectly handling versioning of the interfaces.
In such a scenario you have two options. The most desirable option is to regenerate an RCW and recompile your project to it. If, however, you have reason to believe that the object (and its RCW) may be switched out post-install, you may need to bind to the RCW's name and its underlying object at runtime. There are two ways of coding this requirement, with either the Activator or the Assembly object. Let's start with the Activator:
Late Binding to an RCW via the Activator As you read the code you'll notice we used the method CreateComInstanceFrom, and followed it with an Unwrap call. Activator has three versions of "Creates": CreateInstance, CreateInstanceFrom, and CreateComInstanceFrom. Each of these methods has several overloads. In each case the overloads not taking a Type parameter will return ObjectHandle instead of an object. To retrieve the underlying object we add an Unwrap call to the create method:
// load the object via the assembly and type name
// add the UnWrap() call to get the underlying base object
// NOTICE that the class name is now "TestClassClass" instead of
// "TestClass" - this is the default from the VS tlbimp output.
object myObject =Activator.CreateComInstanceFrom("Interop.TestObject.dll",
"TestObject.TestClassClass").Unwrap();
// this would also have worked, though the quality of exceptions thrown
// is not as informative
//object myObject =Activator.CreateInstanceFrom("Interop.TestObject.dll",
// "TestObject.TestClassClass").Unwrap();
// from the unwrapped object, get the type definition
Type myType = myObject.GetType();
// set up the parameters and invoke the methods
// from this point forward, the code is the same as the previous example Contrary to what one might expect, the second parameter for the "Create" in this scenario must be "TestObject.TestClassClass " instead of "TestObject.TestClass." This naming convention is the output when the assembly is generated via Visual Studio .NET. To be sure of the class name you can (temporarily) reference the RCW into your project and view it in the Object Browser:
Late Binding to an RCW via the Assembly The alternative coding for the same scenario uses the Assembly object:
Here we show using the Assembly object's LoadFrom method to load the assembly. Once the assembly is loaded, we call its CreateInstance method to return the needed object. Unlike the Activator versions, Assembly's CreateInstance overloads all require the assembly file parameter and none return an ObjectHandle.
// load the assembly
Assembly myAsm;
myAsm = Assembly.LoadFrom("Interop.TestObject.dll");
// from assembly, get the target object
object myObject = myAsm.CreateInstance("TestObject.TestClassClass");
// from assembly, get the type definition
Type myType = myAsm.GetType("TestObject.TestClassClass");
// this would have worked too
//Type myType = myObject.GetType();
// set up the parameters and invoke the methods
// from this point forward, the code is the same as the previous example Late Binding in .NET: When You Need a Non-Default Interface Veteran COM programmers are familiar with the ability of a COM object to implement multiple interfaces. When late-binding to a COM server, you will sometimes need to call a method from a non-default interface. In this case, we are only going to late bind to the COM object. The interface is referenced into the project via normal methods, including generation of an RCW. This example uses a COM object "Process1Impl.Processor1Impl " that implements the "Process.Processor " interface. The project code is available in this article's download material.
If you've read the earlier examples, much of this code will be familiar. We acquire the object's default type definition from its ProgID and use Activator to create an instance of that type. A variable is declared of the interface's type, and we cast the object to the interface. Interface method calls are then made as normal - no InvokeMethod syntax is required.
// get the implementing COM object
Type oType = Type.GetTypeFromProgID("Process1Impl.Processor1Impl");
object oTemp = Activator.CreateInstance(oType);
// cast it to the generic interface
Process.Processor myProc = (Process.Processor) oTemp;
// call the interface's method
string myOut = myProc.Process("ThisParam","ThisAction");
txtEcho1.Text=myOut;Next: Late Binding in .NET: Monitoring Performance of the Various Invocation Methods >>
More C# Articles
More By Wrox Team