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# 签名。

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

4

1 回答 1

1

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

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

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

于 2012-09-08T14:08:03.560 回答