2013-03-05 140 views
0

我试图实现一些涉及托管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对象的解决方案,但由于可移植性问题,我必须避免使用它。

在此先感谢。

回答

4

Method_Unknown_Size的参数是double*,您将更改参数本身。如果你想改变主叫方发送原始值,你应该定义的参数作为指向数组的指针这意味着指针的双引用指针的双

指针,你也应该在某种程度上,告诉调用者数组的大小(我想你已经管理过了)。

C++:

int method_Unknown_Size(double *&array) 
{ 
    int size = 9; 
    array = new double[size]; 
    for(int i=0; i<size; i++) array[i] = i; 
    return size; 
} 

void FreeArray(double *&p) 
{ 
    delete[] p; 
    p = NULL; 
} 

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); 
} 

C#:

[DllImport("cObject.dll")] 
private static extern int Run_Unknown_Size(IntPtr pObject, 
     out IntPtr arr_not_allocated); 

[DllImport("cObject.dll")] 
private static extern int Release(IntPtr pObject, 
     ref IntPtr arr_not_allocated); 


// to allocate the array: 
IntPtr pArr; 
int res2 = Run_Unknown_size(m_pNativeObject, out pArr); 

// to free the array: 
Release(m_pNativeObject, ref pArr); 

这绝对有效!告诉我,如果它没有!

+0

非常感谢您的快速回复。我的工作机器上有这样的代码,所以明天我一定会检查出来。我试过双指针,但方式稍有不同。我希望这会起作用!只是一个简单的问题:在C#部分你把属性'[Out] out'。这是什么意思? – 2013-03-05 07:48:36

+0

不客气。我猜这里不需要'[Out]',但是我确信它是确定的。它告诉编组人员将更改放回到变量中,但它仅适用于对象和数组的内容。如果对象*本身*改变,'[Out]'不会帮助!这里是'out'参数修饰符的用途。它使参数成为*参考*(或*指针*)。有关更多信息,请参见[这里](http://msdn.microsoft.com/zh-cn/library/ee332485.aspx)。 – 2013-03-05 07:52:58

+0

这很有意思。这是我的第一个涉及托管/非托管代码的项目,您绝对会睁开眼睛看看一些在网络或教科书中不容易找到的东西。再次感谢! – 2013-03-05 07:55:30

相关问题