I'm trying to duplicate the behavior that I observe in JScript in C#. I am using IDispatch to enumerate members and call them on late-bound objects. I am a complete C++ noob and know just enough about COM to be very dangerous. Here are my question(s):
In the example below, an Excel spreadsheet's cell 1,1 has the value set to some text and "bolded".
Consider the following WSH JScript:
var objExcel = new ActiveXObject("Excel.Application");
objExcel.Workbooks.Add();
objExcel.Visible = true;
objExcel.Cells(1,1).Value = "some test value";
objExcel.Cells(1,1).Font.Bold = true;
This C# code creates the same result (yes, sorry it's very verbose):
Type axType = Type.GetTypeFromProgID("Excel.Application");
object objExcel = Activator.CreateInstance(axType);
object workbooks = objExcel.GetType().InvokeMember("Workbooks", System.Reflection.BindingFlags.GetProperty, null, objExcel, null);
objExcel.GetType().InvokeMember("Visible", System.Reflection.BindingFlags.SetProperty, null, objExcel, new object[] { true });
workbooks.GetType().InvokeMember("Add", System.Reflection.BindingFlags.InvokeMethod, null, workbooks, new object[] { true });
object cell = objExcel.GetType().InvokeMember("Cells", System.Reflection.BindingFlags.GetProperty, null, objExcel, new object[] { 1, 1 });
cell.GetType().InvokeMember("Value", System.Reflection.BindingFlags.SetProperty, null, cell, new object[] { "some test value" });
object font = cell.GetType().InvokeMember("Font", System.Reflection.BindingFlags.GetProperty, null, cell, null);
font.GetType().InvokeMember("Bold", System.Reflection.BindingFlags.SetProperty, null, font, new object[] { true });
When I get time one way I plan to try and learn more about this is to have JScript call out to a C# COM class I would create with logging/debugging.
Yes, DISPID_VALUE is #defined to 0 in oaidl.idl. Giving a property a dispip of 0 makes it the default property. Lots of languages permit omitting the name of the default property. Equivalent to the C# indexer.
It is just a convention, there is certainly no requirement for a COM IDispatch derived interface to expose a default property. Nor does the property have to be named "Value". It is merely common to do this. The indexer of a [ComVisible] C# interface will get dispid 0 but with the name "Item". Lots of other variations around, you can't assume anything.
BindingFlags.SetProperty works because the Cells member is a property, not a method. It looks a bit like a method only because it is an indexed property. That's barely supported in C# (only for the indexer) but unrestricted in COM or VB.NET. The COM IDispatch interface is used in your example code, it permits looking up members by name. IDispatch::GetIDsOfNames() gets that done, mapping a string to a number (the dispid) when then can be used in to invoke the property or method with IDispatch::Invoke(). Note that this works only one way, name to number. IDispatch doesn't support the equivalent of Reflection.
Get rid of the ugly C# code by writing this in VB.NET or by using the C# version 4 dynamic keyword.