Click here to Skip to main content
Click here to Skip to main content

Late Binding Helper Library

, 21 Jan 2009
Rate this:
Please Sign up or sign in to vote.
Simple library which aims to simplify late binding calls with C#

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");

//early binding call
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:

exception.png

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();

//Late Binding calls will be dispatched to the myClassInstance object
IOperationInvoker lbf1 = BindingFactory.CreateObjectBinding( myClassInstance );

//Creates a new MyClass object instance where the late binding call will be dispatched 
//using the default constructor
IOperationInvoker lbf2 = BindingFactory.CreateObjectBinding( typeof(MyClass) );

int integerArgument = 2008;
//Creates a new MyClass object instance where the late binding call will be
//dispatched passing arguments to the constructor
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

//Method call without parameters and no return value
myLBInvoker.Method ("Method1" ).Invoke();
	

//Method call with parameters and no return value (or we don't need the return value)
int arg1 = 1;
double arg2 = 2.3;
myLBInvoker.Method( "Method2" )
	.AddParameter(arg1)
	.AddParameter(arg2)
	.Invoke();

//Method call without parameters and a return value of type int
int result;
result = myLBInvoker.Method( "Method3" )
		.Invoke<int>();

//Or with explicit casting			
result = (int)myLBInvoker.Method( "Method3" )
		.Invoke<object>();		

//Method call with parameters and a return value of type int
int result;
int arg1 = 1;
double arg2 = 2.3;
result = myLBInvoker.Method( "Method2" )
		.AddParameter(arg1)
		.AddParameter(arg2))
		.Invoke<int>();

//Or with explicit casting			
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;

//Make the call. Args.ByRefIndexs indicates the index number of the arguments
//which are passed as reference
myLBInvoker.Method("MultiplyByFive")
	.AddParameter(notRefParam1)
	.AddParameter(notRefParam2)
	.AddRefParameter(refValue)
	.Invoke();

//Recover the new value for the parameter passed by reference
refValue = (int)myLBInvoker.LastCallParameters[2];

Accessing Properties or Fields

//Get property value
bool boolResult;
boolResult = myLBInvoker.Property(("Property1").Get<bool>();

//With explicit cast
boolResult = (bool)myLBInvoker.Property("Property1").Get<object>();

//Get field value
int intResult;
intResult = myLBInvoker.Field("field1").Get<int>();
			
//With explicit cast
intResult = (int)myLBInvoker.Field("field1").Get<object>();

//Set property value
myLBInvoker.Property("Property1").Set(true);

//Set field value
myLBInvoker.Field("field1").Set(10);

Indexing

//Get object at index position
string result;
result = myLBInvoker.Index(5).Get<string>();
			
//Set value for an index position
myLBInvoker.Index(4).Set("[myNewValue]");
	
//Works with any object as indexer:
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")
          //Here we return a list of orders
          .Get<IOperationInvoker>()
             .Index(0)                    
             //We access the first order... hoping we have at least one order
             //stored so no nasty exception is throw.
             .Get<IOperationInvoker>()
                .Field("orderID") 
                //Access orderID field and save it.        
                .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");

//Get Word objects to interop operations
IOperationInvoker document = wordApp.Property("Documents").Get<IOperationInvoker>();

document.Method("Add").Invoke();
IOperationInvoker selection = wordApp.Property("Selection").Get<IOperationInvoker>();

string str = "Hello World!";

//Make workd visible
wordApp.Property("Visible").Set(true);

//Activate bold
selection.Method("BoldRun").Invoke();

//Change font size
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);
}

//Hide Word
bool visibility = wordApp.Property("Visible").Get<bool>();
visibility = !visibility;
wordApp.Property("Visible").Set(visibility);

//We need to get the type of the enumeration, that breaks the Late Binding
//approach as we need a way to know a specific type, and it's aseembly
//Type enumType = Assembly
//      .LoadWithPartialName("Microsoft.Office.Interop.Word")
//      .GetType("Microsoft.Office.Interop.Word.WdSaveOptions", false);
//word.Call("Quit", Activator.CreateInstance(enumType)));

//But if we know the equivalent int to the enum value it will work
//WdSaveOptions.wdDoNotSaveChanges == 0

//Quit
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

License

This article, along with any associated source code and files, is licensed under The BSD License

Share

About the Author

Ricardo Amores Hernández
Software Developer (Junior)
Spain Spain
No Biography provided

Comments and Discussions

 
GeneralThe Helper Library and the Word.Documents Collection Pinmemberbbeard335-Nov-10 8:37 
GeneralRe: The Helper Library and the Word.Documents Collection PinmemberRicardo Amores Hernández5-Nov-10 10:29 
GeneralRe: The Helper Library and the Word.Documents Collection Pinmemberbbeard338-Nov-10 10:14 
GeneralVery Usefull Pinmembermattskelton28-Jul-09 5:38 
GeneralI Don't get it Pinmembertonyt21-Jan-09 22:32 
GeneralRe: I Don't get it PinmemberRicardo Amores Hernández21-Jan-09 23:08 
GeneralQuestion PinmemberMario_F27-Sep-08 13:05 
GeneralRe: Question PinmemberRicardo Amores Hernández28-Sep-08 9:26 
GeneralRe: Question PinmemberMario_F28-Sep-08 10:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140821.2 | Last Updated 21 Jan 2009
Article Copyright 2008 by Ricardo Amores Hernández
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid