客户端获取从C ++ COM DLL返回的字节数组

时间:2012-08-23 08:42:14

标签: c# c++ com-interop

我在C ++ COM头文件和IDL文件中有这个声明:

//Header file:
#define MAX_LENGTH      320
typedef BYTE            PRE_KEY [MAX_LENGTH];

//IDL file:
#define MAX_COUNT       10
HRESULT Save([in] DWORD dwCommand, [in]float fdata[MAX_COUNT], [out] PRE_KEY* phKey);

这是C#客户端代码:

//After C# interop compilation, the method's signature in C# becomes:
Save(uint dwCommand, float[] fdata, out byte[] phKey);

//The code to call the C++ COM server:    
uint dwCommand = 2;
float[] fdata = new float[dwCommand];

fdata[0] = 1; 
fdata[1] = 2;

byte[] phKey = new byte[320];

save(dwCommand, fdata, out phKey);

在调用返回C#之前,代码将在ntdll.dll中崩溃,但C ++服务器已经完成处理并且不再在堆栈中。

任何人都可以弄清楚如何解决此问题?因为我正在使用互操作编译来编译idl文件以生成C#signaure,所以我无法在C ++ IDL文件中执行某些操作并手动更改C#签名。

有趣的是,我有另一个类似的调用,它返回从C ++到C#的完全相同的phKey,它完美地工作。唯一的区别在于,调用phKey在一个结构中,整个结构是一个'[out]'参数。真的不明白为什么这可以在一个结构中返回,但不能直接作为一个参数。

1 个答案:

答案 0 :(得分:1)

IDL声明中的[out]属性是一个严重的互操作问题。这意味着您的COM服务器将分配数组,调用者需要释放它。这很少有好结果,无法保证您的服务器和客户端使用相同的堆。当你将C运行时分配器与malloc()函数或new []运算符一起使用时,它总是会失败,CRT使用它自己的私有堆,调用者永远无法访问它,除非它们共享完全相同的CRT版本。赔率非常小,一般情况下这种情况,当您通过CLR进行互操作时为零。

这就是它爆炸的原因,CLR知道它需要在将数据复制到托管数组后释放它。它将使用CoTaskMemFree(),使用为COM互操作分配保留的堆。当然你没有使用CoTaskMemAlloc()来分配数组。

这个问题的一般解决方案是让调用者提供数组,被调用者填充它。这需要参数的[in,out]。还有一个额外的参数,用于指示传递数组的大小,[sizeis]告诉编组人员。效率很高,无需分配。使用自动SAFEARRAY类型可以避免必须指定额外的参数,CLR知道该类型。