2012-06-05 64 views
2

我一直在這個圈子上圍繞谷歌,我可以找到各種討論,很多建議,但似乎沒有工作。我有一個ActiveX組件,它將圖像作爲字節數組。當我做TLB導入時,它帶有這個簽名:C#元帥字節[]到COM SAFEARRAY參數與「參考對象」簽名

int HandleImage([MarshalAs(UnmanagedType.Struct)] ref object Bitmap); 

如何將字節[]傳遞給那個?

還有另一個函數可以用類似的簽名返回數據,它的工作原理是因爲我可以傳入「null」。返回的類型是一個字節[1..size](非零有界的byte [ ])。但即使我嘗試傳入返回的內容,它仍然會遇到類型不匹配的異常。


更多細節:

我已經編輯在IDispatch接口簽名的方法(使用ILSpy取出從自動生成的互操作組件的界面)。我已經嘗試過下面的每個組合,它總是得到類型不匹配的異常:

  1. 添加和刪除「裁判」
  2. 更改參數的數據類型爲「字節[]」或「陣列」
  3. 編組爲[MarshalAs(UnmanagedType.SafeArray,SafeArraySubType = VarEnum.VT_UI1)]。在與MarshalAs玩了很久之後,我開始相信IDispatch不會使用這些屬性。

還試圖用「Ref對象」接口是,並將其傳遞不同的類型:字節[],Array.CreateInstance(typeof運算(字節)(我認爲都是相同的,但是我發現有人建議它,所以它不能傷害嘗試)

下面是一個創建一個適當的數組中傳遞的Delphi代碼示例:。

var 
    image: OLEVariant; 
    buf: Pointer; 

image := VarArrayCreate([0, Stream.Size], VarByte); 
Buf := VarArrayLock(image); 
Stream.ReadBuffer(Buf^, Stream.Size); 
VarArrayUnlock(image); 

這裏的C代碼做同樣的事情,我想,如果我無法從C#中使用它,我可以通過託管C++調用它,但我寧願將所有內容都放在一個項目中:

long HandleImage(unsigned char* Bitmap, int Length) 
{ 
    VARIANT vBitmap; 
    VariantInit (&vBitmap); 
    VariantClear(&vBitmap); 

    SAFEARRAYBOUND bounds[1]; 
    bounds[0].cElements = Length; 
    bounds[0].lLbound = 1; 

    SAFEARRAY* arr = SafeArrayCreate(VT_UI1, 1, bounds); 
    SafeArrayLock(arr); 
    memcpy(arr->pvData, Bitmap, Length); 
    SafeArrayUnlock(arr); 
    vBitmap.parray = arr; 
    vBitmap.vt = VT_ARRAY | VT_UI1; 

    long result; 
    static BYTE parms[] = VTS_PVARIANT; 
    InvokeHelper(0x5e, DISPATCH_METHOD, VT_I4, (void*)&result, parms, 
     &vBitmap); 

    SafeArrayDestroy(arr); 
    VariantClear(&vBitmap); 

    return result; 
} 

回答

0

我終於想出瞭如何在100%的C#代碼中做到這一點。顯然,微軟從來沒有考慮過某個人可能使用帶有這個簽名的方法來傳遞數據,因爲它正確地向另一個方向集中(它正確地返回爲一個字節[])。

此外,ICustomMarshaler不會在IDispatch調用上調用,它從不會觸及自定義封送拆分器中的斷點(除了獲取其實例的靜態方法)。

由Hans帕桑特在這個問題的答案讓我在正確的軌道上:Calling a member of IDispatch COM interface from C#

IDispatch接口的副本有不包含的IUnknown上的「調用」的方法,但它可以被添加到界面,根據需要使用System.Runtime.InteropServices.ComTypes中的類型:http://msdn.microsoft.com/en-us/library/windows/desktop/ms221479%28v=vs.85%29.aspx

這意味着您可以100%控制編組參數。微軟並沒有公開VARIANT結構的實現,所以你必須自己定義:http://limbioliong.wordpress.com/2011/09/19/defining-a-variant-structure-in-managed-code-part-2/

Invoke的輸入參數是一個變體數組,因此你必須將它們編組到一個非託管數組中,並且有一個變體輸出參數。

那麼現在我們有一個變體,它應該包含什麼?這是自動封送掉落的地方。它不需要直接嵌入一個指向SAFEARRAY的指針,它需要一個指向另一個變體的指針,並且該變體應該指向SAFEARRAY。

您可以通過P建立SAFEARRAYS /調用這些方法:http://msdn.microsoft.com/en-us/library/windows/desktop/ms221145%28v=vs.85%29.aspx

所以主要變型應該是VT_VARIANT | VT_BYREF,它應該指向另一個變體VT_UI8 | VT_ARRAY,並且應該指向通過SafeArrayCreate()生成的SAFEARRAY。然後將該最外層的變體複製到內存塊中,並將其IntPtr發送到Invoke方法。

+0

你有樣品如何在C#上做到這一點? –

+0

對不起,實際的實現是在我以前的僱主代碼庫中,但我認爲可以口頭描述所有部分如何組合在一起......這足以讓某人獲得足夠的信息來自己實現一點點的工作。 –

+0

多大的一堆臭味。 – Zack