Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 519
  • Last Modified:

Can't fix memory leak

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
rkohler
Asked:
rkohler
  • 8
  • 6
  • 5
  • +2
1 Solution
 
SteveGTRCommented:
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
 
rkohlerAuthor Commented:
Thanks for the input, but this change produces exactly the same ASSERTion error.
0
 
rkohlerAuthor Commented:
Thanks for the input, but this change produces exactly the same ASSERTion error.
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!

 
chensuCommented:
m_pMainWnd->Detach();
delete m_pMainWnd;
::PostMessage(g_hWndMain, WM_CLOSE, 0, 0);
0
 
rkohlerAuthor Commented:
Thanks, but the ::PostMessage() call change produces the same ASSERTion error.

- Rob
0
 
chensuCommented:
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
 
SteveGTRCommented:
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
 
rkohlerAuthor Commented:
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
 
rkohlerAuthor Commented:
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
 
chensuCommented:
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
 
rkohlerAuthor Commented:
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
 
SteveGTRCommented:
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
 
chensuCommented:
Are you sure you call m_pMainWnd->Detach() before the delete?
0
 
bertpCommented:
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
 
chensuCommented:
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
 
bertpCommented:
I feel your pain man ;-)
0
 
bertpCommented:
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
 
rkohlerAuthor Commented:
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
 
chensuCommented:
In this case, you probably should do

m_pMainWnd = new CFrameWnd();

And don't do anything related to m_pMainWnd in the ExitInstance.
0
 
ShaunWildeCommented:
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
 
bertpCommented:
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
 
bertpCommented:
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
 
SteveGTRCommented:
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
 
rkohlerAuthor Commented:
These ideas didn't solve the problem, but I appreciate the effort.
0

Featured Post

Hire Technology Freelancers with Gigs

Work with freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely, and get projects done right.

  • 8
  • 6
  • 5
  • +2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now