Introduction
This article describes a helper library for performing late binding calls to an instance of a type using the .NET framework. Additionaly as Automation calls (former OLE Automation) are encapsulated under the .NET Framework by the Late Binding system, this library also simplifies interprocess communication using Automation on applications which supports it, e.g: Microsoft Office suite. As the C# language is still far away from the model available for VB.NET, the intention is to provide a simpler model for that language, although the library can be consumed by any .NET language.
With C#4.0 the dynamic keyword will be added so -with time- this library will be deprecated. But C# 4.0 isn't out yet, and even when it comes out, there will be still a lot of projects using previous versions of the C# language which could benefit from this library.
Finally the interface for the library has completly changed. Now it's designed as a fluent interfece, which really improves both usability and code readability. [^]
Background
Late Binding consists in performing the name binding for a method call or field access at runtime instead of at compile time. The later is called -predictably- early binding. An example of early binding could be many of the method calls we can see in C# every day:
System.IO.StreamReader sr = new System.IO.StreamReader(".//myFile.txt");
string fileContent= sr.ReadToEnd();
Early binding allows compile-time optimization and type-checks, among other things, but in order to do that, we must provide concrete references to the types used. In the example above, a reference to mscorlib.dll assembly, which contains the data for the type System.IO.StreamReader
, must be provided to the C# compiler.
Note that in a strict definition of early binding, a virtual / abstract
method is a late binding call, as the resolution of the real method called for a virtual method depends on the type of the object, which is evaluated at runtime. This is called Dynamic Binding [^], and it will not be treated as Late binding in this article.
Late Binding swaps the security and optimization of early binding for a more wild approach: we just take a generic object and command it to perform an operation with a concrete signature. If the object happens to expose an operation which exactly matches the signature then all will be ok and the CLR will perform the operation at runtime.
But, what if the object does not expose that operation? In that case, bad things will happen:
Using the Library
The source includes a Visual Studio 2005 solution (which can safely be converted to a 2008 one) containing three projects:
- A project for the library itself
- A project a simple type for testing
- A project with Unit Test (NUnit based), which also serves as code sample
The external libraries needed to compile the code (NUnit) are also included: just download/checkout and build!
All late binding functionality is based on the interface LateBindingHelper.IOperationInvoker
Acquisition of an IOperationInvoker
interface is done using a Factory: LateBindingHelper.BindingFactory
which allows creation of an IOperationInvoker
binded to an object instance. The factory provides various possibilities for instanciating an IOperationInvoker
:
- Using an instance of an object as a "prototype", so the
IOperationInvoker
instance is binded to that particular object instance
- Using a
System.Type
to create a new instance of the Type at runtime (optionally arguments for the constructor can be also specified)
- Using a pair of strings defining both the Assembly and the Type of the object we want to instanciate.
- Using a string which defines an specific application ProcID
MyClass myClassInstance = new MyClass();
IOperationInvoker lbf1 = BindingFactory.CreateObjectBinding( myClassInstance );
IOperationInvoker lbf2 = BindingFactory.CreateObjectBinding( typeof(MyClass) );
int integerArgument = 2008;
IOperationInvoker lbf3 = BindingFactory.CreateObjectBinding( typeof(MyClass),
integerArgument );
BindingFactory.CreateAutomationBinding()
factory makes a binding to an Automation application given its Running Object Table (ROT) name or ProcID:
IOperationInvoker word = BindingFactory.CreateAutomationBinding("word.application");
Once we have an instance of an IOperationInvoker
interface, we can send any operation we want to the object using the interface, and the operation will be issued at runtime
Here are some code examples showing the different operations we can perform:
Calling a Method
myLBInvoker.Method ("Method1" ).Invoke();
int arg1 = 1;
double arg2 = 2.3;
myLBInvoker.Method( "Method2" )
.AddParameter(arg1)
.AddParameter(arg2)
.Invoke();
int result;
result = myLBInvoker.Method( "Method3" )
.Invoke<int>();
result = (int)myLBInvoker.Method( "Method3" )
.Invoke<object>();
int result;
int arg1 = 1;
double arg2 = 2.3;
result = myLBInvoker.Method( "Method2" )
.AddParameter(arg1)
.AddParameter(arg2))
.Invoke<int>();
result = (int)myLBInvoker.Method( "Method2" )
.AddParameter(arg1)
.AddParameter(arg2))
.Invoke<object>();
Calling a Method with Reference Parameters
int refValue = 10;
double notRefParam1 = 20;
bool notRefParam2 = true;
myLBInvoker.Method("MultiplyByFive")
.AddParameter(notRefParam1)
.AddParameter(notRefParam2)
.AddRefParameter(refValue)
.Invoke();
refValue = (int)myLBInvoker.LastCallParameters[2];
Accessing Properties or Fields
bool boolResult;
boolResult = myLBInvoker.Property(("Property1").Get<bool>();
boolResult = (bool)myLBInvoker.Property("Property1").Get<object>();
int intResult;
intResult = myLBInvoker.Field("field1").Get<int>();
intResult = (int)myLBInvoker.Field("field1").Get<object>();
myLBInvoker.Property("Property1").Set(true);
myLBInvoker.Field("field1").Set(10);
Indexing
string result;
result = myLBInvoker.Index(5).Get<string>();
myLBInvoker.Index(4).Set("[myNewValue]");
result = myLBInvoker.Index("myStringIndexer").Get<string>();
myLBInvoker.Index("anotherStringIndexer").Set("[myNewValue]");
One More Thing: IOperationInvoker as Return Type
You can use IOperationInvoker
as a return type for an operation, and if you do so, you'll receive an working instance of an object defining the IOperationInvoker
interface. That means that you can concatenate late binding call on different objects:
IOperationInvoker yetAnotherOrdersManager = BindingFactory.CreateBinding(
"MyOrdersManager");
int firstOrderID = yetAnotherOrdersManager
.Property("Orders")
.Get<IOperationInvoker>()
.Index(0)
.Get<IOperationInvoker>()
.Field("orderID")
.Get<int>();
Word Automation Example
A test -disabled by default- is included as an example to create a binding to Microsoft Word and control the application with the library using Automation. The code is also listed here:
IOperationInvoker wordApp = BindingFactory.CreateAutomationBinding("Word.Application");
IOperationInvoker document = wordApp.Property("Documents").Get<IOperationInvoker>();
document.Method("Add").Invoke();
IOperationInvoker selection = wordApp.Property("Selection").Get<IOperationInvoker>();
string str = "Hello World!";
wordApp.Property("Visible").Set(true);
selection.Method("BoldRun").Invoke();
selection.Property("Font").Get<IOperationInvoker>().Property("Size").Set(20);
foreach (char c in str)
{
selection.Method("TypeText").AddParameter(c.ToString()).Invoke();
System.Threading.Thread.Sleep(200);
}
bool visibility = wordApp.Property("Visible").Get<bool>();
visibility = !visibility;
wordApp.Property("Visible").Set(visibility);
wordApp.Method("Quit").AddParameter(0).Invoke();
Related Articles & Bibliography
History
- January 2009 -- Updated article to reflect changes in the interface
- September 2008 -- Initial version
Acknowledgements