Solved

CreateCompatibleDC memory leak?

Posted on 2009-07-15
13
1,442 Views
Last Modified: 2013-11-20
I created a simple VC++ 6.0 MFC application, one dialog as a test. The attached snippet is my OnOK() handler.  All it does is loop indefinitely calling CreateCompatibleDC() and then DeleteDC.

When I first run the app and check it out in Task Manager, I see it consuming 8,308k of memory. This holds steady (it's not doing anything.) I then click the OK button, and the attached snippet runs. Memory usage grows very rapidly.

Why is this? Does something else need to be done to delete the device context?



void CSystemHealthDlg::OnOK() 
{
	CDC memDC;
	while (1==1)
	{
		memDC.CreateCompatibleDC(NULL);
		memDC.DeleteDC();
	}
	
	CDialog::OnOK();
}

Open in new window

0
Comment
Question by:PMH4514
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 5
13 Comments
 
LVL 33

Expert Comment

by:pgnatyuk
ID: 24864184
The code is correct.
Actually it can look this way:
HDC hDC = ::CreateCompatibleDC(NULL);
::DeleteDC(hDC);
I think HDC takes about 10 bytes in the global memory - I'm not sure, I don't remember exactly. So the memory usage you see should be explained somehow else but not with the HDC and these 2 lines - Create, Delete.
Actually you can find many complains about the CDC, but.. in real people just forget there something selected and so, DeleteDC does not release the resource. And this is the leak. But this is the GDI leak.
0
 
LVL 31

Expert Comment

by:Zoppo
ID: 24867511
I agree with pgnatyuk - the code snippet you posted shouldn't leak - I even tested in a test application.

Is this all of the code which you use? Or does something happen in between creation and deletion of the DC?

ZOPPO
0
 

Author Comment

by:PMH4514
ID: 24868786
Hi All -

Zoppo - you actually created the same test app and it doesn't leak? Weird.. There is literally nothing else in my test app. I created a new MFC project, changed settings to UNICODE, added that snippet and ran it.

0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:PMH4514
ID: 24868826
I just tried using pgnatyuks version instead (using HDC) and it does not appear to leak. His gives a handle to a DC, whereas mine returned CDC. There would be subsequent code of course, selecting GDI objects into it.. whereas I would do memDC.SelectObject(..) what would I do with the handle?
0
 

Author Comment

by:PMH4514
ID: 24869087
Further followup (sorry for 3 posts in a row) - I just created a new version of my test app leaving it as _MBCS and not _UNICODE, and it does NOT leak.

So, why would UNICODE cause this to leak? And is there a workaround?
0
 
LVL 33

Expert Comment

by:pgnatyuk
ID: 24870596
I'm afraid the leak is already here "I created a new MFC project". BoundChecker always complains about the MFC.
You can set breakpoint in your code on memDC.CreateCompatibleDC(NULL); and you will see the same code I posted, plus usual MFC stuff.
CDC is just a trivial wrapper on HDC. Eveything is the same.
HBITMAP hBitmapOld = (HBITMAP)::SelectObject(hDC, hBitmap); //select a bitmap
HFONT hFont = (HFONT)::SelectObject(hDC, hFont); //select the font.
everything is the same.
0
 
LVL 33

Expert Comment

by:pgnatyuk
ID: 24870722
Try to debug and get in into the MFC code here: memDC.DeleteDC();
I don't have the VS right now to check, but theorethically it is possible that after ::DeleteDC the internal HDC variable was not assigned to NULL and BoundChecker complains about it.
Actually, I think I'm wrong. BoundChecker is a very good tool, but it is not always possible to make this app absolutely happy.
0
 

Author Comment

by:PMH4514
ID: 24871228
BoundChecker? I am unfamiliar. An MFC app, the snippet I posted, MBCS character set does not show the leak. By "leak" I am referring to watching the mem usage column in Windows Task Manager. It grows very rapidly with each iteration. The Unicode build of the same code shows the leak.  I'll step into the code and see!
0
 
LVL 33

Expert Comment

by:pgnatyuk
ID: 24872312
I think the growing memory usage you see in the task manager is not related to this CreateCompatibleDC code. It can be something else. I tested just a small app with this code - I have an error detection tool.
Actually, the books say that you need to take the DC from the window by using the GetDC function, then create the compatible DC (memory DC), ..., delete the memory DC, release the window DC. Do not leave any bitmap or a font in the memory DC - select "old" bimap and "old" font before the deleting.Otherwise the app will work slower and slower and then, the low-memory message box will jump.
0
 

Author Comment

by:PMH4514
ID: 24872707
But when I remove this code, the memory usage shown in task manager remains constant.

I have also tried this variant:

      CDC* pDC = GetDC();
      CDC memDC;
      memDC.CreateCompatibleDC(pDC);
      memDC.DeleteDC();

      ReleaseDC(pDC);
      pDC = NULL;


and it leaks as well.

I am aware not to leave objects in the memory DC. This test app doesn't even go as far as selecting anything into the DC, it just creates and destroys it. And the memory consumption continues to grow in task manager.
0
 
LVL 33

Accepted Solution

by:
pgnatyuk earned 125 total points
ID: 24876513
I made a simple dialog-based app with MFC, put a button and wrote the code from your example:
CDC memDC;
memDC.CreateCompatibleDC(NULL);
memDC.DeleteDC();

I tested it with Purify+. It shows: Summary of all memory leaks... {19880 bytes, 15 blocks}.
It does not matter, how many times to press on the button - once or 100 times. Purify+ shows the same. The report contains just usual things about the MFC: strings, _CRTDLL_INIT, etc.
I commented the DC code and got absolutely same report - 19880 bytes, 15 blocks.
BOOL CDC::DeleteDC() contains a very simple code:
::DeleteDC(Detach());
Detach removes the handle from an internal store - the HDC was added there on CreateCompatibleDC.

I build release configuration and checked it with the task manager. It shows memory usage 3.460K in the beginning and 17 GDI objects. I pressed on the button and the app took 8K more - it shows 3468K. Then, does not matter how many times to press on the button, the parameters in the task manager do not change.

In order to earn my points :), I made a small console app:
#include <windows.h>
#include <stdio.h>
int main()
{
    HDC hDC = NULL;
    for (int i = 0; i < 100; i++)
    {
        hDC = ::CreateCompatibleDC(NULL);
        ::DeleteDC(hDC);
        hDC = NULL;
     }
     return 0;
}
Purify+ still shows 532 bytes in calloc_dbg (_CRTDLL_INIT) as it was in the test with the MFC app.
I'm fully convinced that there is no memory leak in this code:
CDC memDC;
memDC.CreateCompatibleDC(NULL);
memDC.DeleteDC();
 
0
 

Author Comment

by:PMH4514
ID: 24879770
ok, thank you for everything!
0
 

Author Comment

by:PMH4514
ID: 24879895
Just to followup for future readers. Unicode or not, the following code in a loop does not show increasing memory usage in windows task manager:

   HDC hDC = NULL;
    hDC = ::CreateCompatibleDC(NULL);
    ::DeleteDC(hDC);
    hDC = NULL;


However, this code, in a loop, unicode only, does show substantially growing memory usage in task manager:

      CDC memDC;
      memDC.CreateCompatibleDC(NULL);
      memDC.DeleteDC();


0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
modThree challenge 4 105
abstract class with all non abstract mentods 6 84
Unix Command -- Challenging  question 7 101
wordappend challenge 8 223
Introduction: Load and Save to file, Document-View interaction inside the SDI. Continuing from the second article about sudoku.   Open the project in visual studio. From the class view select CSudokuDoc and double click to open the header …
Introduction: Dialogs (2) modeless dialog and a worker thread.  Handling data shared between threads.  Recursive functions. Continuing from the tenth article about sudoku.   Last article we worked with a modal dialog to help maintain informat…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Although Jacob Bernoulli (1654-1705) has been credited as the creator of "Binomial Distribution Table", Gottfried Leibniz (1646-1716) did his dissertation on the subject in 1666; Leibniz you may recall is the co-inventor of "Calculus" and beat Isaac…

749 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question