The CLR supports marshalling of objects that support the IReflect interface as IDispatch COM objects. Similarly, IExpando gets marshalled as IDispatchEx. Here is a sample of a managed type called ManagedIDispatch which is used from VBScript and used in a late-bound way. VBScript just deals with IDispatch, and under the hoods, the CLR routes the calls to the IReflect methods implemented by ManagedIDispatch, and also takes care of providing a stable mapping between method names and dispids.
IReflect.GetMethods gets called every time COM instantiates the managed type. The CLR caches the method names for the given object, and the names are then available to COM from the IDispatch interface. If COM accesses a method name that is not in the cached list, IReflect.GetMethods get called again.
The C# code should be compiled and registered as:
csc /t:library ManagedIDispatch.cs
regasm /codebase ManagedIDispatch.dll
public class ManagedIDispatch : IReflect {
...
public MethodInfo[] GetMethods(BindingFlags bindingAttr) {
Console.WriteLine("In ManagedIDispatch.GetMethods (_getMethodsDone={0})", _getMethodsDone);
// dynFoo is a method that is not always present
if (_getMethodsDone) {
MethodInfo[] methods = new MethodInfo[_methodInfos.Length + 1];
_methodInfos.CopyTo(methods, 0);
methods[_methodInfos.Length] = new MyMethodInfo("dynFoo");
return methods;
} else {
_getMethodsDone = true;
return _methodInfos;
}
public object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) {
Console.WriteLine("In ManagedIDispatch.InvokeMember({0} : {1})", name, GetArgs(target, args));
switch (name) {
case "[DISPID=0]": return this;
case "raiseException": throw new Exception("raiseException has been invoked");
case "foo": return "foo-return";
case "dynFoo": Debug.Assert(_getMethodsDone); return "dynFoo-return";
default:
if (name == ("bar" + _index)) return "bar-return";
throw Utils.NotImplemented("This is unreachable");
};
Sub AssertError(errNum, errDescr) If Err.Number <> errNum Then MsgBox "Error number is incorrect:" & Err.Number & ", expected " & errNum End If
If Err.Description <> errDescr Then MsgBox "Error description is incorrect:" & Err.Description & ", expected " & errDescr End If
Err.ClearEnd Sub
c1 = CreateObject("TestFactory")m = c1.GetManagedObject()m.foo()m.dynFoo()m.bar0()
On Error Resume Next
m.nonexistent()AssertError 438, "Object doesn't support this property or method"
m.raiseException()AssertError -2146233088, "" ' 0x80131500 : CLR exception
The output of the script is:
c:\bugs>cscript vbs1.vbsMicrosoft (R) Windows Script Host Version 5.7Copyright (C) Microsoft Corporation. All rights reserved.In ManagedIDispatch.GetMethods (_getMethodsDone=False)In MyMethodInfo.Name.get(foo)In MyMethodInfo.Name.get(bar0)In MyMethodInfo.Name.get(raiseException)In ManagedIDispatch.InvokeMember([DISPID=0] : ManagedIDispatch)In ManagedIDispatch.InvokeMember(foo : ManagedIDispatch)In ManagedIDispatch.GetMethods (_getMethodsDone=True)In MyMethodInfo.Name.get(dynFoo)In ManagedIDispatch.InvokeMember(dynFoo : ManagedIDispatch)In ManagedIDispatch.InvokeMember(bar0 : ManagedIDispatch)In ManagedIDispatch.GetMethods (_getMethodsDone=True)In MyMethodInfo.Name.get(dynFoo)In ManagedIDispatch.InvokeMember(raiseException : ManagedIDispatch)