Search code examples
.netvisual-c++c++-clinativemixed-mode

Access .NET ushort[] array as uint16_t* without copying


When converting ushort[] from c# to c++/cli to c++ do I have to copy all the data? or can I cast the pointer?

PreprocessorWrapper::Function1(ImageData^ rawImage)
{
    array<unsigned short>^ rawDataTemp = rawImage->RawData;
    bayer_raw_image* inputImage = new bayer_raw_image();
    inputImage->raw_data = (uint16_t*)rawDataTemp; //error is here
}

here is the error i'm getting

error C2440: 'type cast' : cannot convert from 'cli::array<Type> ^' to 'uint16_t 

this is the C# class

public class ImageData
    {
       public int WidthColumns { get; set; }
        public int HeightLines { get; set; }
        public ushort[] RawData { get; set; }
        public ushort BitDepth { get; set; }
    }

c++ class

typedef struct
{
    uint16_t width;
    uint16_t height;
    uint16_t* raw_data;
    uint16_t bit_depth;
} bayer_raw_image;

Solution

  • No, you don't have to, but you'll have to pin it, as the GC is allowed to move managed objects in memory during a garbage collection.

    array<uint16_t>^ rawDataTemp = rawImage->RawData;
    pin_ptr<uint16_t> rawDataPinned = &rawDataTemp[0];
    bayer_raw_image* inputImage = new bayer_raw_image();
    inputImage->raw_data = rawDataPinned;
    

    You should not make use of the pointer after rawDataPinned goes out of scope, as the data will no longer be pinned and will be subject to being moved around by the GC compaction. That's a nasty bug waiting to happen.

    If you need long-term pinning, use a pinned GCHandle:

    GCHandle::Alloc(rawImage->RawData, GCHandleType::Pinned)
    

    It provides a void* to the first array item through handle->AddrOfPinnedObject().ToPointer().
    Don't forget to Free() it after use.

    This is more heavyweight than pin_ptr as it needs to register a handle in the GC, whereas pin_ptr will only be noticed by the GC if a collection takes place while your object is pinned - it's as light as you can get.