如何访问使用TLBIMP创建的托管.NET对象的基础COM对象

时间:2013-02-28 05:15:14

标签: c# com com-interop

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

作为一个例子,假设我有两个名为ABCDEF的COM库,我使用ABCManaged创建了名为DEFManagedtlbimp的托管包装器}。假设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对象上的属性和方法,因此它们需要是托管对象。

1 个答案:

答案 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接口上将其转换为扩展方法,因此托管包装器的用户可以直接调用扩展方法并处理它们的反射。