0

我正在尝试实现一些涉及托管 C# 和非托管 C++ 代码之间的编组数组的项目。我面临一个问题,我在网上找到的解决方案似乎都不起作用。对于这方面的任何评论,我将不胜感激。

我没有提供完整的代码,而是显示问题的非常简化的部分。虽然它看起来像一个大块 - 它非常简单 - 只是概念性的。只是想提供尽可能多的全貌。

C++部分:

对象.h

class cObject
{
public:
    //...constructor, destructor...
    int Method_Known_Size(double* array, int size);
    int Method_Unknown_Size(double* array);
    ...
    void FreeArray(double* p);
}

对象.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* size 来确定大小)。

在传递 IntPtr 并基于此指针在 C++ 中创建数组后,我所有的试验都以零指针(没有错误)结束。

我见过涉及 COM 对象的解决方案,但由于可移植性问题,我必须避免使用它。

提前致谢。

4

1 回答 1

5

The parameter of Method_Unknown_Size is double* and you are changing the parameter itself. If you want to change the original value send by the caller, you should define the parameter as a pointer to an array which means pointer to pointer of double or reference to pointer of double

You should also in some way, tell the caller the size of the array (I guess you managed that already).

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

This absolutely works! Tell me if it didn't!

于 2013-03-05T07:42:10.137 回答