如何对通过 RCW 公开的 COM 对象使用反射?
How to use reflection on a COM object exposed through the RCW?
我使用类型库导入程序 (TlbImp.exe) 生成了一个用于与 COM 对象连接的互操作程序集。我想做的是构建一个表达式树,它调用 COM 对象上的方法。我想将表达式树编译成 lambda 并缓存它。
之所以要这样做,是因为在这个COM对象中,有很多类似的接口,唯一不同的是参数类型。例如,有一个用于 IFooDouble、IFooInt、IFooString、IFooLongInt 等的接口。然后每个接口都将定义一个方法 SetValue(T value),其中 T 可以是 double、int、string 等,具体取决于接口。在这里,我将表达式树编译成 Action<IFooBase, T>
lambda 并缓存它。
生成的互操作程序集包含 SetValue
方法的强类型接口,但我找不到通过反射获取对其 MethodInfo
对象的引用的方法。由于包装器类型是 System.__ComObject
,我可以调用 InvokeMember
并以这种方式进行。但是,我想知道它是否会比我直接通过互操作接口之一调用该方法慢得多?也就是说,我想知道 IFooBase.InvokeMember("SetValue", ...)
的性能是否会比 IFooDouble.SetValue(11.3)
差很多,尤其是在进行多次重复调用时。
在一次性控制台应用程序中进行一些试验后,我能够让它工作。事实证明我在反射调用中犯了一些错误。我没有在 Assembly.GetType
方法中使用接口的完整类型名称,其中包括命名空间。我也没有在 Type.GetMethod
的 BindingFlags
参数中指定正确的标志。
这是它的工作版本:
private static Action<object, T> CreateSetterExpression<T>(string fooType)
{
// IFoo is a base interface in the interop assembly that
// we are interested in.
var fooBaseInterfaceType = typeof(IFoo);
var fooInterfaceType = fooBaseInterfaceType.Assembly
.GetType(
$"{fooBaseInterfaceType.Namespace}.IFoo{fooType}",
true,
true);
var setValueMethodInfo = fooInterfaceType.GetMethod("SetRequestedValue",
BindingFlags.Instance | BindingFlags.Public,
null,
CallingConventions.Any,
new[] { typeof(T) },
null);
ParameterExpression instanceParam = Expression.Parameter(typeof(object), "instanceObject");
var instanceExp = Expression.Convert(instanceParam, fooInterfaceType);
ParameterExpression valueParam = Expression.Parameter(typeof(T), "value");
var callExpression = Expression.Call(instanceExp, setValueMethodInfo, valueParam);
return Expression.Lambda<Action<object, T>>(
callExpression, instanceParam, valueParam)
.Compile();
}
我使用类型库导入程序 (TlbImp.exe) 生成了一个用于与 COM 对象连接的互操作程序集。我想做的是构建一个表达式树,它调用 COM 对象上的方法。我想将表达式树编译成 lambda 并缓存它。
之所以要这样做,是因为在这个COM对象中,有很多类似的接口,唯一不同的是参数类型。例如,有一个用于 IFooDouble、IFooInt、IFooString、IFooLongInt 等的接口。然后每个接口都将定义一个方法 SetValue(T value),其中 T 可以是 double、int、string 等,具体取决于接口。在这里,我将表达式树编译成 Action<IFooBase, T>
lambda 并缓存它。
生成的互操作程序集包含 SetValue
方法的强类型接口,但我找不到通过反射获取对其 MethodInfo
对象的引用的方法。由于包装器类型是 System.__ComObject
,我可以调用 InvokeMember
并以这种方式进行。但是,我想知道它是否会比我直接通过互操作接口之一调用该方法慢得多?也就是说,我想知道 IFooBase.InvokeMember("SetValue", ...)
的性能是否会比 IFooDouble.SetValue(11.3)
差很多,尤其是在进行多次重复调用时。
在一次性控制台应用程序中进行一些试验后,我能够让它工作。事实证明我在反射调用中犯了一些错误。我没有在 Assembly.GetType
方法中使用接口的完整类型名称,其中包括命名空间。我也没有在 Type.GetMethod
的 BindingFlags
参数中指定正确的标志。
这是它的工作版本:
private static Action<object, T> CreateSetterExpression<T>(string fooType)
{
// IFoo is a base interface in the interop assembly that
// we are interested in.
var fooBaseInterfaceType = typeof(IFoo);
var fooInterfaceType = fooBaseInterfaceType.Assembly
.GetType(
$"{fooBaseInterfaceType.Namespace}.IFoo{fooType}",
true,
true);
var setValueMethodInfo = fooInterfaceType.GetMethod("SetRequestedValue",
BindingFlags.Instance | BindingFlags.Public,
null,
CallingConventions.Any,
new[] { typeof(T) },
null);
ParameterExpression instanceParam = Expression.Parameter(typeof(object), "instanceObject");
var instanceExp = Expression.Convert(instanceParam, fooInterfaceType);
ParameterExpression valueParam = Expression.Parameter(typeof(T), "value");
var callExpression = Expression.Call(instanceExp, setValueMethodInfo, valueParam);
return Expression.Lambda<Action<object, T>>(
callExpression, instanceParam, valueParam)
.Compile();
}