Does C# .NET support IDispatch late binding?
Asked Answered
S

6

28

The Question

My question is: Does C# nativly support late-binding IDispatch?


Pretend i'm trying to automate Office, while being compatible with whatever version the customer has installed.

In the .NET world if you developed with Office 2000 installed, every developer, and every customer, from now until the end of time, is required to have Office 2000.

In the world before .NET, we used COM to talk to Office applications.

For example:

1) Use the version independant ProgID

"Excel.Application"

which resolves to:

clsid = {00024500-0000-0000-C000-000000000046}

and then using COM, we ask for one of these classes to be instantiated into an object:

IUnknown unk;
CoCreateInstance(
    clsid, 
    null,
    CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
    IUnknown, 
    out unk);

And now we're off to the races - able to use Excel from inside my application. Of course, if really you want to use the object, you have to call have some way of calling methods.

We could get ahold of the various interface declarations, translated into our language. This technique is good because we get

  • early binding
  • code-insight
  • compile type syntax checking

and some example code might be:

Application xl = (IExcelApplication)unk;
ExcelWorkbook workbook = xl.Workbooks.Add(template, lcid);
Worksheet worksheet = workbook.ActiveSheet;

But there is a downside of using interfaces: we have to get ahold of the various interface declarations, transated into our language. And we're stuck using method-based invocations, having to specify all parameters, e.g.:

ExcelWorkbook workbook = xl.Workbooks.Add(template, lcid);
xl.Worksheets.Add(before, after, count, type, lcid);

This has proved, in the real world, to have such downsides that we would willingly give up:

  • early binding
  • code-insight
  • compile time syntax checking

and instead use IDispatch late binding:

Variant xl = (IDispatch)unk;
Variant newWorksheet = xl.Worksheets.Add();

Because Excel automation was designed for VB Script, a lot of parameters can be ommitted, even when there is no overload without them.

Note: Don't confuse my example of Excel with a reason of why i want to use IDispatch. Not every COM object is Excel. Some COM objects have no support other than through IDispatch.

Shontashoo answered 31/12, 2008 at 15:33 Comment(1)
This is a well written question. Thanks.Headstream
S
26

You can, relativly, use late-binding IDispatch binding in C#.

http://support.microsoft.com/kb/302902

Here's some sample for using Excel. This way you don't need to add a needless dependancy on Microsoft's bloaty PIA:

//Create XL
Object xl = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));

//Get the workbooks collection.
//   books = xl.Workbooks;
Object books = xl.GetType().InvokeMember( "Workbooks", 
      BindingFlags.GetProperty, null, xl, null);

//Add a new workbook.
//   book = books.Add();
Objet book = books.GetType().InvokeMember( "Add", 
      BindingFlags.InvokeMethod, null, books, null );

//Get the worksheets collection.
//   sheets = book.Worksheets;
Object sheets = book.GetType().InvokeMember( "Worksheets",
      BindingFlags.GetProperty, null, book, null );

Object[] parameters;

//Get the first worksheet.
//   sheet = sheets.Item[1]
parameters = new Object[1];
parameters[0] = 1;
Object sheet = sheets.GetType().InvokeMember( "Item", 
      BindingFlags.GetProperty, null, sheets, parameters );

//Get a range object that contains cell A1.
//   range = sheet.Range["A1];
parameters = new Object[2];
parameters[0] = "A1";
parameters[1] = Missing.Value;
Object range = sheet.GetType().InvokeMember( "Range",
      BindingFlags.GetProperty, null, sheet, parameters );

//Write "Hello, World!" in cell A1.
//   range.Value = "Hello, World!";
parameters = new Object[1];
parameters[0] = "Hello, World!";
objRange_Late.GetType().InvokeMember( "Value", BindingFlags.SetProperty, 
      null, range, parameters );

//Return control of Excel to the user.
//   xl.Visible = true;
//   xl.UserControl = true;
parameters = new Object[1];
parameters[0] = true;
xl.GetType().InvokeMember( "Visible", BindingFlags.SetProperty,
      null, xl, Parameters );
xl.GetType().InvokeMember( "UserControl", BindingFlags.SetProperty,
      null, xl, Parameters );
Shontashoo answered 28/1, 2009 at 20:47 Comment(4)
Hmm...The linked to MSDN page had the sample in C# already...? Regardless, thanks for the info. I am sure I can find a use for this style of late-binding approach.Spragens
This results in some truely ugly code, but at least it works :)Headstream
Very great! I've "broke" my head thinking on the solution for my similar problem, but you've save my life. :) It really works! :) Thanks! :)Woodward
@IanBoyd Does Activator.CreateInstance() method creates a new Excel window or connects to existing one?Cattima
B
9

You gotta wait for C# 4.0 to come out to get the late binding that you are looking for. Any time I need interop capabilities I switch back to VB.Net mode so I can take advantage of the COM capabilities that C# seems to lack.

The simple method that I use is creating a class in VB.Net that does the IDispatch work and then exposing the methods that I want to use as methods of my wrapper and then I can call them at will from my C# code. Not the most elegant of solutions, but it has gotten me out of a jam or two over the past few months.

Berrie answered 31/12, 2008 at 15:43 Comment(1)
That's a good way to get around that. I usually use an Adapter project in C# with strong typing and the COM interfaces though - I find it more error-proof.Karr
K
5

C# 4's dynamic keyword supports IDispatch and late binding. You can read Sam Ng's dynamic series for more information

Oh, and C# 4 is only available as a CTP today. You'll have to either wait for Visual Studio vNext or use the beta (which runs on a Windows Server 2008 Virtual PC) to use that.

Karr answered 31/12, 2008 at 15:36 Comment(1)
Well, CTP, but same difference.Engelhart
R
4

As others have said - using c#4's "dynamic" keyword rocks. Here's a simple example - it so much more succint than using "InvokeMethod"

dynamic xl = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
dynamic books = xl.Workbooks;
dynamic book = books.Add();

Console.WriteLine(books.Count);     //Writes 1

foreach (dynamic b in books)        
{
Console.WriteLine(b.Name);      //Writes "Book1"
}
Reiter answered 9/11, 2011 at 18:33 Comment(1)
i still find it sad that it took MS ten years to do what competitors were doing in 1999. That having been said: i still have to support .NET 2.0.Shontashoo
D
2

probably you can get away with much nicer code in in C# 2.0/3.0 if you take the time to write an interface containing the methods and properties you want of the object and add some attributes (i write it from memory, so details may not be correct, but i swear it worked for me...)

    using System.Runtime.Interopservices;

    [Guid("00024500-0000-0000-C000-000000000046")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    interface IExcel
    {
      // sample property
      string Name{get;}
      // more properties
    }

    // and somewhere else
    void main()
    {
      Object xl = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
      IExcel excel = (IExcel)xl;
      string name = xl.name
    }

As mentioned, the code will not work out of the box, it is more a hint what to dig for in msdn.

Dogmatism answered 31/10, 2009 at 20:31 Comment(2)
What you have there is early binding, which is the exact opposite of late binding.Shontashoo
Oh, and some COM objects only expose two interfaces: IUnknown and IDispatch.Shontashoo
P
1

hey Dude, i have 2 codeplex projects currently to resolve this problem.

the first one is LateBindingApi.Excel http://excel.codeplex.com mapped latebinded invoke call to the well known object model. this was a test project for the following project.

the second one is a CodeGenerator http://latebindingapi.codeplex.com the tool creates c# projects from COM Type libraries. the generated projects includes mapper objects with latebind accessing the COM Server. the highlight is the tool converts COM type libs in different versions to one single project(for example excel 9,10,11) and marked all entities with an self defined SupportByLibrary Attribut. i have analyzed all office apps in version 9,10,11,12,14 with this this tool now and generate a c# solution, its available as tested beta with sample code on main page.

Phlyctena answered 28/4, 2011 at 12:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.