Search code examples
c#.netunsafe

Unsafe C#: How do I copy data from IntPtr to byte*?


In my (unsafe) .Net app I have an IntPtr referencing a block of pixel data in a Bitmap (part of a third party Image class). I also have a struct which contains a fixed byte array with enough size to contain 8*1024*1024 bytes of image data.

(The code snippet shows a reduced version of the struct that exists in shared memory.)

I've see in this post (How can I pin an array of byte?) where a solution is provided for a similar question, but it works for byte[], where in this case the compiler thinks my "ImageData" field is a byte*. And of course it will not let me cast or convert between byte* and byte[], safe or unsafe.

So, how can I get data copied from my IntPtr (or any other array) into my MyDecoder->ImageData?

(The ultimate goal, with or without using an intermediate IntPtr, is to copy managed (RGBA) pixel data to an unmanaged byte* location.)

[StructLayout(LayoutKind.Explicit)]
unsafe public struct SharedMemMPEG2Decode
{
    [FieldOffset(0)] public UInt32 Width; // Width of the image
    [FieldOffset(4)] public UInt32 Height; // Height of the image
    [FieldOffset(8)] public UInt32 FourCC; // Data format identifier 
    [FieldOffset(12)] public UInt32 ImageSize; // Size of the image data in bytes
    // ... 
    [FieldOffset(16896)] public unsafe fixed byte ImageData[8 * 1024 * 1024];
}
// ...
// This pointer is initialized with MemoryMappedViewAccessor.SafeMemoryMappedViewHandle.AcquirePointer():
SharedMemMPEG2Decode *MyDecoder; 
// ...
IntPtr xfer_buffer = Marshal.AllocHGlobal(8 * 1024 * 1024); 
// ...
// Not shown: Copy of pixel data from 3rd party Image class to the above buffer
Marshal.Copy(xfer_buffer, MyDecoder->ImageData, 0, image_size_bytes); // b0rken! Won't compile

Solution

  • First off, you are of course required to ensure that both the source and destination are not going to be moved by the garbage collector; I assume you can do so correctly.

    To efficiently do the memory copy:

    • cast the IntPtr to byte*
    • obtain a byte* from the fixed-size array using the fixed statement. (Yes it is unfortunate that C# uses fixed to mean both "fixed in size" and "fixed in place")
    • Now you have two byte*s and you can use System.Buffer.MemoryCopy.

    See https://learn.microsoft.com/en-us/dotnet/api/system.buffer.memorycopy?view=netframework-4.7.2 for details.