情况: 我有一个托管(C#,. NET 2.0)应用程序,它使用P / Invoke使用非托管(C ++)DLL。除了“简单”方法(POD参数/返回值)之外,还需要将boost :: variant值数组传递给代码。原因是这些方法传递报告数据(类似于Excel单元格,可以是任何类型)。 C#代码将它们作为盒装“对象”接受。
之前的实现要求使用COM VARIANT的SafeArray。然而,由于编码不好/没有测试它,编组结果是泄漏记忆。现在我必须找到另一个用于编组数据的选项。
以前的实现看起来像这样: 的 C ++:
extern "C" __declspec(dllexport) void GetReport(VARIANT& output) {
// ... here a SafeArray of VARIANT values was created
output.vt = VT_VARIANT | VT_ARRAY;
output.parray = safeArray;
}
C#
[DllImport("CppLibrary.dll")]
private static extern void GetReport(out object output);
//....
object data;
GetReport(data);
object rows = data as object[];
这个特定的实现通过不释放互操作结构来泄漏内存。
我试图通过包含SafeArray编组指令来改变原型:
C ++
extern "C" __declspec(dllexport) void GetReport(SAFEARRAY output) { // Also tried SAFEARRAY*, SAFEARRAY&, VARIANT, VARIANT&, VARIANT*
// Internal stuff
}
C#
private static extern void GetReport([Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)]ref object[] output);
然而,我唯一能做到的就是空结果对象,或因内存损坏/堆栈溢出而崩溃。
问题: 如何正确地将这种数据类型(VARIANT类型结构的数组)封送到C#? 我可以使C ++ DLL成为COM,但这需要重写相当少量的代码。有没有更简单的方法摆脱这种情况?也许我错过了什么。
答案 0 :(得分:1)
最后,他们直接使用IntPtr
(他们将其用作返回值,您必须将其用作out IntPtr
),然后Marshal.GetObjectForNativeVariant()
,{{1}来自C#侧的{和} VariantClear()
,而在C / C ++端,Marshal.FreeCoTaskMem()
分配了VARIANT
。
CoTaskMemAlloc()
显然你可以在你的dll中暴露另一个C函数来释放[DllImport("MyDLL.dll", CallingConvention = CallingConvention.StdCall)]
static extern void MyFunction(out IntPtr ptr);
[DllImport("oleaut32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern Int32 VariantClear(IntPtr pvarg);
IntPtr pVariant;
MyFunction(out pVariant);
object objRet = Marshal.GetObjectForNativeVariant(pVariant);
VariantClear(pVariant);
Marshal.FreeCoTaskMem(pVariant);
pVariant = IntPtr.Zero;
(在你的库中公开VARIANT
方法总是正确的,这样调用者就可以使用它们而不是问自己“应该怎么做我释放了这段记忆?“)