1

在 Compact Framework 3.5 中,我尝试调用具有 IDL 函数签名的 ActiveX 对象:

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

互操作生成创建 msil

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

这似乎很合理,但我不断收到“NotSupportedException”。根据一篇题为“互操作:常见问题和调试技术”的文章(我不能发布多个超链接,这是该短语的第一个 google 结果),在“编组”标题下的第一个要点中,紧凑框架没有正确编组 SAFEARRAY。

我试图通过操纵这个 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 的指针得到可能是完全虚假的,它已经被遗忘了。

关于我在这些定义中可能搞砸的任何建议,或对解决此问题的其他方法的建议?

提前致谢...

4

1 回答 1

1

好吧,我已经解决了这个问题。
希望我的回答能帮助遇到同样问题的其他人。我遇到的问题是 COM tlb 声明中的 [out] 标记意味着我传入的任何内容都将被在 COM 库中创建的对象覆盖。经典(也是非常基本的问题)“按引用传递与按值传递”的相当复杂的版本

因此,正确的编组是使用我在上面发布的 SafeArray 的定义。

不要触摸 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);
于 2010-11-10T16:54:00.983 回答