从C++ COM dll返回一个字节数组给客户端

4

我有这个声明在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#签名,因此不能在C++ IDL文件中做些什么并手动更改C#签名。

有趣的是,我还有另一个类似的调用,它返回完全相同的phKey从C++到C#,并且它完美地工作。唯一的区别就是在那个调用中,phKey在一个结构体中,并且整个结构体是一个“[out]”参数。真的看不出为什么可以在结构体中返回它,但不能直接作为参数返回。

1个回答

1

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

这就是为什么它会出错,CLR知道它需要在将其复制到托管数组后释放数组。它将使用CoTaskMemFree(),使用专门为COM互操作分配保留的堆。您肯定没有使用CoTaskMemAlloc()来分配数组。

解决此问题的一般方法是让调用者提供数组,被调用方填充它。这需要在参数上使用[in,out],并添加一个额外的参数来指示传递的数组的大小,[sizeis]告诉编组程序有关它的信息。非常高效,不需要分配。使用自动化SAFEARRAY类型避免了必须指定该额外参数的情况,CLR知道该类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接