问题描述
情况:我有一个托管(C#,.NET 2.0)应用程序,该应用程序使用P/Invoke使用非托管(C ++)DLL.与简单"方法(POD参数/返回值)一起,还需要将boost :: variant值数组传递给代码.原因是这些方法传递报告数据(类似于Excel单元格,可以是任何类型). C#代码将它们接受为装箱的对象".
Situation:I have a managed (C#, .NET 2.0) application which uses an unmanaged (C++) DLL using P/Invoke. Along with the "simple" methods (POD arguments/return value) there's a requirement to pass a boost::variant value arrays to the code. The reason for it is that these methods pass report data (similar to Excel cells, which can be of any type). C# code accepts them as boxed "object"'s.
以前的实现要求使用COM VARIANT的SafeArray.但是,由于编码不佳/未对其进行测试,因此编组被证明是内存泄漏.现在,我必须找到封送数据的另一种选择.
The previous implementation called for use of SafeArray of COM VARIANT's. However, due to poor coding/not testing it, the marshalling turned out to be leaking memory. Now I have to find another option for marshalling the data.
先前的实现如下所示: C ++:
Previous implementation looked like this: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[];
此特定实现通过不释放互操作结构来泄漏内存.
This specific implementation leaks memory by not freeing up the interop structures.
我试图通过包含SafeArray编组指令来更改原型:
I've tried to change the prototypes by including SafeArray marshalling directives:
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);
但是,我唯一要做的就是要么得到一个空的结果对象,要么由于内存损坏/堆栈溢出而崩溃.
However, the only things I've achieved were either an empty resulting object(s), or crash due to memory corruption/stack overflow.
问题:如何正确地将此类数据类型(VARIANT类型的结构的数组)编组到C#?我可以使C ++ DLL成为COM之一,但这将需要重写大量代码.有没有更简单的方法可以解决这种情况?也许我想念一些东西.
Problem:How to correctly marshal such data type (array of VARIANT-typed structures) to C#?I can make the C++ DLL a COM one, but this will require rewriting quite a handful of code. Is there any simpler way out of the situation? Maybe I'm missing something.
推荐答案
有以下示例:"> http://limbioliong.wordpress.com/2011/03/20/c-interop-how-to-return-a-来自非托管功能的变体/
最后,他们直接使用IntPtr
(它们将其用作返回值,您必须将其用作out IntPtr
),然后从C#中使用Marshal.GetObjectForNativeVariant()
,VariantClear()
和Marshal.FreeCoTaskMem()
方面,而在C/C ++方面,VARIANT
被分配了CoTaskMemAlloc()
.
In the end they use directly an IntPtr
(they use it as a return value, you would have to use it as a out IntPtr
), then Marshal.GetObjectForNativeVariant()
, VariantClear()
and Marshal.FreeCoTaskMem()
from the C# side, while on the C/C++ side the VARIANT
was allocated with 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;
很显然,您可以在dll中公开另一个C函数,以释放VARIANT
(在库中公开Free
方法始终是正确的,以便调用者可以使用它们,而不会问自己我应该如何释放此内存" ?")
Clearly you could expose another C function in your dll that frees the VARIANT
(it's always correct to expose Free
methods in your library, so that caller can use them and not ask himself "how should I free this memory?")
这篇关于使用P/Invoke整理VARIANT数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!