3

情况: 我有一个使用 P/Invoke 的非托管 (C++) DLL 的托管(C#、.NET 2.0)应用程序。除了“简单”方法(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 的,但这需要重写相当多的代码。有没有更简单的方法摆脱这种情况?也许我错过了一些东西。

4

1 回答 1

1

有这个例子: http: //limbioliong.wordpress.com/2011/03/20/c-interop-how-to-return-a-variant-from-an-unmanaged-function/

最后,他们直接使用 an IntPtr(他们将其用作返回值,您必须将其用作 a out IntPtr),然后Marshal.GetObjectForNativeVariant()VariantClear()C Marshal.FreeCoTaskMem()# 端使用 ,而在 C/C++ 端VARIANT则使用CoTaskMemAlloc().

[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;

显然,您可以在您的dllVARIANT中公开另一个 C 函数以释放Free

于 2013-09-10T10:35:46.627 回答