mromeo
asked on
How do you detect resource leaks in an MFC MDI application?
Hi everyone. I have an MDI application which has a drag/drop interface. The problem that I'm having is that when I start dragging objects around on my View, memory starts growing very quickly. The program doesn't have any detectable memory leaks that are obvious to me (ie. all 'operator new's' are matched up with operator 'delete') and the program doesn't report any memory leaks when I exit the program. I am suspecting that I am leaking other resources, such as GDI objects, fonts, brushes, etc... Does anybody have any good ideas about how I can go about detecting resource leaks? The program is fairly large, so it's hard to isolate the exact moment where it starts growing.
Let me know if there is other information that I can give you that may be helpful. Thanks.
Let me know if there is other information that I can give you that may be helpful. Thanks.
Are you calling _CrtCheckMemory in your application destructor?
ASKER
Yes, I'm calling it in ExitInstance. It doesn't give me any output to the output window. Is there sometng I else I need to call to use it?
ASKER
I added a few checkpoints and then call __CrtMemDifference/_CrtDum pMemStatis tics, but I get no output. What should I see in the debug window?
>>Is there sometng I else I need to call to use it?
No, that should do it.
>> What should I see in the debug window?
You should get a message saying memory leak detected, and it should include a file name and line count.
You can force it to give you a leak by adding the following in your ExitInnstance function:
int *dummy = new[12345];
And don't delete it.
Then you'll see what the leak meassage should look like.
No, that should do it.
>> What should I see in the debug window?
You should get a message saying memory leak detected, and it should include a file name and line count.
You can force it to give you a leak by adding the following in your ExitInnstance function:
int *dummy = new[12345];
And don't delete it.
Then you'll see what the leak meassage should look like.
Exactly what makes you think you'r getting a leak?
ASKER
Well, it's possible that it's not a leak, but something is making memory grow very quickly in my application. When I start dragging objects in my main View, memory starts growing by about 100k every 5 seconds. That's before I even do the Drop. It happens during OnDragOver. I have been going over this code so carefully, and I can't figure out what's doing it. I'm trying to figure out if its a system resource or something. I don't know where to look next.
ASKER
I put an intentional leak right before I call _CrtDumpMemoryLeaks and it didn't tell me that I leaked anything. Something isn't right.
char *a = new char[123456];
int ret = _CrtCheckMemory();
_CrtDumpMemoryLeaks( );
char *a = new char[123456];
int ret = _CrtCheckMemory();
_CrtDumpMemoryLeaks( );
Remove all other _Crt??? function calls, and only make the _CrtDumpMemoryLeaks() after the created memory leak.
You should also comment out any other _Crt??? code in your entire app.
One of them could be causing the problem.
You should just do the _CrtDumpMemoryLeaks() call by itself.
You should also comment out any other _Crt??? code in your entire app.
One of them could be causing the problem.
You should just do the _CrtDumpMemoryLeaks() call by itself.
Also add a TRACE before calling _CrtCheckMemory.
char *a = new char[123456];
TRACE("****** Here comes memory leak dump ********");
_CrtDumpMemoryLeaks( );
char *a = new char[123456];
TRACE("****** Here comes memory leak dump ********");
_CrtDumpMemoryLeaks( );
ASKER
Ok, well, I got rid of the couple of minor leaks that I had. Nothing has really changed. When I start dragging objects around (which are CStatic objects), memory starts growing very rapidly. As soon as I release the mouse and OnDrop is called. It stops growing. Where would you start looking? I've been over and over this code.
ASKER
Ok, well, it appears to be totally unrelated to Dragging. If I simply start resizing the main window (which has NO MDI windows open), memory start to grow. I don't really know what to make of this. Has anybody ever seen the app grow just by resizing the main FrameWnd?
Post your OnSize() event.
ASKER
I don't have one.
then it is your paint function, every time you drag the mouse to resize, a paint is made.
You can try:
CYourView::OnPaint() // I suposse you have implemented this function, or could be OnPaint
{
::MessageBeep(MB_OK);
// All your code here
}
You can try:
CYourView::OnPaint() // I suposse you have implemented this function, or could be OnPaint
{
::MessageBeep(MB_OK);
// All your code here
}
ASKER
No, no, no. I don't even have a view window open. All I have is the MainFrame. No views, no documents, etc have even been created. All I'm doing is dragging the lower right corner to resize the window and the memory just grow and grow and grow. I have no OnSize method and only one OnPaint method which is not being called. I'm using Spy++ to try to figure out what other messages are being sent. I tried a smaller test mdi application, and it does not happen with that program.
>> I am suspecting that I am leaking other resources, such as GDI objects, fonts, brushes, etc... Does anybody have any
>> good ideas about how I can go about detecting resource leaks?
Hmm, since you mentioned GDI objects, I supossed you had a painting function.
Then, if you say you have not child windows (doc/view), could you post your entire CMDIFrameWnd implementation?
>> good ideas about how I can go about detecting resource leaks?
Hmm, since you mentioned GDI objects, I supossed you had a painting function.
Then, if you say you have not child windows (doc/view), could you post your entire CMDIFrameWnd implementation?
ASKER
Right, that's what I thought it was until I realized it was happening even without the View window open.
Here's the code. Don't know how much it will help out of context:
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNAMIC(CMainFra me, CMDIFrameWnd)
BEGIN_MESSAGE_MAP(CMainFra me, CMDIFrameWnd)
//{{AFX_MSG_MAP(CMainFrame )
ON_WM_CREATE()
ON_COMMAND(ID_FILE_CHOOSE_ URL, OnFileChooseUrl)
ON_COMMAND(ID_WINDOW_TILE_ VERT, OnWindowTileVert)
ON_WM_CLOSE()
ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
ON_COMMAND(ID_FILE_SAVEALL , OnFileSaveall)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
static UINT indicators[] =
{
ID_SEPARATOR, // status line indicator
ID_INDICATOR_DATABASE,
ID_INDICATOR_POS,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};
////////////////////////// ////////// ////////// ////////// ////////// ////////// /
// CMainFrame construction/destruction
CMainFrame::CMainFrame()
{
}
CMainFrame::~CMainFrame()
{
}
int CMainFrame::OnCreate(LPCRE ATESTRUCT lpCreateStruct)
{
if (CMDIFrameWnd::OnCreate(lp CreateStru ct) == -1)
return -1;
if (!m_wndToolBar.CreateEx(th is, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar( IDR_MAINFR AME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if (!m_wndStatusBar.Create(th is) ||
!m_wndStatusBar.SetIndicat ors(indica tors,
sizeof(indicators)/sizeof( UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// create the main toolbar.
m_wndToolBar.EnableDocking (CBRS_ALIG N_ANY);
CString toolbarTitle;
toolbarTitle.LoadString(ID R_MAINFRAM E);
m_wndToolBar.SetWindowText (toolbarTitle);
EnableDocking(CBRS_ALIGN_A NY);
DockControlBar(&m_wndToolB ar);
// set the window placment.
WINDOWPLACEMENT wp;
if (ReadWindowPlacement(&wp))
SetWindowPlacement(&wp);
return 0;
}
BOOL CMainFrame::PreCreateWindo w(CREATEST RUCT& cs)
{
if( !CMDIFrameWnd::PreCreateWi ndow(cs) )
return FALSE;
return TRUE;
}
////////////////////////// ////////// ////////// ////////// ////////// ////////// /
// CMainFrame diagnostics
#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
CMDIFrameWnd::AssertValid( );
}
void CMainFrame::Dump(CDumpCont ext& dc) const
{
CMDIFrameWnd::Dump(dc);
}
#endif //_DEBUG
////////////////////////// ////////// ////////// ////////// ////////// ////////// /
// CMainFrame message handlers
void CMainFrame::OnFileChooseUr l()
{
// display the URL dialog box.
CURLDialog urlDlg;
if (urlDlg.DoModal() == IDOK)
{
}
}
void CMainFrame::OnWindowTileVe rt()
{
MDITile(MDITILE_VERTICAL);
}
void CMainFrame::OnClose()
{
WINDOWPLACEMENT wp;
// before it is destroyed, save the position of the window
wp.length = sizeof wp;
if ( GetWindowPlacement(&wp) )
{
if ( IsIconic() )
// never restore to Iconic state
wp.showCmd = SW_SHOW ;
if ((wp.flags & WPF_RESTORETOMAXIMIZED) != 0)
// if maximized and maybe iconic restore maximized state
wp.showCmd = SW_SHOWMAXIMIZED ;
// and write it to the .INI file
WriteWindowPlacement(&wp);
}
CMDIFrameWnd::OnClose();
}
BOOL CMainFrame::WriteWindowPla cement(WIN DOWPLACEME NT *pwp)
{
return CAppPreferences::SaveWindo wPosition( pwp, STR_MAINWIN_POS);
}
BOOL CMainFrame::ReadWindowPlac ement(WIND OWPLACEMEN T *pwp)
{
return CAppPreferences::GetWindow Position(p wp, STR_MAINWIN_POS);
}
void CMainFrame::SetStatusBar(i nt pane, CString &text)
{
m_wndStatusBar.SetPaneText (pane, text);
}
void CMainFrame::OnFileClose()
{
// check to see how many documents are open.
// when there are none left, change the status bar
// to read the current url db value.
CDocument *doc = GetActiveDocument();
if (doc == NULL)
{
CString prefsURL = CAppPreferences::Fetch_Get ModulesURL ();
SetStatusBar(1, prefsURL);
}
}
void CMainFrame::OnFileSaveall( )
{
MessageBox("Save All Not Yet Implemented");
// need to get all documents and tell each one to save
CScreenMaintenanceApp *theApp = static_cast<CScreenMainten anceApp *>(AfxGetApp());
theApp->SaveAllModified();
}
Here's the code. Don't know how much it will help out of context:
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNAMIC(CMainFra
BEGIN_MESSAGE_MAP(CMainFra
//{{AFX_MSG_MAP(CMainFrame
ON_WM_CREATE()
ON_COMMAND(ID_FILE_CHOOSE_
ON_COMMAND(ID_WINDOW_TILE_
ON_WM_CLOSE()
ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
ON_COMMAND(ID_FILE_SAVEALL
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
static UINT indicators[] =
{
ID_SEPARATOR, // status line indicator
ID_INDICATOR_DATABASE,
ID_INDICATOR_POS,
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
};
//////////////////////////
// CMainFrame construction/destruction
CMainFrame::CMainFrame()
{
}
CMainFrame::~CMainFrame()
{
}
int CMainFrame::OnCreate(LPCRE
{
if (CMDIFrameWnd::OnCreate(lp
return -1;
if (!m_wndToolBar.CreateEx(th
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if (!m_wndStatusBar.Create(th
!m_wndStatusBar.SetIndicat
sizeof(indicators)/sizeof(
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// create the main toolbar.
m_wndToolBar.EnableDocking
CString toolbarTitle;
toolbarTitle.LoadString(ID
m_wndToolBar.SetWindowText
EnableDocking(CBRS_ALIGN_A
DockControlBar(&m_wndToolB
// set the window placment.
WINDOWPLACEMENT wp;
if (ReadWindowPlacement(&wp))
SetWindowPlacement(&wp);
return 0;
}
BOOL CMainFrame::PreCreateWindo
{
if( !CMDIFrameWnd::PreCreateWi
return FALSE;
return TRUE;
}
//////////////////////////
// CMainFrame diagnostics
#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
CMDIFrameWnd::AssertValid(
}
void CMainFrame::Dump(CDumpCont
{
CMDIFrameWnd::Dump(dc);
}
#endif //_DEBUG
//////////////////////////
// CMainFrame message handlers
void CMainFrame::OnFileChooseUr
{
// display the URL dialog box.
CURLDialog urlDlg;
if (urlDlg.DoModal() == IDOK)
{
}
}
void CMainFrame::OnWindowTileVe
{
MDITile(MDITILE_VERTICAL);
}
void CMainFrame::OnClose()
{
WINDOWPLACEMENT wp;
// before it is destroyed, save the position of the window
wp.length = sizeof wp;
if ( GetWindowPlacement(&wp) )
{
if ( IsIconic() )
// never restore to Iconic state
wp.showCmd = SW_SHOW ;
if ((wp.flags & WPF_RESTORETOMAXIMIZED) != 0)
// if maximized and maybe iconic restore maximized state
wp.showCmd = SW_SHOWMAXIMIZED ;
// and write it to the .INI file
WriteWindowPlacement(&wp);
}
CMDIFrameWnd::OnClose();
}
BOOL CMainFrame::WriteWindowPla
{
return CAppPreferences::SaveWindo
}
BOOL CMainFrame::ReadWindowPlac
{
return CAppPreferences::GetWindow
}
void CMainFrame::SetStatusBar(i
{
m_wndStatusBar.SetPaneText
}
void CMainFrame::OnFileClose()
{
// check to see how many documents are open.
// when there are none left, change the status bar
// to read the current url db value.
CDocument *doc = GetActiveDocument();
if (doc == NULL)
{
CString prefsURL = CAppPreferences::Fetch_Get
SetStatusBar(1, prefsURL);
}
}
void CMainFrame::OnFileSaveall(
{
MessageBox("Save All Not Yet Implemented");
// need to get all documents and tell each one to save
CScreenMaintenanceApp *theApp = static_cast<CScreenMainten
theApp->SaveAllModified();
}
ASKER
It appears to be related to the toolbar. When I comment out the code to create the toolbar, the problem goes away.
Don't what are these functions (maybe this: http://www.codeproject.com/dialog/windowpres.asp) but you can try to remove them temporarily:
BOOL CMainFrame::WriteWindowPla cement(WIN DOWPLACEME NT *pwp)
{
return CAppPreferences::SaveWindo wPosition( pwp, STR_MAINWIN_POS);
}
BOOL CMainFrame::ReadWindowPlac ement(WIND OWPLACEMEN T *pwp)
{
return CAppPreferences::GetWindow Position(p wp, STR_MAINWIN_POS);
}
BOOL CMainFrame::WriteWindowPla
{
return CAppPreferences::SaveWindo
}
BOOL CMainFrame::ReadWindowPlac
{
return CAppPreferences::GetWindow
}
>>It appears to be related to the toolbar. When I comment out the code to create the toolbar, the problem goes away.
If your toolbar is a custom toolbar, it could be.
If your toolbar is a custom toolbar, it could be.
ASKER
What do you mean by "custom" toolbar? I haven't done anything special to it. I have a member variable called m_wndToolBar. All the code that deals with it is in the code I posted.
What do you mean by "custom" toolbar?
I mean some derived toolbar that you can obtain in the internet, some of these objects have memory leaks.
But if you say it is a standard CToolbar object, then it is strange that it produces a memory leak.
You toolbar buttons must be related to some application functions, am I correct?
If so, maybe you have some 'OnUpdateSomeCommand()' functions to manage buttons state, there could be a possible point of leakage.
I mean some derived toolbar that you can obtain in the internet, some of these objects have memory leaks.
But if you say it is a standard CToolbar object, then it is strange that it produces a memory leak.
You toolbar buttons must be related to some application functions, am I correct?
If so, maybe you have some 'OnUpdateSomeCommand()' functions to manage buttons state, there could be a possible point of leakage.
ASKER
It's not that either. All my ::OnUpdateXXX methods are in the view or document classes. I guess I'm goint to ave to rip this code apart, line by line to figure out what is going on. Maybe I need to re-assemble the toolbar to see at what point this memory problem starts.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Yes, best option is to read carefully your code, I think
in every function that works with drag and drop, you must ensure to deselect every resource type like: bitmaps, pens, brushes, fonts, etc.
So, every first time you select and object in a function (with SelectObject, saving previous object) you have to unselect it using SelectObject again.