Link to home
Start Free TrialLog in
Avatar of sternocera
sternocera

asked on

MFC: ID_APP_EXIT message as a means of ending a program

My MFC application needs a dialog to log the user into a database. Having already more or less implemented my main user interface, I decided to create a dialog before the user ever sees the main user interface. I instantiate a CDialog derived class in the usual way inside my CDocument derived class's constructor.

This almost works perfectly, except when the user chooses to exit, and not login. My exit button send an ID_APP_EXIT message, which causes an Unhandled exception. Why so? Is there another way to exit the application? Is placing the code in my document's constructor like this a good idea?,

Thanks,
Sternocera
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

I *guess* the main window doesn't exist at that point - hence the exception.

I'd be tempted to have something like the following in the InitInstance

BOOL CMyApp::InitInstance()
{
  CMyDialog dlg;
  dlg.DoModal();
  if(dlg.m_bTerminate)  //m_bTerminate is a boolean flag to determine if the user wanted to quit that app.
    return FALSE;

....


Depending on the architecture of your app a possible alternative would be in the OnInitialUpdate of the view

CMyView::OnInitialUpdate(...)
{
  CMyDialog dlg;
  dlg.DoModal();
  if(dlg.m_bTerminate)  //m_bTerminate is a boolean flag to determine if the user wanted to quit that app.
    {
      AfxGetMainWnd()->PostMessage(WM_CLOSE, 0, 0L);  
      return;
    }
Avatar of sternocera
sternocera

ASKER

Andy,
I'd rather do it in my main application class. I'll let you know how I get on...

Thanks
In the InitInstance - initialise the common controls before the dialog.  (Forgot to mention that initially)
Andy,

I'm still getting an assetion failure when the dialog sends an IDOK message when the "Login" button is pressed. Specifically:

---------------------------
Microsoft Visual C++ Debug Library
---------------------------
Debug Assertion Failed!

Program: ...
File: f:\rtm\vctools\vc7libs\ship\atlmfc\src\mfc\viewcore.cpp
Line: 63

For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.

(Press Retry to debug the application)
---------------------------
Abort   Retry   Ignore  
---------------------------

If I ignore, everything works fine. However, I'm not willing to live with an assertion failure. Why do you think I'm getting this assertion failure?

If I call my member function that changes the value of m_bTerminate by pressing the exit button, and then press login(IDOK), the app exits without error, which is what I'd expect, because there is no mechanism for the dialog to close built into my exit button message handler.

What CDialog member function can I call from within the message handler function that'll close the login dialog, so that InitInstance() can return FALSE, and so that the user won't have to subsequently click on the login button to get InitInstance() to return FALSE in the way I've described?

Regards,
Sternocera
ASKER CERTIFIED SOLUTION
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Andy,

Andy,

My classes are the same as the ones you posted in every appreciable manner, and yet I'm still getting that same assertion error.

I now have the exit button working fine. Here is my message handler:
void CAuthenticate::OnBnClickedExitButton()
{
      m_bTerminate = TRUE;
      this->PostMessage(WM_CLOSE, 0, 0L);  
}

However, If I click on the "X" to close the window, I get the same assertion failure I get with the IDOK message/login button.  If I ignore, I'll go through to my main application just as with the IDOK message/login button.

Please advise,
Regards,
Sternocera
I now have the exit button working fine. Here is my message handler:
void CAuthenticate::OnBnClickedExitButton()
{
      m_bTerminate = TRUE;
      this->PostMessage(WM_CLOSE, 0, 0L);  
}

try this
I now have the exit button working fine. Here is my message handler:
void CAuthenticate::OnBnClickedExitButton()
{
      m_bTerminate = TRUE;
      OnOK();  <<---------------------------------
}
ps.. Here the PostMessage also works.

Can you post the contents of the InitInstance here please.
Here it is:

BOOL CLustreApp::InitInstance()
{
      

      // InitCommonControlsEx() is required on Windows XP if an application
      // manifest specifies use of ComCtl32.dll version 6 or later to enable
      // visual styles.  Otherwise, any window creation will fail.
      INITCOMMONCONTROLSEX InitCtrls;
      InitCtrls.dwSize = sizeof(InitCtrls);
      // Set this to include all the common control classes you want to use
      // in your application.
      InitCtrls.dwICC = ICC_WIN95_CLASSES;
      InitCommonControlsEx(&InitCtrls);
      CAuthenticate Login;
      Login.DoModal();
      if(Login.m_bTerminate)
            return FALSE;

      CWinApp::InitInstance();

      if (!AfxSocketInit())
      {
            AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
            return FALSE;
      }

      // Initialize OLE libraries
      if (!AfxOleInit())
      {
            AfxMessageBox(IDP_OLE_INIT_FAILED);
            return FALSE;
      }
      AfxEnableControlContainer();
      // Standard initialization
      // If you are not using these features and wish to reduce the size
      // of your final executable, you should remove from the following
      // the specific initialization routines you do not need
      // Change the registry key under which our settings are stored
      // TODO: You should modify this string to be something appropriate
      // such as the name of your company or organization
      SetRegistryKey(_T("Lustre EPOS solutions"));
      LoadStdProfileSettings(4);  // Load standard INI file options (including MRU)
      // Register the application's document templates.  Document templates
      //  serve as the connection between documents, frame windows and views
      CSingleDocTemplate* pDocTemplate;
      pDocTemplate = new CSingleDocTemplate(
            IDR_MAINFRAME,
            RUNTIME_CLASS(CLustreDoc),
            RUNTIME_CLASS(CMainFrame), // main SDI frame window      
            RUNTIME_CLASS(CLeftView));
      if (!pDocTemplate)
            return FALSE;
      AddDocTemplate(pDocTemplate);



      // Parse command line for standard shell commands, DDE, file open
      CCommandLineInfo cmdInfo;
      ParseCommandLine(cmdInfo);


      // Dispatch commands specified on the command line.  Will return FALSE if
      // app was launched with /RegServer, /Register, /Unregserver or /Unregister.
      if (!ProcessShellCommand(cmdInfo))
            return FALSE;

      // The one and only window has been initialized, so show and update it
      m_pMainWnd->SetRedraw(FALSE);
      m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
      m_pMainWnd->SetRedraw(TRUE);
      m_pMainWnd->UpdateWindow();
      // call DragAcceptFiles only if there's a suffix
      //  In an SDI app, this should occur after ProcessShellCommand
      return TRUE;
}


Thanks
Odd.

Could you do something quick a a test.

Make a new project (SDI, just accept the defaults).
Add a new dialog with one button onto it.
Use the .cpp/.h that I posted earlier and try to launch it in the InitInstance.

Does that one crash?
Andy,

I've done that. it does not crash.

I notice you don't call the base implementation of InitInstance(), unlike my wizard generated app.

Here is my dialog .cpp:
// Authenticate.cpp : implementation file
//

#include "stdafx.h"
#include "Lustre.h"
#include "Authenticate.h"


// CAuthenticate dialog

IMPLEMENT_DYNAMIC(CAuthenticate, CDialog)

CAuthenticate::CAuthenticate(CWnd* pParent /*=NULL*/)
      : CDialog(CAuthenticate::IDD, pParent)
      , m_bTerminate(FALSE)
{

}

CAuthenticate::~CAuthenticate()
{
}

void CAuthenticate::DoDataExchange(CDataExchange* pDX)
{
      CDialog::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(CAuthenticate, CDialog)
      ON_BN_CLICKED(ID_EXIT_BUTTON, &CAuthenticate::OnBnClickedExitButton)
END_MESSAGE_MAP()
/*
BOOL CAuthenticate::OnInitDialog()
{
      CDialog::OnInitDialog();
      
      return TRUE;
}
*/
// CAuthenticate message handlers

void CAuthenticate::OnBnClickedExitButton()
{
      m_bTerminate = TRUE;
      //CDialog::OnOk();
      this->PostMessage(WM_CLOSE, 0, 0L);  
}

void CAuthenticate::OnOk()
{

}

and the h:

#pragma once


// CAuthenticate dialog

class CAuthenticate : public CDialog
{
      DECLARE_DYNAMIC(CAuthenticate)

public:
      CAuthenticate(CWnd* pParent = NULL);   // standard constructor
      virtual ~CAuthenticate();

// Dialog Data
      enum { IDD = IDD_AUTHENTICATE };

protected:
      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
      //BOOL CAuthenticate::OnInitDialog();

      DECLARE_MESSAGE_MAP()
public:
      BOOL m_bTerminate;
public:
      afx_msg void OnBnClickedExitButton();
public:
      void OnOk(void);
};


Thanks,
Sternocera
OK - something is odd somewhere.
Is the real app an MDI or an SDI?
The real app is an SDI.
OK, lets give this a try (bit of desparation creeps in)

move the following code

CAuthenticate Login;
      Login.DoModal();
      if(Login.m_bTerminate)
            return FALSE;

to after the line
      AddDocTemplate(pDocTemplate);


(There should be a main window for the app at this point - so we might have a memory leak, but this is just to test at present)
Unfortunately, that hasn't helped. Everything is exactly the same as before.
I'm stumped.
You can see from the test app that appears to be a valid method, I just don't see why it fails in your real app.



ps.
If you asked for a recommendation where to put this sort of functionality I would say in the OnInitialUpdate.  However that would only be suitable if the design of your app allowed it.
I've reverted to an earlier version of my source using my version control system, and I STILL get the assertion failure.

Something spooky is going on. I'm going to bring my source to another machine and see if that throughts the assertion failure,
Regards,
Sternocera
Are you doing anything in the constructor of the CWinApp based class?
yes:
CLustreApp::CLustreApp()
{
      // from http://www.0ok.de/page/articles/1.txt . Relates to getting
      // back windows xp theme after changing encoding
      INITCOMMONCONTROLSEX icce;
      icce.dwSize = sizeof(INITCOMMONCONTROLSEX);
      icce.dwICC = ICC_WIN95_CLASSES;
      InitCommonControlsEx(&icce);
      // TODO: add construction code here,
      // Place all significant initialization in InitInstance
      
}

This is to ensure that windows xp themes are used. When I changed character encoding from unicode to multi-byte,  I lost windows xp themes, and had to regain them by following this tutorial:

http://www.0ok.de/page/articles/1.txt

Thanks
You also have that in the InitInstance - I'd comment this code out in the constructor.  (I don't know if it causes problems to do it twice.)
Eureka.

commenting out this line:

BOOL CLustreView::PreCreateWindow(CREATESTRUCT& cs)
{
      //cs.style = WS_THICKFRAME;
      return CFormView::PreCreateWindow(cs);
}

fixes the assertion failure, but now I have to see an unsightly sunken border on my CFormView derived class, which was the reason I overloaded the function. So, If I could remove the overloaded function, and change the CFormView's style in some other way that does not cause an assertion failure, I'd have it all.

Thanks Andy
whines.


cs.style=WS_THICKFRAME;

and what about the other style settings that removed ?
The code in InitInstance is produced by the wizard. I've removed it and moved that other code from the constructor to where the first code was in InitInstance. Ockham's razor and all that.

Thanks,
Sternocera
Andy,

For whatever reason, the problem didn't manifest itself till today. I didn't realise that it would override other settings. I'm sorry for the inconvenience,

Regards,
Sternocera
Does it work now (apart from the sunken border) ?
Yes, it does (apart from the sunken border),

Thanks,
Sternocera
WS_THICKFRAME   Creates a window with a thick frame that can be used to size the window.

That has an interesting side effect if your app has a status bar (put the mouse at the bottom right of the view)

Does this help in the PreCreate of the view?
      cs.style &= ~WS_BORDER;
      return CFormView::PreCreateWindow(cs);
Andy,

Just go home from work. I'll let you know how I got on with that when I get a chance,
Regards,
Sternocera
No problems
Andy,

I've added this to my OnInitialUpdate():

ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);

It's done the job nicely,
Regards,
Sternocera
Andy,

The solution you proposed:
CMyFormView::PreCreateWindow(CREATESTRUCT& cs)
{
      cs.style &= ~WS_BORDER;
      return CFormView::PreCreateWindow(cs);
}

doesn't work. However, as I've already said, this does work:

ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED);

However, this leaves one last niggling problem that I didn't have before, when I had the code that caused the assertion failure: When the window is resized , the scroll bars are initially windows 95 style gray, rather then windows xp style blue. When I use the scroll bar, they turn windows xp blue.

How can I have it so that the scroll bars are initially windows xp blue?

Regards,
Sternocera
I've decided to make a whole new question about that:

https://www.experts-exchange.com/questions/22746178/MFC-Restoring-Windows-xp-style-scrollbars.html

Thanks for everything Andy,
Regards,
Sternocera