I have some code that implements the IReflect interface for presenting an IDispatch face onto some .NET objects without sticking all of the COM interop goo onto the classes themselves.
We used this because we had a scripting client that wanted to use these classes, but we didn't want to put all the COM interop stuff onto the classes themselves.
The code pretty much looks like this:
[ComVisible(true)]
[ProgId("My.Factory")]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class MyFactory
{
public object CreateObject(string type)
{
return new AutoWrap(Activator.CreateInstance(Type.GetType(type)));
}
}
[ProgId("My.AutoWrap")]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ComVisible(true)]
[Guid("72EAFB10-099F-4e96-A17E-B67E34DACA53")]
public class AutoWrap : IReflect
{
protected object O = null;
protected Type T = null;
public AutoWrap()
{
}
public AutoWrap(object obj)
{
O = obj;
T = O.GetType();
}
#region IReflect Members
public System.Reflection.FieldInfo GetField(string name, System.Reflection.BindingFlags bindingAttr)
{
return T.GetField(name, bindingAttr);
}
/* SNIP other IReflect methods */
public object InvokeMember(string name, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, object target, object[] args, System.Reflection.ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] namedParameters)
{
// Unwrap any AutoWrap'd objects (they need to be raw if a paramater)
if (args != null && args.Length > 0)
{
for (int x = 0; x < args.Length; x++)
{
if (args[x] is AutoWrap)
{
args[x] = ((AutoWrap)args[x]).O;
}
}
}
// Invoke whatever needs be invoked!
object obj = T.InvokeMember(name, invokeAttr, binder, O, args, modifiers, culture, namedParameters);
// Wrap any return objects (that are not primative types)
if (obj != null)
{
switch (obj.GetType().ToString())
{
case "System.String":
case "System.DateTime":
case "System.Boolean":
case "System.Byte":
case "System.Char":
case "System.Decimal":
case "System.Double":
case "System.Single": // Float
case "System.Int32":
case "System.Int64": // Long
case "System.SByte":
case "System.Int16": // Short
case "System.UInt32":
case "System.UInt64":
case "System.UInt16":
break; // These Types do not get wrapped
default:
obj = new AutoWrap(obj); // Wrap Type
break;
}
}
return obj;
}
public object UnderlyingObject
{
get
{
return O;
}
}
public Type UnderlyingSystemType
{
get { return T.UnderlyingSystemType; }
}
#endregion
}
The scripting client then has code like the following to make calls into the objects (example in VBScript):
Set factory= CreateObject("My.Factory")
set myObject = factory.CreateObject("MyDotNetType")
myObject.DoSomething ' where DoSomething() is a method on MyDotNetType
With the script above, an instance AutoWrap that wraps an instance of MyDotNetType will get created, and when the client calls DoSomething the InvokeMember method will get called on AutoWrap, which turns around and calls DoSomething on MyDotNetType.
This all works wonderfully well in VBScript and in Javascript.
However, when trying to use this from Siebel's eScript scripting language, things fail because eScript always passes in [DISPID=somerandomnumber] as the name into InvokeMember, rather than the method name as VBScript does.
Does anyone know of a way to get control of those DispIDs from the .NET code? I've tried a few different approaches, none of which have worked:
- Returning custom PropertyInfo/MethodInfo classes from the other IReflect members that will return a DispIdAttribute from GetCustomAttributes
- Reflection.Emiting a wrapper class at runtime that has the required attributes to be exposed to COM (didn't really expect this to work, but thought I'd try it)
TIA