我试图实现一些涉及托管C#和非托管C++代码之间的数组编组的项目。我面临一个问题,我在网上找到的解决方案似乎没有任何工作。对于这方面的意见,我将不胜感激。在C#中传递IntPtr指针后分配非托管C++代码中的数组,编组类型
我没有提供完整的代码,但非常简化的部分显示了这个问题。虽然它看起来像一大块 - 它很简单 - 只是概念。只是想给尽可能多的全貌。
C++部分:
Object.h
class cObject
{
public:
//...constructor, destructor...
int Method_Known_Size(double* array, int size);
int Method_Unknown_Size(double* array);
...
void FreeArray(double* p);
}
Object.cpp
int Method_Known_Size(double* array, int size)
{
//modify array somehow..
for(int i=0; i<size; i++) array[i] = i;
}
int method_Unknown_Size(double* array)
{
int size = 9;
array = new double[size];
for(int i=0; i<size; i++) array[i] = i;
}
(跳过Caller.h) Caller.cpp
//...callers for constructor, destructor, for releasing unmanaged memory...
extern "C" int __stdcall Run_Known_Size(cObject* pObject, double* array, int size)
{
return cObject->Method_Known_Size(array, size);
}
extern "C" int __stdcall Run_Unknown_Size(cObject* pObject, double* array)
{
return cObject->Method_Unknown_Size(array);
}
extern "C" void __stdcall Release(cObject* cObject, double* array)
{
if(cObject != NULL) cObject->FreeArray(array);
}
所以,基本上Run_Known_Size
方法只是会修改已经被C#的内存分配,并且Run_Unknown_Size
创建数组并修改它。
C#部分
public class DllWrapper: IDisposable
{
/* Creating an object, disposing,...
[DllImport("cObject.dll")]
CreateObject();...DisposeObject(IntPtr pObject);
...CallFreeArray(IntPtr pArray);*/
[DllImport("cObject.dll")]
private static extern int CallRun_Known_Size(IntPtr pObject,
[Out] double [] arr_allocated, int size);
[DllImport("cObject.dll")]
private static extern int CallRun_Unknown_Size(IntPtr pObject,
[Out] IntPtr arr_not_allocated);
private IntPtr m_pNativeObject;
public DllWrapper()
{
this.m_pNativeObject = CreateObject();
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool bDisposing)
{
if (this.m_pNativeObject != IntPtr.Zero)
{
DisposeObject(this.m_pNativeObject);
this.m_pNativeObject = IntPtr.Zero;
}
if (bDisposing)
{
GC.SuppressFinalize(this);
}
}
~DllWrapper()
{
Dispose(false);
}
public void ReleaseUnmanAraray(IntPtr pArr)
{
CallFreeArray(pArr);
}
public int Run_Known_Size(double[] arr_allocated, int size)
{
return CallRun_Known_Size(this.m_pNativeObject, arr_allocated, size);
}
public int Run_Unknown_Size(IntPtr arr_not_allocated)
{
return CallRun_Known_Size(this.m_pNativeObject, arr_not_allocated);
}
}
static void Main(string[] args)
{
double[] alloc_arr = new double[] { 1, 5, 3, 3, 5, 5, 8, 9,1 };
int size = 9;
double[] Arr_for_Copy = new double[size];
IntPtr pArr = new IntPtr();
DllWrapper wrapper = new DllWrapper();
int res1 = Run_Known_Size(alloc_arr, size);
int res2 = Run_Unknown_size(pArr);
if (pArr != IntPtr.Zero) // pArr IS ZERO ALWAYS!!!!!!
{
Marshal.Copy(pArr, Arr_for_Copy, 0, size);
}
else
{
Console.WriteLine("Pointer was zero again");
}
wrapper.ReleaseUnmanAraray(pScores);
wrapper.Dispose();
Console.ReadLine();
}
一切正常,只是在C#中分配的数组罚款 - 他们来自C++修改没有任何错误。但是,如果我不知道数组的大小,因此无法预分配数组,所以我发现的唯一解决方案是传递[Out] IntPtr并让C++管理内存,分配和修改数组。然后返回IntPtr可以编组到C#的double []数组,因为我们已经知道了大小(为了简化,我只是把数字4作为大小,但是我通过int *大小来确定大小)。
在传递IntPtr并根据此指针在C++中创建数组之后,所有的试验都会以零指针结束(无错误)。
我见过涉及COM对象的解决方案,但由于可移植性问题,我必须避免使用它。
在此先感谢。
非常感谢您的快速回复。我的工作机器上有这样的代码,所以明天我一定会检查出来。我试过双指针,但方式稍有不同。我希望这会起作用!只是一个简单的问题:在C#部分你把属性'[Out] out'。这是什么意思? – 2013-03-05 07:48:36
不客气。我猜这里不需要'[Out]',但是我确信它是确定的。它告诉编组人员将更改放回到变量中,但它仅适用于对象和数组的内容。如果对象*本身*改变,'[Out]'不会帮助!这里是'out'参数修饰符的用途。它使参数成为*参考*(或*指针*)。有关更多信息,请参见[这里](http://msdn.microsoft.com/zh-cn/library/ee332485.aspx)。 – 2013-03-05 07:52:58
这很有意思。这是我的第一个涉及托管/非托管代码的项目,您绝对会睁开眼睛看看一些在网络或教科书中不容易找到的东西。再次感谢! – 2013-03-05 07:55:30