Solved

Can't fix memory leak

Posted on 2000-03-31
24
455 Views
Last Modified: 2013-11-25
I've got an app that is a COM EXE server.  I used Microsoft's KB article Q173974 to add MFC support to the project.  Here is the recommended code change (replaced _tWinMain with this):

The problem I'm having is that Bounds Checker (V6.03) is complaining about a memory leak relative to the m_pMainWnd member that is new'd in InitInstance().  I've tried deleting it in the ExitInstance() function, but that ends up with a debug ASSERTion in wincore.cpp.

Can anyone help me with a safe delete of this pointer?

// ASSERTion from wincore.cpp:
#ifdef _DEBUG
            ASSERT(pMap->LookupPermanent(hWndOrig) == NULL);
#endif
// end from wincore.cpp
//////////////////////////////////////////////////////////////////////////////
// from Artice Q173974:

class CMyApp : public CWinApp
{
public:
  virtual BOOL InitInstance();
  virtual int ExitInstance();
  virtual ~CMyApp();

  BOOL m_bRun;
};

// the app is global //////////////////////////////////////////////////////////
CMyApp theApp;
///////////////////////////////////////////////////////////////////////////////

BOOL CMyApp::InitInstance()
{
  // Initialize OLE libraries.
  if (!AfxOleInit())
  {
     AfxMessageBox(_T("OLE Initialization Failed!"));
        return FALSE;
  }

  // Initialize CcomModule.
  _Module.Init(ObjectMap,m_hInstance);
  _Module.dwThreadID = GetCurrentThreadId();

  // Check command line arguments.
  TCHAR szTokens[] = _T("-/");
  m_bRun = TRUE;
  LPCTSTR lpszToken = FindOneOf(m_lpCmdLine, szTokens);

  while (lpszToken != NULL)
  {
     // Register ATL and MFC class factories.
     if (lstrcmpi(lpszToken, _T("Embedding"))==0 ||
         lstrcmpi(lpszToken, _T("Automation"))==0)
     {
        AfxOleSetUserCtrl(FALSE);
        break;
     }
     // Unregister servers.
     // There is no unregistration code for MFC
     // servers. Refer to <LINK TYPE="ARTICLE" VALUE="Q186212">Q186212</LINK> "HOWTO: Unregister MFC
     // Automation Servers" for adding unregistration
     // code.
     else if (lstrcmpi(lpszToken, _T("UnregServer"))==0)
     {

        VERIFY(SUCCEEDED(_Module.UpdateRegistryFromResource(IDR_FactoryTestExec, FALSE)));
        VERIFY(SUCCEEDED(_Module.UnregisterServer(TRUE)));
        m_bRun = FALSE;
        break;
     }
     // Register ATL and MFC objects in the registry.
     else if (lstrcmpi(lpszToken, _T("RegServer"))==0)
     {

        VERIFY(SUCCEEDED(_Module.UpdateRegistryFromResource(IDR_FactoryTestExec, TRUE)));
        VERIFY(SUCCEEDED(_Module.RegisterServer(TRUE)));
        COleObjectFactory::UpdateRegistryAll();
        m_bRun = FALSE;
        break;
     }
     lpszToken = FindOneOf(lpszToken, szTokens);
  } // while

  if (m_bRun)
  {
     // Comment out the next line if not using VC 6-generated
     // code.
     _Module.StartMonitor();

     VERIFY(SUCCEEDED(_Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE)));
     VERIFY(COleObjectFactory::RegisterAll());

     // Atlas TaskWindow code /////////////////////////////////////////////////////////////
     // - Displays the splash screen and creates the tray icon
     CreateTaskWindow(m_hInstance);
     TaskWindowShow("Starting WinNT Test Exec task");
     Sleep(3000);   // leave the splash screen up for a bit
     TaskWindowHide();
     GetWindowElements(); // get the instance handle and window handle into globals

     m_pMainWnd = new CWnd();
     m_pMainWnd->Attach(g_hWndMain);  // satisfy condition set above

     // end Atlas TaskWindow code /////////////////////////////////////////////////////////
  }  // if
  return TRUE;
}

int CMyApp::ExitInstance()
{
  // MFC's class factories registration is
  // automatically revoked by MFC itself.

  // make the tray icon go away
  DestroyWindow(g_hWndMain);  // this is the only safe way to do this
  delete m_pMainWnd;

  if (m_bRun)
     _Module.RevokeClassObjects();
  if (m_bRun)
     _Module.Term();          // cleanup ATL Global Module

  return CWinApp::ExitInstance();
}
0
Comment
Question by:rkohler
  • 8
  • 6
  • 5
  • +2
24 Comments
 
LVL 30

Expert Comment

by:SteveGTR
ID: 2674071
After you delete the m_pMainWnd pointer set it to NULL. This way MFC won't try and process it:

int CMyApp::ExitInstance()
{
  // MFC's class factories registration is
  // automatically revoked by MFC itself.

  // make the tray icon go away
  DestroyWindow(g_hWndMain);  // this is the only safe way to do this
  delete m_pMainWnd;
  m_pMainWnd = 0;             // *** Inserted this line

  if (m_bRun)
     _Module.RevokeClassObjects();
  if (m_bRun)
     _Module.Term();          // cleanup ATL Global Module

  return CWinApp::ExitInstance();
}

Good Luck,
Steve


     
0
 

Author Comment

by:rkohler
ID: 2674172
Thanks for the input, but this change produces exactly the same ASSERTion error.
0
 

Author Comment

by:rkohler
ID: 2674230
Thanks for the input, but this change produces exactly the same ASSERTion error.
0
 
LVL 23

Expert Comment

by:chensu
ID: 2674232
m_pMainWnd->Detach();
delete m_pMainWnd;
::PostMessage(g_hWndMain, WM_CLOSE, 0, 0);
0
 

Author Comment

by:rkohler
ID: 2674334
Thanks, but the ::PostMessage() call change produces the same ASSERTion error.

- Rob
0
 
LVL 23

Expert Comment

by:chensu
ID: 2674354
How do you create the g_hWndMain window? Is it a CWnd or CFrameWnd?

Try also

m_pMainWnd->Detach();
delete m_pMainWnd;
m_pMainWnd = NULL;
::PostMessage(g_hWndMain, WM_CLOSE, 0, 0);

0
 
LVL 30

Expert Comment

by:SteveGTR
ID: 2674373
When does the ASSERTION happen? Can you trace it back to a specific line of code in your program? For example does it happen when you call CWinApp::ExitInstance() in your ExitInstance() routine. What is the orgin of the hWndOrig in the ASSERTION? Is this the handle of your g_hWndMain window?
0
 

Author Comment

by:rkohler
ID: 2674434
To answer the first question:
-------------------------------------

g_hWndMain is an HWND that is created in CreateTaskWindow() - see 5 lines above the "new CWnd()".

By the way -

m_pMainWnd = NULL;
::PostMessage(g_hWndMain, WM_CLOSE, 0, 0);

produces the same ASSERTion error.

To answer the second question:
-----------------------------------------
This occurs on the delete m_pMainWnd line.  The hWndOrig is the m_hWnd of the CWnd.
0
 

Author Comment

by:rkohler
ID: 2674483
To answer the first question:
-------------------------------------

g_hWndMain is an HWND that is created in CreateTaskWindow() - see 5 lines above the "new CWnd()".

By the way -

m_pMainWnd = NULL;
::PostMessage(g_hWndMain, WM_CLOSE, 0, 0);

produces the same ASSERTion error.

To answer the second question:
-----------------------------------------
This occurs on the delete m_pMainWnd line.  The hWndOrig is the m_hWnd of the CWnd.
0
 
LVL 23

Expert Comment

by:chensu
ID: 2674501
Is g_hWndMain a CWnd or CFrameWnd? If it is a CFrameWnd, you don't have to delete it because it will delete itself.

Can you show us the callstack when the assertion failure occurs.
0
 

Author Comment

by:rkohler
ID: 2674661
g_hWndMain is an HWND.  But that isn't the problem.  The problem is m_pMainWnd.  The ASSERTion happens on the "delete m_pMainWnd".

CALL STACK
---------------
CMyApp::ExitInstance() line 315
CWinThread::Run() line 481 + 11 bytes
CWinApp::Run() line 400
AfxWinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00133ee1, int 1) line 49 + 11 bytes
WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00133ee1, int 1) line 30
WinMainCRTStartup() line 198 + 54 bytes
KERNEL32! 77f1ba06()
0
 
LVL 30

Expert Comment

by:SteveGTR
ID: 2674728
Where's the call stack above CMyApp? Like WINCORE.CPP for instance --- where the assertion occurs? Also, what is line 315 (and 314 where code is that initiated) of your CMyApp:ExitInstance() code?
0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 
LVL 23

Expert Comment

by:chensu
ID: 2674854
Are you sure you call m_pMainWnd->Detach() before the delete?
0
 

Expert Comment

by:bertp
ID: 2674881
if m_pMainWnd is CFrameWnd derived object then you shouldn't have to worry about it because CFrameWnds delete themselves via an overide of the CWnd:PostNCDestroy message.

reference "Programming Windows With MFC"
second addition" by J Prosise page 124

note On page 124 he also suggests (for non CframeWnds) the ExitInstance solution you tried BUT he notes on his web page in the errata section that this will not work because because >>the pointer<< has been set to null by this point...

So if pMainWnd in not a CFrameWnd then just override the postncdestroy

void MyMainWnd::PostNcDestroy()
{
    delete this;

}





0
 
LVL 23

Expert Comment

by:chensu
ID: 2674908
bertp>if m_pMainWnd is CFrameWnd derived object then you shouldn't have to worry about it because CFrameWnds delete themselves via an overide of the CWnd:PostNCDestroy message.

That's why I keep asking "Is g_hWndMain a CWnd or CFrameWnd?".
0
 

Expert Comment

by:bertp
ID: 2674925
I feel your pain man ;-)
0
 

Expert Comment

by:bertp
ID: 2674967
oh yeah, I would like to insert this in my first comment after the

"BUT he notes on his web page in the errata section that this will not work because because >>the pointer<< has been set to null by this point..."

This text


" This explains you assert problem. It will be NULL so don't assert that it is not!!  ;-)"

--------------------------------
summary: remove all references to m_pMainWnd in you ExitInstance()

if the object Is derived from CFrameWnd
then do nothing else

if it is CWnd derived with CFrameWnd
then add the PostNcDestroy() that I listed in the first comment....

If BoundsChecker still screams, complain to NuMega ;-) Your object are getting deleted!








 





 

0
 

Author Comment

by:rkohler
ID: 2674987
I appreciate everyone's help here, but unless I'm just real tired, I really don't appreciate the smart ass remark - wink, wink back to you.  If my ignorance shows through, that's why I'm here asking experts.  If it's too painful, then bail MAN.

Now back to business:

g_hWndMain is NEITHER a CWnd or a CFrameWnd.  It is an HWND returned from CreateWindowEx() in other app code.  This happens a few lines above the new in:  CreateTaskWindow(m_hInstance).

m_pMainWnd is  the member variable in CWinThread.  CWinApp derives from that and CMyApp derives from CWinApp.  So, m_pMainWnd has nothing to do with a CFrameWnd.

here it is:
------------
  hWnd = CreateWindowEx(
     WS_EX_TOOLWINDOW,
     AppName,
     Title,
     WS_POPUP,
     fx, fy, 454, 348,       // was fx, fy, SplashWidth, SplashHeight
     NULL,
     NULL,
     hInst,
     NULL);
  if (!hWnd) return false;
  g_hWndMain = hWnd; // g_hWndMain is global
0
 
LVL 23

Expert Comment

by:chensu
ID: 2675056
In this case, you probably should do

m_pMainWnd = new CFrameWnd();

And don't do anything related to m_pMainWnd in the ExitInstance.
0
 
LVL 9

Expert Comment

by:ShaunWilde
ID: 2675546
would he be better having his own window for m_pMainWnd and making it hidden etc and then he can have his child window created by and managed by that new Window.

He therefore does not get a framewindow which looks like what he is tying to avoid and he has a valid m_pMainWnd which shouldn' cause asserts due to not being created etc
 

0
 

Expert Comment

by:bertp
ID: 2676393
Sorry...  It wasn't my intention to insult anyone... just being light- hearted....  and check my profile, I am not really and expert either  
  BUT because last week I reviewed MFC fundmentals in preparation to get MCSD certification, I was able to not only give you (in my opinion, obviously) an excellent answer but also the very page number in an well respected source so that you may confirm or seek further discusion from a true expert, author Jeff Prosise.

Sincerest best wishes,
Bert
0
 

Expert Comment

by:bertp
ID: 2676406
Sorry...  It wasn't my intention to insult anyone... just being light- hearted....  and check my profile, I am not really and expert either  
  BUT because last week I reviewed MFC fundmentals in preparation to get MCSD certification, I was able to not only give you (in my opinion, obviously) an excellent answer but also the very page number in an well respected source so that you may confirm or seek further discusion from a true expert, author Jeff Prosise.

Sincerest best wishes,
Bert
0
 
LVL 30

Accepted Solution

by:
SteveGTR earned 200 total points
ID: 2676424
When you create the g_hWndMain via the line:

CreateWindowEx(
  WS_EX_TOOLWINDOW,
  AppName,
  Title,
  WS_POPUP,
  fx, fy, 454, 348,       // was fx, fy, SplashWidth, SplashHeight
  NULL,
  NULL,
  hInst,
  NULL);

What is AppName? How was it prepared? Here is the help on this field:

lpClassName
[in] Pointer to a null-terminated string or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be in the low-order word of lpClassName; the high-order word must be zero.
If lpClassName is a string, it specifies the window class name. The class name can be any name registered with RegisterClass or RegisterClassEx, or any of the predefined control-class names.



 

0
 

Author Comment

by:rkohler
ID: 2694029
These ideas didn't solve the problem, but I appreciate the effort.
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

If you use Adobe Reader X it is possible you can't open OLE PDF documents in the standard. The reason is the 'save box mode' in adobe reader X. Many people think the protected Mode of adobe reader x is only to stop the write access. But this fe…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
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.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…

746 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

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now