Click here to Skip to main content
11,432,627 members (62,549 online)
Click here to Skip to main content

How the new C# dynamic type can simplify access to a late bound COM object

, 21 Jan 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
Explains how the new dynamic type can simplify access to late bound COM objects.

Introduction

A couple of years ago, I got an interesting task. I had to develop a public Web Service which internally utilized a third-party COM object. Sounds easy, the concept of COM Interop exists in .NET since the first release, so I didn't expect any difficulties on my way.

Just to be more specific, let's say we have a very simple VB6 COM object "MyProject.MyClass" with a single method Add, taking two Integer parameters and returning the sum of them:

Public Function Add(first As Integer, second As Integer) As Integer

  Add = first + second

End Function

I started a new Visual Studio project, added a reference to the COM object, and wrote the following code:

using System;
namespace ComTest
{
  class Program
  {
      static void Main(string[] args)
      {
          MyProject.MyClass comObject = new MyProject.MyClass();
          short sum = comObject.Add(1,2);
          Console.Out.WriteLine("Early binding. Sum = " + sum);
          Console.ReadLine();
      }
  }
}

It worked. But after some time, I realized that the third-party COM object is updated on nearly a monthly basis. And the COM object has been probably developed in VB6 with no version compatibility settings, resulting in constantly changing GUIDs even when the interface signatures haven't changed. As a result, my program starts to crash showing the following message:

An unhandled exception of type 'System.InvalidCastException' occurred in ComTest.exe

I had two options: either recompile my Web Service each time I receive a new COM object, or use late binding. Obviously, I went for the last option. My C# code looked like this:

using System;
using System.Reflection;

namespace ComTest
{
  class Program
  {
      static void Main(string[] args)
      {
          System.Type objType = System.Type.GetTypeFromProgID("MyProject.MyClass");
          object comObject = System.Activator.CreateInstance(objType);

          object[] oParms = new Object[] { 1, 2 };
          short sum = (short)objType.InvokeMember("Add", 
                         BindingFlags.InvokeMethod, null, comObject, oParms);
          Console.Out.WriteLine(sum);
          Console.ReadLine();
      }
  }
}

It worked, but imagine if you have not just one method in COM object but many of them. Building a parameter list every time and calling InvokeMember is not the most pleasant way to spend your time in the office. And there was no other option until Visual C# 2010. Thanks to the new dynamic type, it is possible now to declare an object of a type that bypasses static type checking. At compile time, an element that is typed as dynamic is assumed to support any operation. Therefore, we can write the following code now:

using System;
using System.Reflection;

namespace ComTest
{
  class Program
  {
      static void Main(string[] args)
      {
          System.Type objType = 
            System.Type.GetTypeFromProgID("MyProject.MyClass");
          dynamic comObject = 
            System.Activator.CreateInstance(objType);

          short sum = comObject.add(1, 2);
          Console.Out.WriteLine(
            "Late binding with dynamic type. Sum = " + sum);
          Console.ReadLine();
      }
  }
}

So we are now using the same method signatures as in the first example without the need to call InvokeMember. However, if the method signature is not valid, errors are caught at run time. How will it help me? Very simple. First, I start developing my application by adding a reference to the COM object. It's so easy to use the .NET wrappers generated for you by Visual Studio. When I am almost ready to deploy, I remove the reference and use GetTypeFromProgID instead.

That's it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Igor Merabishvili
Software Developer (Senior)
Germany Germany
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 Pin
Mahsa Hassankashi12-Mar-15 5:02
mvpMahsa Hassankashi12-Mar-15 5:02 
Questionaccess interface and methods Pin
abhibio4-Feb-13 2:43
memberabhibio4-Feb-13 2:43 
GeneralAnother option... Pin
Richard Deeming25-Jan-11 10:29
memberRichard Deeming25-Jan-11 10:29 
GeneralRe: Another option... Pin
Igor Merabishvili26-Jan-11 0:41
memberIgor Merabishvili26-Jan-11 0:41 
GeneralRe: Another option... Pin
Richard Deeming26-Jan-11 2:44
memberRichard Deeming26-Jan-11 2:44 
The only part that relies on GUIDs is event handling - everything else works from the ProgID, and uses late-binding behind the scenes.

To quote the article, instead of this:
[ComImport, TypeLibType((short) 4160), Guid("00063001-0000-0000-C000-000000000046")]
public interface _Application
{
    [DispId(61440)]
    Application Application
    {
        [return: MarshalAs(UnmanagedType.Interface)] 
        [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType=MethodCodeType.Runtime)]
        [DispId(61440)] 
        get;
    }
    ...
}

you use something like this:
public interface Common : IDisposable
{
    Application Application { get; }
    NameSpace Session { get; }
    object Parent { get; }
    OlObjectClass Class { get; }
}
 
public interface Application : Common
{
    string Name { get; }
    
    Explorer ActiveExplorer();
    Explorers Explorers{ get; }
    
    string Version { get; }
    Inspector ActiveInspector();
    object CreateItem(OlItemType ItemType);
    object CreateItemFromTemplate(string TemplatePath, object InFolder);
    object CreateObject(string ObjectName);
    NameSpace GetNamespace(string Type);
    
    void Quit();
}
...
using (Application app = (Application)DisposableCOMProxy.Create("Outlook.Application", typeof(Application)))
{
    Debug.WriteLine( app.Name );
    
    using (Explorer explorer = app.ActiveExplorer())
    {
        Debug.WriteLine( explorer.Caption );
    }
}

Not a single GUID in sight. Smile | :)



"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer


GeneralMy vote of 5 Pin
Yusuf21-Jan-11 12:05
memberYusuf21-Jan-11 12:05 
GeneralMy vote of 5 Pin
Nemanja Trifunovic21-Jan-11 8:21
memberNemanja Trifunovic21-Jan-11 8:21 
GeneralRe: My vote of 5 Pin
Igor Merabishvili22-Jan-11 10:54
memberIgor Merabishvili22-Jan-11 10:54 
GeneralRe: My vote of 5 Pin
Philippe Bouteleux24-Jan-11 3:45
memberPhilippe Bouteleux24-Jan-11 3:45 

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 | Terms of Use | Mobile
Web02 | 2.8.150428.2 | Last Updated 21 Jan 2011
Article Copyright 2011 by Igor Merabishvili
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid