Search code examples
c#.netinteropintptr

Tell if IntPtr points to managed or unmanaged memory


I'm using a wrapped C-library in C# and need to convert an image from that library to a Bitmap and back, but without copying the pixel buffer.

Converting to a Bitmap was simple:

Bitmap WrapAsBitmap(CImage image)
{
    return new Bitmap(image.Width, image.Height, image.BytesPerLine, PixelFormat.Format24bppRgb, image.Data);
}

By just passing in the original pixel buffer (image.Data) to the Bitmap constructor, no copy is needed. For doing the opposite, I need to call LockBits and UnlockBits to get access to the IntPtr of the original pixel buffer:

CImage WrapAsCImage(Bitmap bitmap)
{
    BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
    var image = new CImage(bitmap.Width, bitmap.Height, data.Stride * 3, data.Scan0);
    bitmap.UnlockBits(data);
    return image;
}

Now, as you can see, if data.Scan0 is in managed memory, this is actually a dangerous approach as the bits are 'unlocked' before the newly constructed image will be used. If bitmap was constructed from the WrapAsBitmap method, then there is no problem as data.Scan0 points to unmanaged memory which will not be moved by .NET. Therefore, I want to build in a safety check that checks the location (managed/unmanaged) of the data.Scan0 pointer.

An other solution I was thinking of was to inherit from the Bitmap class and keep track of the original pointer image.Data along with the Bitmap, but the Bitmap class is sealed, so I'm out of luck.

In short: any ideas how to detect the location (managed/unmanaged) of an IntPtr?


Solution

  • There is no scenario where that IntPtr is ever going to point to managed memory. Not when the image data was generated by C code of course. Not when it was generated by Bitmap either, GDI+ is unmanaged code. You can reason this through by yourself, you can only ever obtain an IntPtr for managed memory by pinning it. GCHandle.AddrOfPinnedObject(). A hard requirement to prevent the garbage collector from invalidating the pointer when it compacts the heap. Nowhere do you see a documented need to pin.

    There is no point in implementing the test.

    You do have the rock-hard requirement to keep that IntPtr valid for the lifetime of the Bitmap wrapper. Up to the point where you call its Dispose() method. You cannot make this automagic, other than by encapsulating the Bitmap object in a class that implements IDisposable itself. Not doing so causes very ugly mis-behavior, you only get an AccessViolationException when you're lucky. A corrupted bitmap with random pixel content is the normal failure mode.

    If you can't implement that guarantee then you should not do this and copy the bitmap data instead. It is not hard to implement, just not pretty.