我在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]'参数。真的不明白为什么这可以在一个结构中返回,但不能直接作为一个参数。
答案 0 :(得分:1)
IDL声明中的[out]属性是一个严重的互操作问题。这意味着您的COM服务器将分配数组,调用者需要释放它。这很少有好结果,无法保证您的服务器和客户端使用相同的堆。当你将C运行时分配器与malloc()函数或new []运算符一起使用时,它总是会失败,CRT使用它自己的私有堆,调用者永远无法访问它,除非它们共享完全相同的CRT版本。赔率非常小,一般情况下这种情况,当您通过CLR进行互操作时为零。
这就是它爆炸的原因,CLR知道它需要在将数据复制到托管数组后释放它。它将使用CoTaskMemFree(),使用为COM互操作分配保留的堆。当然你没有使用CoTaskMemAlloc()来分配数组。
这个问题的一般解决方案是让调用者提供数组,被调用者填充它。这需要参数的[in,out]。还有一个额外的参数,用于指示传递数组的大小,[sizeis]告诉编组人员。效率很高,无需分配。使用自动SAFEARRAY类型可以避免必须指定额外的参数,CLR知道该类型。