简体   繁体   English

在C#,封送处理类型中传递IntPtr指针后,在非托管C ++代码中分配数组

[英]Allocating array in unmanaged C++ code after passing IntPtr pointer in C#, Marshaling Types

I'm trying to implement some project involving marshaling arrays between managed C# and unmanaged C++ codes. 我正在尝试实现一些涉及托管C#和非托管C ++代码之间的封送处理数组的项目。 I'm facing a problem and none of the solutions found by me on web seem to work. 我遇到了一个问题,我在网上找到的所有解决方案似乎都无效。 I would greatly appreciate any comments on this regard. 我对此表示感谢。

I'm not presenting the full code, but very much simplified part showing the issue. 我没有提供完整的代码,而是显示了问题的非常简化的部分。 Although it looks like a large piece - it is very simple - just conceptual. 尽管它看起来很大,但非常简单-只是概念上的。 Just wanted to give as much full picture as possible. 只是想提供尽可能多的全貌。

C++ part: 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 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;
}

(skipping Caller.h) Caller.cpp (跳过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);
}

So, basically Run_Known_Size method just modifies already allocated by C# memory, and Run_Unknown_Size creates the array and modifies it. 因此,基本上Run_Known_Size方法只是修改C#内存已分配的值,而Run_Unknown_Size创建并修改数组。

C# part 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();
}

Everything works just fine with arrays allocated in C# - they come modified from C++ with no errors. 使用C#分配的数组,一切都可以正常工作-它们是从C ++修改而来的,没有错误。 But in case when I don't know the size of an array, therefore could not preallocate arrays, the only solution that I found is to pass [Out] IntPtr and let C++ manage the memory, allocate and modify array. 但是,如果我不知道数组的大小,因此无法预分配数组,我发现的唯一解决方案是传递[Out] IntPtr并让C ++管理内存,分配和修改数组。 Then returned IntPtr can be marshaled to C#'s double[] array because we already know the size (for simplification I just put number 4 as the size, but I pass int* size to determine the size). 然后返回的IntPtr可以编组到C#的double []数组中,因为我们已经知道大小了(为简单起见,我只将数字4用作大小,但是我通过int * size来确定大小)。

All my trials end up with zero pointer (with no error) after passing IntPtr and creating array in C++ based on this pointer. 在传递IntPtr并基于此指针在C ++中创建数组之后,我所有的试验都以零指针(无错误)结束。

I've seen solutions involving COM objects but I have to avoid using that because of portability issues. 我已经看到了涉及COM对象的解决方案,但是由于可移植性问题,我不得不避免使用该解决方案。

Thanks in advance. 提前致谢。

The parameter of Method_Unknown_Size is double* and you are changing the parameter itself . Method_Unknown_Size的参数为double*并且您正在更改参数本身 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 如果要更改调用方发送的原始值,则应将参数定义为指向数组指针,这意味着指向double指针的指针 double指针的 引用

You should also in some way, tell the caller the size of the array (I guess you managed that already). 您还应该以某种方式告诉调用方数组的大小(我想您已经对此进行了管理)。

C++: 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#: 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! 告诉我是否没有!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM