Ich habe etwas Code, der die IReflect-Schnittstelle implementiert, um ein IDispatch-Gesicht auf einigen .NET-Objekten zu präsentieren, ohne alle COM-Interop-Go auf die Klassen selbst zu kleben.IReflect und DispId
Wir haben das verwendet, weil wir einen Scripting-Client hatten, der diese Klassen verwenden wollte, aber wir wollten nicht alle COM-Interop-Sachen auf die Klassen selbst legen.
Der Code ziemlich sieht wie folgt aus:
[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
}
Der Scripting-Client hat dann Code wie der folgende Anrufe in die Objekte (zB in VBScript) zu machen:
Set factory= CreateObject("My.Factory")
set myObject = factory.CreateObject("MyDotNetType")
myObject.DoSomething ' where DoSomething() is a method on MyDotNetType
Mit dem Skript oben wird eine Instanz-AutoWrap, die eine Instanz von MyDotNetType umschließt, erstellt, und wenn der Client DoSomething aufruft, wird die InvokeMember-Methode auf AutoWrap aufgerufen, die sich dreht und DoSomething auf MyDotNetType aufruft.
Das alles funktioniert wunderbar in VBScript und Javascript.
Wenn Sie jedoch versuchen, dies aus Siebels eScript-Skriptsprache zu verwenden, schlagen die Dinge fehl, da eScript immer den Namen der Methode [DISPID = anyrandomnumber] in InvokeMember und nicht den Methodennamen wie VBScript eingibt.
Kennt jemand eine Möglichkeit, die Kontrolle über diese DispIDs aus dem .NET-Code zu bekommen? Ich habe ein paar verschiedenen Ansätze ausprobiert, von denen keines gearbeitet haben:
- Rückkehr benutzerdefinierte Propertyinfo/Method Klassen von den anderen IReflect Mitgliedern, die eine DispIdAttribute von GetCustomAttributes
- Reflection.Emiting eine Wrapper-Klasse zur Laufzeit zurück das hat die erforderlichen Attribute für COM verfügbar gemacht werden (nicht wirklich dies erwarten zu arbeiten, aber ich dachte, es würde versuchen)
TIA
Sie können auch obj.GetType(). IsValueType anstelle dieser switch-Anweisung verwenden. Marshal.IsComObject (Obj) ist auch sehr nützlich. –