我需要从WF4活动访问许多COM库,因此我使用tlbimp
为它们生成托管包装器。这样可以正常工作,但在某些情况下,有一个COM方法需要Variants数组作为其参数之一,并且数组的一个元素是来自另一个DLL的COM类的实例,我可以'看看如何获得引用,即使我有一个对代表同一个对象的托管包装器的引用。
作为一个例子,假设我有两个名为ABC
和DEF
的COM库,我使用ABCManaged
创建了名为DEFManaged
和tlbimp
的托管包装器}。假设DEF
有一个带有以下签名的方法(原谅VB风格的签名,就像它在文档中那样):
CallFunction (parmArray() as Variant) as Long
这在DEFManaged
中表示如下:
int CallFunction(ref object parmArray)
到目前为止一切顺利。现在,作为CallFunction()
的参数的Variants数组的元素具有不同的类型,因此在我的情况下,第一个元素是double
,第二个元素是ABC
的实例}。以下是我如何构建我的数组以传递给我的托管CallFunction()
:
// obtain our managed ABC from the workflow context
ABCManaged abc = context.GetValue(this.InputABC);
object[] parameters = new object[2];
parameters[0] = new double();
parameters[1] = abc;
DEFManaged def = new DEFManaged();
def.CallFunction(parameters);
这编译很好,但不起作用 - 对CallFunction()
的调用会抛出此异常:
System.ArgumentException: Value does not fall within the expected range.
我的猜测是def
没有正确地封送到底层的非托管DEF
类型,可能是因为签名只有一个object
数组,而且逻辑决定了真实数组元素的类型在COM方法CallFunction()
的实现中的某处。我无法弄清楚如何将def
对象手动封送到非托管DEF
类型,然后再将其粘贴到数组中以传递给我的托管CallFunction()
。
此处也不能使用直接P / Invoke;我当然可以编写代码,但其中一个要求是WF4活动的用户能够访问DEFManaged
对象上的属性和方法,因此它们需要是托管对象。
答案 0 :(得分:0)
事实证明,数组中的各个对象正在被正确编组,但数组本身是通过值而不是通过引用传递的(COM对象要求它通过引用传递 - VT_BYREF
)。当我能够将调试器附加到COM对象时,这就浮出水面了。
通过引用从C#传递数组到COM并不简单。我最初解决这个问题,在VB6(!)中有一个中间代理COM对象,它不需要通过引用传递,并从我的代理对象调用原始COM对象(VB6总是通过引用传递)。
这并不理想,但事实证明,有可能强制C#通过引用使用后期绑定通过反射和ParameterModifier将参数传递给COM方法,如this answer中所述。到my question on the topic。
以下是它的工作方式:
// obtain our managed ABC from the workflow context
ABCManaged abc = context.GetValue(this.InputABC);
object[] parameters = new object[2]; // method argument, i.e. parmArray value
parameters[0] = new double();
parameters[1] = abc;
DEFManaged d = new DEFManaged(); // managed wrapper for our COM object
var t = typeof(DEFManaged);
object[] args = new object[1]; // array which will contain all method arguments
args[0] = data; // data is the first argument, i.e. first element of args array
ParameterModifier[] pms = new ParameterModifier[1];
ParameterModifier pm = new ParameterModifier(1);
pm[0] = true; // pass the 1st argument by reference
pms[0] = pm; // add pm to the array of modifiers
// invoke CallFunction by name via IDispatch interface
var ret = t.InvokeMember("CallFunction", System.Reflection.BindingFlags.InvokeMethod, null, d, args, pms, null, null);
Console.Out.WriteLine("Result = " + ret);
正如在原始答案中所建议的那样,我在DEFManaged
接口上将其转换为扩展方法,因此托管包装器的用户可以直接调用扩展方法并处理它们的反射。