2013-02-28 76 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接口的擴展方法,所以託管包裝的用戶只需直接調用擴展方法併爲它們處理反射。