CDialog memory leaks in VC10
Asked Answered
H

1

7

We are upgrading from VC8 to VC10 and have found a number of memory leaks that seem to be CDialog related. The simplest example of this is demonstrated with the following code using a CDialog that just has a number of buttons. In VC10 this leaks, but in VC8 it doesn't:

for (int i = 0; i < 5000; ++i) {
  CDialog* dialog = new CDialog;
  dialog->Create(IDD_LEAKER, 0);
  dialog->DestroyWindow();
  delete dialog;
}

Memory usage keeps rising and the example dialog we have with about 30 buttons leaks 10s of Mb.

Note that the above is a test example where we have stripped out all of our dialog handling code, in our real code we have a derived class and use PostNcDestroy().

Oddly neither of the following code examples leak in either VC8 or VC10:

CDialog* dialog = new CDialog;
for (int i = 0; i < 5000; ++i) {
  dialog->Create(IDD_LEAKER, 0);
  dialog->DestroyWindow();
}
delete dialog;

for (int i = 0; i < 5000; ++i) {
  CDialog* dialog = new CDialog;
  delete dialog;
}

What are we missing here?

Huihuie answered 18/8, 2011 at 12:42 Comment(5)
Just for curiosity - Why do you need 1) Create CDialog on heap, 2) Create 5000 dialogs, 3) Not use ShowDialog or DoModal?Apprise
I think the questioner is asking about a specific problem with modeless dialogs and has simply provided some cut down code to explain the issue he has seen: "Note that the above is a test example...". I suspect that the 5000 is an arbitrary number used just to emphasise the symptoms.Maryalice
To answer: The code is a test that anyone can plug into a windows app and test with one of their own dialogs. Our dialogs happen to be modeless, but are deleted and cleaned up when the user closes them. Our problem with this isn't just with the memory leak, bad as that is, but also with the fact that if you time the loop it gets longer and longer with each iteration.Huihuie
Are you absolutely positive that you're testing this memory leakage with an application complied in Release mode (i.e., with optimizations enabled)?Crenel
I wrote a test program so that I could look into this and can confirm that the issue occurs in a release build (with optimisations enabled) as well as a debug build.Maryalice
M
7

This appears to be down to the way that MFC manages its handle maps:

What is the lifetime of a CWnd obtained from CWnd::FromHandle?

If you wait long enough for your application to become idle, you do get your memory back, i.e. it's not really a leak. However, as you have observed, while Visual C++ 2010 continues to consume more and more memory - until the maps are tidied in OnIdle() - this doesn't appear to happen in Visual C++ 2008.

Debugging an application containing your code does show that there are a lot more objects in the HWND temporary map in the VC 10 version than there are in the VC 9 version.

The handle map code (winhand.cpp) doesn't appear to have changed between the two versions but there's lots of code in MFC that uses it!

Anyway, assuming that you really want to run your program like this - I guess you're running in some kind of automated mode? - then you'll want to force the garbage collection at appropriate intervals. Have a look at this entry on MSDN:

http://msdn.microsoft.com/en-us/library/xt4cxa4e(v=VS.100).aspx

CWinThread::OnIdle() actually calls this to tidy things up:

AfxLockTempMaps();
AfxUnlockTempMaps(/*TRUE*/);
Maryalice answered 19/8, 2011 at 9:41 Comment(1)
Yep that appears to fix it. Our automatic test system runs 1000s of test scripts raising and lowering forms but never going into an idle loop. In VC10 a test that would have taken 1 second when run at the start of a sequence, if run at the end would be taking 5 or 6 seconds.Huihuie

© 2022 - 2024 — McMap. All rights reserved.