I'd like to build a COM visible C# class, say DynamicComponent
, that would provide a dynamic interface through COM.
Internally this class would maintain a dictionary of delegates:
"GetTheAnswer" -> () => { return 42; }
"Add" -> (int a, int b) => { return a + b; }
...
The client code would be some VBA.
Here is the workflow I naively imagine:
- from the Excel/VBA editor the user references the TLB
- the user instantiates a new
DynamicComponent
(well at least get a stub provided by Excel/VBA) - Excel/VBA COM infrastructure queries the component through its IDispatch interface
- the component answers with a disp-ids map like
["GetTheAnswer" -> 1, "Add" -> 2]
- the user can benefit from auto-completion and sees two methods:
GetTheAnswer
andAdd
- the user invokes any of these methods as if it was statically defined
My first question: is it possible?
If no: why?
If yes: how?
From what I know about COM, if it's possible, the IDispatch COM interface is my best friend.
Moreover, from what I understand, the ICustomQueryInterface interface from .Net 4 could greatly help too.
But as nowadays COM is not really cutting-edge ;) it's quite hard to find resources like code samples.
I've found this interesting sample: https://clrinterop.codeplex.com/releases/view/32350 which implements COM aggregation using the ICustomQueryInterface interface
But it's not dynamic and based on statically defined types and interfaces.
Any help would be greatly appreciated.
Thanks.
Exposing
IDispatchEx
would work for JavaScript, but I don't think VBA makes any use of it. AFAIK, VBA relies uponIDispatch
for late binding. Furthermore, C#dynamic
is great for consumption of COMIDispatch
-based objects on .NET side, but not vice versa. For some reason (.NET designers decision?), dynamic properties and methods ofExpandoObject
andDynamicObject
are not exposed to COM by default.Fortunately, there's a way to override this: by implementing IReflect interface. Refer to this excellent blog post for implementation details. I've myself looked at exposing properties of C# anonymous class to COM, and ended up using
IReflect
. This is how you can expose dynamic methods and properties to COM. Figuratively speaking,IReflect
is exposed to COM asIDispatch
.On a side note, IExpando does the same job for
IDispatchEx
, so a JavaScript client can add new properties which can later be accesses by managed code.[UPDATE] Below is a prototype implementation that exposes an instance of
DynamicComponent
to VBScript running insideWebBrowser
. It works quite well for VBScript and should do so for VBA too. Although, I doubt VBA auto-completion will work, or there is an easy way to implement such feature. AFAIU, VBA auto-completion relies upon COM type library (obtainable viaIDispatch::GetTypeInfo
), but I don't think .NET interop engine would generate a dynamic type library when it implementsIDispatch
throughIReflect
(I might be wrong). Also, this implementation is case-sensitive for method-by-name look-ups, which should be tweaked as VB is case-insensitive.You can't create interfaces on the fly, but you can create methods. Look at Expando Objects in C#.
In particular, you can create a custom IDispatchEx implementation using expandos; you can use the expando to map names to IDs, and reflection to invoke the object.