none
C++/CLI Get data into a Jagged Array from a Native function RRS feed

  • Question

  • Hello, this is my first time posting a question so please excuse (and feel free to correct) any posting faux pas.

    I have a native C++ method that looks like this:

    void MyClass::fillJaggedArray(int flag, int nSamples, int * data[]);

    Where nSamples is the number of elements in each sub array (the sub arrays are not necessarily next to each other, they are each contiguous, but taken as a whole the 2-D array is not contiguous in memory.) The native class fills the array of arrays with data.  Objects of the class know how many sub arrays to fill.

    I am trying to get the data into a .Net array<array<int>^>^. I am currently using what is suggested in this post:

    Pinning an array of pointers ( jagged array ) for native interop

    But the person asking the question knew alot more about this than I did and left out alot of details.

    In C++/CLI I have a wrapper class with this method:

    void MyDotNetClass::fillJaggedArray(int flags, int nSamples, array<array<int>^>^ data) {
        int nSubArrays = nativeClassPtr->getNSubArrays();
        if (data->Length != nSubArrays) {
            throw gcnew MyRuntimeException("Jagged data array has bad number of subarrays");
        }
        if (data[0]->Length < nSamples) {
            throw gcnew MyRuntimeException("Number of elements in each array must be >= nSamples");
        }
        int ** pData = new int*[nSubArrays];
        array^ hData = gcnew array(nSubArrays);
        for (int i = 0; i < nSubArrays; ++i ) {
            hData[i] = GCHandle::Alloc(data[i], GCHandleType::Pinned);
            pData[i] = (int*)(GCHandle::ToIntPtr(hData[i]).ToPointer());
        }    // fill the data array using the native function call
        try {
            nativeClassPtr->fillJaggedArray(flags, nSamples, pData);
        } catch (runtime_exception & ex) {
            // convert native exception to .net
            throw gcnew MyRuntimeException(marshal_as(ex.what()));
        }
    
    // At this point I expect data[0] and data[1] to have the data, but they don't
    
        for (int i = 0; i < nSubArrays; ++i) {
    	hData[i].Free();
        }
        delete[] pData;
    }

    Note that I have edited this code to be generic, I may have made a typo, but the actual code looks basically just like this and compiles fine. 

    I am calling the method from a C# test program like this:

    const int N_SAMPLES = 1024;
    int[] subArray1 = new int[N_SAMPLES];
    int[] subArray2 = new int[N_SAMPLES];
    int[][] data = { subArray1, subArray2 };
    myObject.fillJaggedArray(0, N_SAMPLES, data);
    Console.WriteLine("data:");
    for (int i = 0; i < 32; ++i) {
        Console.WriteLine("[" + subArray1[i] + ", " + subArray2[i] + "]");
    }

    After the call to the native method pData[0] and pData[1] have the correct data, but data does not.  I thought that since pData is a native version of hData, and hData[0] and hData[1] are pinned pointers that point to the same place as data[0] and data[1] that writing to pData should write to data. I think this method works for getting data into the native method, but I guess I am missing a degree of indirection to get the data out.  I don't mind using marshalling calls or other funny syntax but I need to be able to avoid copying the data.

    If anyone can help me get the data out into the calling code that would be great.

    Thank you,

    --jeremy









    • Edited by Jeremy Sorensen Tuesday, February 14, 2012 1:13 AM I think this editor converts my template code to html and removes it occasionally
    • Moved by Helen Zhao Wednesday, February 15, 2012 2:57 AM (From:Visual C++ Language)
    Monday, February 13, 2012 5:47 PM

Answers

  • So after my post about a single array but using GCHandle, I tried it myself and figured out what I was doing wrong.  Here is the fixed code, note the use of AddrOfPinnedObject instead of toIntPtr.

    So if anyone else wants to know how to pin a 2-D jagged array to fill it using native code here you go:

    void MyDotNetClass::fillJaggedArray(int flags, int nSamples, array<array<int>^>^ data) {
        int nSubArrays = nativeClassPtr->getNSubArrays();
        if (data->Length != nSubArrays) {
            throw gcnew MyRuntimeException("Jagged data array has bad number of subarrays");
        }
        if (data[0]->Length < nSamples) {
            throw gcnew MyRuntimeException("Number of elements in each array must be >= nSamples");
        }
        int ** pData = new int*[nSubArrays];
        array^ hData = gcnew array(nSubArrays);
        for (int i = 0; i < nSubArrays; ++i ) {
            hData[i] = GCHandle::Alloc(data[i], GCHandleType::Pinned);
            pData[i] = (int*)(hData[i].AddrOfPinnedObject().ToPointer());
        }    // fill the data array using the native function call
        try {
            nativeClassPtr->fillJaggedArray(flags, nSamples, pData);
        } catch (runtime_exception & ex) {
            // convert native exception to .net
            throw gcnew MyRuntimeException(marshal_as<String ^>(ex.what()));
        }
    
    // At this point data[0] and data[1] have the data
    
        for (int i = 0; i < nSubArrays; ++i) {
    	hData[i].Free();
        }
        delete[] pData;
    }



    Jeremy Sorensen

    Tuesday, February 21, 2012 11:08 PM

All replies

  • Hi Jeremy,

    Welcome to the MSDN Forum.

    According to your description, I'd like to move this thread to "Common Language Runtime Forum" for better support, where more experts live.

    Thanks for your understanding.
    Best regards,


    Helen Zhao [MSFT]
    MSDN Community Support | Feedback to us


    • Edited by Helen Zhao Wednesday, February 15, 2012 2:57 AM
    Wednesday, February 15, 2012 2:56 AM
  • By way of additional information; I currently have a maximum of 8 subarrays, so as a stop-gap measure I use 8 pin_pointer instances and a big switch to decide how many to set pointing to a sub-array.  This works and I get data out, so if someone could tell me how to do the equivalent of pinning a single array but using GCHandle::Alloc instead of pin_ptr, such that the native code fills in the managed (and pinned) array, I bet I could figure it out for a jagged array.

    Thanks,

    --jeremy


    Jeremy Sorensen


    • Edited by Jeremy Sorensen Tuesday, February 21, 2012 10:01 PM corrected grammer mistake
    Tuesday, February 21, 2012 10:01 PM
  • So after my post about a single array but using GCHandle, I tried it myself and figured out what I was doing wrong.  Here is the fixed code, note the use of AddrOfPinnedObject instead of toIntPtr.

    So if anyone else wants to know how to pin a 2-D jagged array to fill it using native code here you go:

    void MyDotNetClass::fillJaggedArray(int flags, int nSamples, array<array<int>^>^ data) {
        int nSubArrays = nativeClassPtr->getNSubArrays();
        if (data->Length != nSubArrays) {
            throw gcnew MyRuntimeException("Jagged data array has bad number of subarrays");
        }
        if (data[0]->Length < nSamples) {
            throw gcnew MyRuntimeException("Number of elements in each array must be >= nSamples");
        }
        int ** pData = new int*[nSubArrays];
        array^ hData = gcnew array(nSubArrays);
        for (int i = 0; i < nSubArrays; ++i ) {
            hData[i] = GCHandle::Alloc(data[i], GCHandleType::Pinned);
            pData[i] = (int*)(hData[i].AddrOfPinnedObject().ToPointer());
        }    // fill the data array using the native function call
        try {
            nativeClassPtr->fillJaggedArray(flags, nSamples, pData);
        } catch (runtime_exception & ex) {
            // convert native exception to .net
            throw gcnew MyRuntimeException(marshal_as<String ^>(ex.what()));
        }
    
    // At this point data[0] and data[1] have the data
    
        for (int i = 0; i < nSubArrays; ++i) {
    	hData[i].Free();
        }
        delete[] pData;
    }



    Jeremy Sorensen

    Tuesday, February 21, 2012 11:08 PM