Memory leak - drawing images with BitBlt

aewarnick

Well-known member
Joined
Jan 29, 2003
Messages
1,031
C#:
public static void DrawBitBlt(Graphics g, Bitmap bmp, ref Rectangle destRec)
{
	IntPtr hDC= g.GetHdc();
	IntPtr offscreenDC= a.Api.CreateCompatibleDC(hDC);
	a.Api.SelectObject(offscreenDC, bmp.GetHbitmap());
	if(bmp.Width==destRec.Width && bmp.Height==destRec.Height)
		a.Api.BitBlt(hDC, destRec.X, destRec.Y, destRec.Width, destRec.Height, offscreenDC, 0, 0, SrcCopy);
	else
		a.Api.StretchBlt(hDC, destRec.X, destRec.Y, destRec.Width, destRec.Height, offscreenDC, 0, 0, bmp.Width, bmp.Height, SrcCopy);
	a.Api.DeleteDC(offscreenDC);
	g.ReleaseHdc(hDC);
}
I am hoping someone here can pinpoint where it is.
 
Try replacing this like:
C#:
a.Api.SelectObject(offscreenDC, bmp.GetHbitmap());
with this:
C#:
a.Api.DeleteObject(a.Api.SelectObject(offscreenDC, bmp.GetHbitmap()));
Define DeleteObject if its not already part of your API class. :)

Also, you can use
C#:
if (bmp.Size.Equals(destRect.Size)) {
I believe, for your size check.
 
That did the trick. Thanks VF! I have one more question to ask about BitBlt. Ill post a new thread right now. I couldnt find anything on the net about it.
 
This doesnt make sense!

Why is the IntPtr returned here:
IntPtr objectPtr= a.Api.SelectObject(offscreenDC, bmp.GetHbitmap());
different from the one returned below?
 
C#:
public static void DrawBitBlt(Graphics g, Bitmap bmp, ref Rectangle destRec)
			{
				IntPtr hDC= g.GetHdc();
				IntPtr offscreenDC= a.Api.CreateCompatibleDC(hDC);
				IntPtr objectPtr= a.Api.SelectObject(offscreenDC, bmp.GetHbitmap());
				if(bmp.Width==destRec.Width && bmp.Height==destRec.Height)
					a.Api.BitBlt(hDC, destRec.X, destRec.Y, destRec.Width, destRec.Height, offscreenDC, 0, 0, SrcCopy);
				else
					a.Api.StretchBlt(hDC, destRec.X, destRec.Y, destRec.Width, destRec.Height, offscreenDC, 0, 0, bmp.Width, bmp.Height, SrcCopy);
				a.Api.DeleteObject(objectPtr);
a.MB.ShowDialog(objectPtr+"  "+a.Api.SelectObject(offscreenDC, bmp.GetHbitmap()));
				a.Api.DeleteDC(offscreenDC);
				g.ReleaseHdc(hDC);
			}
My custom messageBox (MB) shows the results.
 
Because youve already selected the new object into it.

At first, you are returning the handle to the bitmap stored in the DC into a variable. Then you are replacing the handle in the DC with a new bitmaps handle. So its like this:

Before
objectPtr is nothing
Handle in offscreenDC = xxxxxx
Handle of bmp = yyyyyy

Your objectPtr = statement here

After
objectPtr = xxxxx
Handle in offscreenDC = yyyyyy
Handle of bmp = yyyyyy

So naturally, when you retrieve the handle in offscreenDC it will be different than it initially was. The second time around it returns the handle of bmp, instead of the handle of the placeholder in offscreenDC.
 
THE LEAK IS BACK!

I dont know how or why.
C#:
public static void DrawBitBlt(Graphics g, Bitmap bmp, ref Rectangle destRec)
{
	IntPtr hDC= g.GetHdc();
	IntPtr offscreenDC= a.Api.CreateCompatibleDC(hDC);
	a.Api.SelectObject(offscreenDC, bmp.GetHbitmap());
	if(bmp.Size.Equals(destRec.Size))
		a.Api.BitBlt(hDC, destRec.X, destRec.Y, destRec.Width, destRec.Height, offscreenDC, 0, 0, SrcCopy);
	else
		a.Api.StretchBlt(hDC, destRec.X, destRec.Y, destRec.Width, destRec.Height, offscreenDC, 0, 0, bmp.Width, bmp.Height, SrcCopy);
				a.Api.DeleteObject(a.Api.SelectObject(offscreenDC, bmp.GetHbitmap()));
	a.Api.DeleteDC(offscreenDC);
	g.ReleaseHdc(hDC);
}
 
THE LEAK IS BACK!

I dont know how or why.
C#:
public static void DrawBitBlt(Graphics g, Bitmap bmp, ref Rectangle destRec)
{
	IntPtr hDC= g.GetHdc();
	IntPtr offscreenDC= a.Api.CreateCompatibleDC(hDC);
	a.Api.SelectObject(offscreenDC, bmp.GetHbitmap());
	if(bmp.Size.Equals(destRec.Size))
		a.Api.BitBlt(hDC, destRec.X, destRec.Y, destRec.Width, destRec.Height, offscreenDC, 0, 0, SrcCopy);
	else
		a.Api.StretchBlt(hDC, destRec.X, destRec.Y, destRec.Width, destRec.Height, offscreenDC, 0, 0, bmp.Width, bmp.Height, SrcCopy);
	a.Api.DeleteObject(a.Api.SelectObject(offscreenDC, bmp.GetHbitmap()));
	a.Api.DeleteDC(offscreenDC);
	g.ReleaseHdc(hDC);
}
The return value of DeleteObject is 1 meaning success.
 
Replace the first SelectObject line with the second one, and then remove the second one from the bottom; once you use SelectObject, the handle in the DC changes, so SelectObject() will not return the same thing it did last time you used it. It basically sticks what you tell it to into the DC and returns what used to be there. If you dont do anything with it, you cant get it back later, because the new object is now in the DC, so SelectObject will return that.
 
Its done and it works but it takes a while for the dumped memory to show up in the task manager.
Let me see if I understand this correctly. When I call SelectObject, deleting it in the same statement it only deletes an extra object I dont need and the other it placed into the back buffer DC.
Correct?
 
Yes, thats correct.

And also, dont worry about the memory taking awhile to show up; memory allocated within a .NET app probably wont be really free until the Garbage Collector frees it up.
 
That creates a major problem. When I use DrawImageUnscaled my ram stays at around 12000kb but using BitBlt it jacks up to over 40k before it is removed! Making the animation very choppy.

How can I prevent this?
 
That doesnt seem right... paste your code again, let me make sure there are no leaks.
 
C#:
public static void DrawBitBlt(Graphics g, Bitmap bmp, ref Rectangle destRec)
			{
				IntPtr hDC= g.GetHdc();
				IntPtr offscreenDC= a.Api.CreateCompatibleDC(hDC);
				a.Api.DeleteObject(a.Api.SelectObject(offscreenDC, bmp.GetHbitmap()));
				if(bmp.Size.Equals(destRec.Size))
					a.Api.BitBlt(hDC, destRec.X, destRec.Y, destRec.Width, destRec.Height, offscreenDC, 0, 0, SrcCopy);
				else
					a.Api.StretchBlt(hDC, destRec.X, destRec.Y, destRec.Width, destRec.Height, offscreenDC, 0, 0, bmp.Width, bmp.Height, SrcCopy);
				a.Api.DeleteDC(offscreenDC);
				g.ReleaseHdc(hDC);
				GC.Collect(); //a desparate attempt, that failed.
			}
 
Well, it seems like it should be alright... perhaps using the API so much (I assume you are using it in some sort of loop, meaning calling it very often) it taking its toll on .NET... it is unmanaged afterall. divil would know better than I on this one, I think.
 
Here is the whole project. Maybe you will get different results. It is very simple. Not much code in the main form. At the bottom of Form1 (the main form) you will see this:
//a.Api.DrawBitBlt(e.Graphics, B, ref this.posRec);
e.Graphics.DrawImageUnscaled(B, this.posRec);

Just alternate them to test.

You will notice also, that the form closes 50 times faster when the api is not used.
 

Attachments

Last edited by a moderator:
Heres the correct way of doing it, making sure everything used is cleaned up properly.

C#:
public static void DrawBitBlt(Graphics g, Bitmap bmp, ref Rectangle destRec)
{
	// Get DC handle and create a compatible one
	IntPtr hDC= g.GetHdc();
	IntPtr offscreenDC= a.Api.CreateCompatibleDC(hDC);

	// Select our bitmap in to DC, recording what was there before
	IntPtr hBitmap = bmp.GetHbitmap();
	IntPtr oldObject = a.Api.SelectObject(offscreenDC, hBitmap);

	// Perform blt
	if(bmp.Size.Equals(destRec.Size))
		a.Api.BitBlt(hDC, destRec.X, destRec.Y, destRec.Width, destRec.Height, offscreenDC, 0, 0, SrcCopy);
	else
		a.Api.StretchBlt(hDC, destRec.X, destRec.Y, destRec.Width, destRec.Height, offscreenDC, 0, 0, bmp.Width, bmp.Height, SrcCopy);

	// Select our bitmap object back out of the DC
	a.Api.SelectObject(offscreenDC, oldObject);

	// Delete our bitmap
	a.Api.DeleteObject(hBitmap);

	// Delete memory DC and release our DC handle
	a.Api.DeleteDC(offscreenDC);
	g.ReleaseHdc(hDC);
}
 
Back
Top