2010-10-29 67 views
1

在Compact Framework的3.5,我試圖調用一個IDL函數簽名的ActiveX對象:.NET精簡框架 - 調用ActiveX對象使用[出] SAFEARRAY(浮動)*

HRESULT MyFunc([out] SAFEARRAY(float) *var) 

的互操作一代創建MSIL

[out] class [mscorlib]System.Array& marshal(safearray float32) 

這似乎不夠合理,但我不斷收到一個「NotSupportedException異常」。根據題爲「Interop:常見問題和調試技術」的文章(我不能發佈超過一個超鏈接,這是該短語的第一個谷歌結果),在「Marshaling」標題下的第一個要點中,緊湊框架不適當編組SAFEARRAYs。

我試圖解決這個問題,通過操縱這個MSDN論壇的帖子中描述的答案(最後條目描述他的方法):http://social.msdn.microsoft.com/forums/en-US/clr/thread/6641abfc-3a9c-4976-a523-43890b2b79a2/

所以,我創建瞭如下定義:

[StructLayout(LayoutKind.Sequential)] 
struct SafeArray 
{ 
    public ushort dimensions;  // Count of dimensions in the SAFEARRAY 
    public ushort features;  // Flags to describe SAFEARRAY usage 
    public uint elementSize; // Size of an array element 
    public uint locks;   // Number of times locked without unlocking 
    public IntPtr dataPtr;  // Pointer to the array data 
    public uint elementCount; // Element count for first (only) dimension 
    public int lowerBound;  // Lower bound for first (only) dimension 
} 

並重新定義了IDL的函數簽名:

HRESULT MyFunc([out] long *var) 

然後執行以下代碼:

IntPtr safeArrayPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(SafeArray))); 
SafeArray safeArray; 
safeArray.dimensions = 1; 
safeArray.features = 0; 
safeArray.elementSize = (uint)(Marshal.SizeOf(typeof(float))); 
safeArray.locks = 0; 
safeArray.elementCount = 6; 
safeArray.lowerBound = 0; 
safeArray.dataPtr = Marshal.AllocCoTaskMem((int)(safeArray.elementCount * safeArray.elementSize)); 

Marshal.StructureToPtr(safeArray, safeArrayPtr, false); 
int iTmp = safeArrayPtr.ToInt32(); 
MyFunc(out iTmp) 

雖然代碼似乎成功,當我嘗試讀回的數據值,使用Marshal.Copy(dataPtr,myFloatArr,FALSE)函數,我得到所有0的數據,還告訴我ActiveX DLL正在得到的指針可能完全是虛假的,並且它已經被遺忘了。

對於我可能在這些定義中搞砸了什麼有什麼建議,或者對於解決這個問題的其他方法有什麼建議?

在此先感謝...

回答

1

嗯,我已經解決了這個問題。
希望我的回答能夠幫助其他遇到同樣問題的人。我遇到的問題是COM tlb聲明中的[out]標籤意味着我傳入的任何內容都將被在COM庫內創建的對象覆蓋。經典的(非常基本的問題)的一個相當複雜版「通過引用傳遞VS值傳遞」

所以,正確的編組是使用安全數組的,我上面貼的定義。

不要觸摸IDL簽名本身 - 這不是一個很乾淨的做法。相反,使用ILDASM上產生的互操作庫,從修改IL:

[out] class [mscorlib]System.Array& marshal(safearray float32) 

[out] native int& 

,然後用ILASM這將產生一個C#函數簽名

void MyFunc(out IntPtr var) 

的重組電話代碼變爲:

IntPtr ip; 
SafeArray safeArray; 
float []arrFloats; 
MyFunc(out ip); 
//Marshal the structure itself 
Marshal.StructureToPtr(safeArray, ip, false); 

//Marshal the data over to .NET 
float []arrFloats = new float[safeArray.elementCount]; 
Marshal.Copy(safeArray.dataPtr, arrFloats, 0, (int)safeArray.elementCount); 

最後,我們需要釋放內存(請記住,我們更改了函數簽名,因此我們沒有給.NET足夠的信息來實際釋放內存。

//Don't forget to free both the structure and the object 
Marshal.FreeCoTaskMem(safeArray.dataPtr); 
Marshal.FreeCoTaskMem(ip);