0

我有许多需要从 WF4 活动访问的 COM 库,因此我曾经tlbimp为它们生成托管包装器。这很好用,但在某些情况下,有一个 COM 方法需要一个 Variants 数组作为其参数之一,并且该数组的元素之一是来自另一个 DLL 的 COM 类的实例,我可以'即使我有对表示同一对象的托管包装器的引用,也看不到如何获得引用。

例如,假设我有两个名为ABCand的 COM 库DEF,为此我创建了名为ABCManagedand DEFManagedusing的托管包装器tlbimp。假设DEF有一个具有以下签名的方法(请原谅 VB 风格的签名,这就是它在文档中的方式):

CallFunction (parmArray() as Variant) as Long

这表示DEFManaged如下:

int CallFunction(ref object parmArray)

到现在为止还挺好。现在,作为参数的变量数组的元素是CallFunction()不同类型的,所以在我的例子中,第一个元素是 adouble而第二个元素是ABC. 以下是我如何构建我的数组以传递给我的 managed 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()。在将对象粘贴到数组中以传递给我的 managed 之前,我无法弄清楚如何手动将我的def对象编组为非托管类型。DEFCallFunction()

在这里也不能选择直接使用 P/Invoke;我当然可以编写代码,但其中一项要求是 WF4 活动的用户能够访问DEFManaged对象的属性和方法,因此它们需要是托管对象。

4

1 回答 1

0

事实证明,数组中的各个对象被正确编组,但数组本身是按值而不是按引用传递的(COM 对象要求它按引用传递 - VT_BYREF)。当我能够将调试器附加到 COM 对象时,这浮出水面。

通过引用从 C# 向 COM 传递数组并不简单。我最初通过在 VB6 (!) 中有一个不需要通过引用传递的中间代理 COM 对象来解决这个问题,并从我的代理对象调用原始 COM 对象(VB6 总是通过引用传递)。

这并不理想,但事实证明,可以通过反射和ParameterModifier使用后期绑定来强制 C# 通过引用将参数传递给 COM 方法,正如我对主题的问题的回答中所述。

这是它的工作方式:

// 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接口上的扩展方法,因此托管包装器的用户可以简单地直接调用扩展方法并为他们处理反射。

于 2013-03-16T14:37:41.780 回答