2013-02-28 75 views
0

我有一些COM库需要从WF4活动访问,所以我使用tlbimp为它们生成托管包装。这很好,但在一些情况下,有一个COM方法需要一个变量数组作为其参数之一,而数组的其中一个元素是来自另一个DLL的COM类的一个实例,即使我有一个引用表示同一个对象的托管包装的引用,也不会看到如何获取引用。如何访问使用TLBIMP创建的托管.NET对象的底层COM对象

举个例子,说我有两个叫ABCDEF COM库,为此,我创建了托管的包装使用tlbimp称为ABCManagedDEFManaged。说DEF具有以下签名的方法(原谅VB风格的签名,这是它在文档中的方式):它被作为DEFManaged表示如下

CallFunction (parmArray() as Variant) as Long 

int CallFunction(ref object parmArray) 

到现在为止还挺好。现在,作为CallFunction()参数的变量数组的元素是不同类型的,所以在我的情况下,第一个元素是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

该对象似乎正确地编组到com服务器。否则会引发另一个异常,很可能是一个'InvalidComObjectException'或另一个'COMException'。服务器实现抛出'ArgumentException' get。 http://msdn.microsoft.com/en-us/library/9ztbc5s1.aspx – Carsten 2013-02-28 08:05:56

+0

好吧,'object []'正在编组到'Variant()',但我的猜测是数组中的实际元素是因为接口不知道它必须这样做,所以没有正确编组,因为COM对象和它的管理对象的签名都没有指定数组元素的类型。 你是否认为这个猜测是错误的,并且我的数组的第二个元素(在我的例子中是一个'ManagedABC'对象)被正确封送到底层COM对象(在我的例子中是一个'DEF'对象),尽管有接口没有指定该类型? – user533676 2013-02-28 14:27:06

+0

我误解了这里发生的事情:数组中的单个对象正在编组,但数组本身是通过值而不是通过引用传递的(COM对象要求它通过引用传递)。请参阅下面的答案,详细了解如何解决此问题。 – user533676 2013-03-16 14:17:56

回答

0

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

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

这不是理想的,但它也证明,它可以强制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接口的扩展方法,所以托管包装的用户只需直接调用扩展方法并为它们处理反射。