What you're doing definitely isn't safe. Why are you doing this? Is there a reason you're so comfortable leaving the safe, managed environment?
The bitmap is created around that byte[]
. This is okay as long as you don't mind having a pinned byte[]
in the managed memory (okay for a few moments, not really for the duration of the application etc.). However, on the very next line, you release the pointer!
Then you use the same byte[]
, modify it, and use it for another bitmap. Boom, it's still the same byte array. It shouldn't be surprising that both bitmaps have the same content - you asked for that.
The reason why it sometimes works and sometimes it doesn't is that if the handle isn't moved by the GC, both bitmaps will be correct. However, if the GC moves the byte array, the Bitmaps have no way of adjusting - they will still point to the same location in memory (which is now wrong).
What you have to understand is that a GCHandle doesn't create a new object. It just instructs the GC not to mess with the physical location (well, in virtual memory, but...) as long as the GCHandle exists. If you want to create a new object, do something like byte[].Clone()
. However, you're still going to have to have the handle pinned for all the lifetime of the Bitmap, which is usually a bad idea. Instead, try creating the Bitmap the usual way, then doing Bitmap.LockBits
, then use Marshal.Copy
to copy the bitmap array to the unmanaged memory of the Bitmap, and you're done, nice and relatively safe.
Here's a code snippet that illustrates the whole concept:
byte[] data = new byte[320 * 200 * 1];
Bitmap bmp1 = new Bitmap(320, 200,
System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
Bitmap bmp2 = new Bitmap(320, 200,
System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
var bdata = bmp1.LockBits(new Rectangle(new Point(0, 0), bmp1.Size),
ImageLockMode.WriteOnly, bmp1.PixelFormat);
try
{
Marshal.Copy(data, 0, bdata.Scan0, data.Length);
}
finally
{
bmp1.UnlockBits(bdata);
}
// Do your modifications
bdata = bmp2.LockBits(new Rectangle(new Point(0, 0), bmp2.Size),
ImageLockMode.WriteOnly, bmp2.PixelFormat);
try
{
Marshal.Copy(data, 0, bdata.Scan0, data.Length);
}
finally
{
bmp2.UnlockBits(bdata);
}
This isn't the best code performance-wise (it does need some copying), but the only real alternative is to use unsafe
code - which you really shouldn't be doing, given your current apparent knowledge about the managed environment. It can lead to some nasty issues if you don't use it properly. In any case, the performance gains might be quite negligible - find out if you actually care before you go the unsafe way.
For more information about the problem and the complexities of working with managed and unmanaged memory, you can have a look at my blog at http://www.luaan.cz/2014/07/a-quick-introduction-to-managed-and.html It's still rather high-level, but it explains more than this answer on its own.