WPF CreateBitmapSourceFromHBitmap() memory leak
Solution 1
MSDN for Bitmap.GetHbitmap()
states:
Remarks
You are responsible for calling the GDI DeleteObject method to free the memory used by the GDI bitmap object.
So use the following code:
// at class level
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
// your code
using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(1000, 1000))
{
IntPtr hBitmap = bmp.GetHbitmap();
try
{
var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hBitmap);
}
}
I also replaced your Dispose()
call by an using
statement.
Solution 2
Whenever dealing with unmanaged handles it can be a good idea to use the "safe handle" wrappers:
public class SafeHBitmapHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[SecurityCritical]
public SafeHBitmapHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return GdiNative.DeleteObject(handle) > 0;
}
}
Construct one like so as soon as you surface a handle (ideally your APIs would never expose IntPtr, they would always return safe handles):
IntPtr hbitmap = bitmap.GetHbitmap();
var handle = new SafeHBitmapHandle(hbitmap , true);
And use it like so:
using (handle)
{
... Imaging.CreateBitmapSourceFromHBitmap(handle.DangerousGetHandle(), ...)
}
The SafeHandle base gives you an automatic disposable/finalizer pattern, all you need to do is override the ReleaseHandle method.
Solution 3
I had the same requirement and issue (memory leak). I implemented the same solution as marked as answer. But although the solution works, it caused an unacceptable hit to performance. Running on a i7, my test app saw a steady 30-40% CPU, 200-400MB RAM increases and the garbage collector was running almost every millisecond.
Since I'm doing video processing, I'm in need of much better performance. I came up with the following, so thought I would share.
Reusable Global Objects
//set up your Bitmap and WritableBitmap as you see fit
Bitmap colorBitmap = new Bitmap(..);
WriteableBitmap colorWB = new WriteableBitmap(..);
//choose appropriate bytes as per your pixel format, I'll cheat here an just pick 4
int bytesPerPixel = 4;
//rectangles will be used to identify what bits change
Rectangle colorBitmapRectangle = new Rectangle(0, 0, colorBitmap.Width, colorBitmap.Height);
Int32Rect colorBitmapInt32Rect = new Int32Rect(0, 0, colorWB.PixelWidth, colorWB.PixelHeight);
Conversion Code
private void ConvertBitmapToWritableBitmap()
{
BitmapData data = colorBitmap.LockBits(colorBitmapRectangle, ImageLockMode.WriteOnly, colorBitmap.PixelFormat);
colorWB.WritePixels(colorBitmapInt32Rect, data.Scan0, data.Width * data.Height * bytesPerPixel, data.Stride);
colorBitmap.UnlockBits(data);
}
Implementation Example
//do stuff to your bitmap
ConvertBitmapToWritableBitmap();
Image.Source = colorWB;
The result is a steady 10-13% CPU, 70-150MB RAM, and the garbage collector only ran twice in a 6 minute run.
Alban
Updated on July 09, 2022Comments
-
Alban almost 2 years
I need to draw an image pixel by pixel and display it inside a WPF. I am attempting to do this by using a
System.Drawing.Bitmap
then usingCreateBitmapSourceFromHBitmap()
to create aBitmapSource
for a WPF Image control. I have a memory leak somewhere because when theCreateBitmapSourceFromBitmap()
is called repeatedly the memory usage goes up and does not drop off until the application is ended. If I don't callCreateBitmapSourceFromBitmap()
there is no noticeable change in memory usage.for (int i = 0; i < 100; i++) { var bmp = new System.Drawing.Bitmap(1000, 1000); var source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap( bmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions()); source = null; bmp.Dispose(); bmp = null; }
What can I do to free the
BitmapSource
memory? -
Alban over 14 yearsThat works. There is a bit of residual memory held after the test, but the garbage collector picks it up. Thanks Julien.
-
Cameron over 8 yearsVery nice mini-article about something I should know better.
-
Demetris Leptos over 7 yearsthe "answer" pointed to the right direction but still did not work - I still got out of memory - but your solution works flawlessly - and not only that but also I love wrapping in this manner - it's true abstraction and the future of coding - sorry got carried away
-
TrickySituation over 6 yearsNo, sorry, I'm unable to repro your error. Based on your error tho, I think your trying to access the Bitmap directly. See, what's going on is that your copying the bitmap from the Kinect stream and and writing it to your own WritableBitmap all in the conversion code. Try double checking the sequence of locking and unlocking, that your moving between Bitmap -> BitmapData -> WritableBitmap, and that the rectangle is the proper size including the z-axis = bytesPerPixel. Good luck
-
DDoSolitary about 5 yearsThe code is essentially the same as that in the question. It doesn't solve the memory leak problem at all.
-
DDoSolitary about 5 yearsYour code for calculating screen bounds (
Screen.AllScreens.Min/Max
) can be replaced bySystemParameters.VirtualScreenLeft/Top/Width/Height
. This eliminates the need of referencing System.Windows.Forms, which isn't part of WPF, and makes the code faster especially when you have multiple screens. -
Darthchai over 3 yearsYou are awesome. I've been trying to eliminate this bug for a long time, and your solution worked like a charm. thanks