2010-10-29 68 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);