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
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
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()->PostMessa ge(WM_CLOS E, 0, 0L);
return;
}
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()->PostMessa
return;
}
ASKER
Andy,
I'd rather do it in my main application class. I'll let you know how I get on...
Thanks
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)
ASKER
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\shi p\atlmfc\s rc\mfc\vie wcore.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
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\shi
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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::OnBnClicked ExitButton ()
{
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
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::OnBnClicked
{
m_bTerminate = TRUE;
this->PostMessage(WM_CLOSE
}
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::OnBnClicked ExitButton ()
{
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::OnBnClicked ExitButton ()
{
m_bTerminate = TRUE;
OnOK(); <<------------------------ ---------
}
void CAuthenticate::OnBnClicked
{
m_bTerminate = TRUE;
this->PostMessage(WM_CLOSE
}
try this
I now have the exit button working fine. Here is my message handler:
void CAuthenticate::OnBnClicked
{
m_bTerminate = TRUE;
OnOK(); <<------------------------
}
ps.. Here the PostMessage also works.
Can you post the contents of the InitInstance here please.
Can you post the contents of the InitInstance here please.
ASKER
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(&Init Ctrls);
CAuthenticate Login;
Login.DoModal();
if(Login.m_bTerminate)
return FALSE;
CWinApp::InitInstance();
if (!AfxSocketInit())
{
AfxMessageBox(IDP_SOCKETS_ INIT_FAILE D);
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(pDocTemplat e);
// 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(cmdI nfo))
return FALSE;
// The one and only window has been initialized, so show and update it
m_pMainWnd->SetRedraw(FALS E);
m_pMainWnd->ShowWindow(SW_ SHOWMAXIMI ZED);
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
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(&Init
CAuthenticate Login;
Login.DoModal();
if(Login.m_bTerminate)
return FALSE;
CWinApp::InitInstance();
if (!AfxSocketInit())
{
AfxMessageBox(IDP_SOCKETS_
return FALSE;
}
// Initialize OLE libraries
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT
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);
// 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),
RUNTIME_CLASS(CLeftView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplat
// 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(cmdI
return FALSE;
// The one and only window has been initialized, so show and update it
m_pMainWnd->SetRedraw(FALS
m_pMainWnd->ShowWindow(SW_
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?
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?
ASKER
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(CAuthent icate, CDialog)
CAuthenticate::CAuthentica te(CWnd* pParent /*=NULL*/)
: CDialog(CAuthenticate::IDD , pParent)
, m_bTerminate(FALSE)
{
}
CAuthenticate::~CAuthentic ate()
{
}
void CAuthenticate::DoDataExcha nge(CDataE xchange* pDX)
{
CDialog::DoDataExchange(pD X);
}
BEGIN_MESSAGE_MAP(CAuthent icate, CDialog)
ON_BN_CLICKED(ID_EXIT_BUTT ON, &CAuthenticate::OnBnClicke dExitButto n)
END_MESSAGE_MAP()
/*
BOOL CAuthenticate::OnInitDialo g()
{
CDialog::OnInitDialog();
return TRUE;
}
*/
// CAuthenticate message handlers
void CAuthenticate::OnBnClicked ExitButton ()
{
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(CAuthentic ate)
public:
CAuthenticate(CWnd* pParent = NULL); // standard constructor
virtual ~CAuthenticate();
// Dialog Data
enum { IDD = IDD_AUTHENTICATE };
protected:
virtual void DoDataExchange(CDataExchan ge* pDX); // DDX/DDV support
//BOOL CAuthenticate::OnInitDialo g();
DECLARE_MESSAGE_MAP()
public:
BOOL m_bTerminate;
public:
afx_msg void OnBnClickedExitButton();
public:
void OnOk(void);
};
Thanks,
Sternocera
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(CAuthent
CAuthenticate::CAuthentica
: CDialog(CAuthenticate::IDD
, m_bTerminate(FALSE)
{
}
CAuthenticate::~CAuthentic
{
}
void CAuthenticate::DoDataExcha
{
CDialog::DoDataExchange(pD
}
BEGIN_MESSAGE_MAP(CAuthent
ON_BN_CLICKED(ID_EXIT_BUTT
END_MESSAGE_MAP()
/*
BOOL CAuthenticate::OnInitDialo
{
CDialog::OnInitDialog();
return TRUE;
}
*/
// CAuthenticate message handlers
void CAuthenticate::OnBnClicked
{
m_bTerminate = TRUE;
//CDialog::OnOk();
this->PostMessage(WM_CLOSE
}
void CAuthenticate::OnOk()
{
}
and the h:
#pragma once
// CAuthenticate dialog
class CAuthenticate : public CDialog
{
DECLARE_DYNAMIC(CAuthentic
public:
CAuthenticate(CWnd* pParent = NULL); // standard constructor
virtual ~CAuthenticate();
// Dialog Data
enum { IDD = IDD_AUTHENTICATE };
protected:
virtual void DoDataExchange(CDataExchan
//BOOL CAuthenticate::OnInitDialo
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?
Is the real app an MDI or an SDI?
ASKER
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(pDocTemplat e);
(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)
move the following code
CAuthenticate Login;
Login.DoModal();
if(Login.m_bTerminate)
return FALSE;
to after the line
AddDocTemplate(pDocTemplat
(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)
ASKER
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.
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.
ASKER
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
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?
ASKER
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(INITCOMMONCONTROLSE X);
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
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(INITCOMMONCONTROLSE
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.)
ASKER
Eureka.
commenting out this line:
BOOL CLustreView::PreCreateWind ow(CREATES TRUCT& 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
commenting out this line:
BOOL CLustreView::PreCreateWind
{
//cs.style = WS_THICKFRAME;
return CFormView::PreCreateWindow
}
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 ?
cs.style=WS_THICKFRAME;
and what about the other style settings that removed ?
ASKER
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
Thanks,
Sternocera
ASKER
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
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) ?
ASKER
Yes, it does (apart from the sunken border),
Thanks,
Sternocera
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);
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
ASKER
Andy,
Just go home from work. I'll let you know how I got on with that when I get a chance,
Regards,
Sternocera
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
ASKER
Andy,
I've added this to my OnInitialUpdate():
ModifyStyleEx(WS_EX_CLIENT EDGE, 0, SWP_FRAMECHANGED);
It's done the job nicely,
Regards,
Sternocera
I've added this to my OnInitialUpdate():
ModifyStyleEx(WS_EX_CLIENT
It's done the job nicely,
Regards,
Sternocera
ASKER
Andy,
The solution you proposed:
CMyFormView::PreCreateWind ow(CREATES TRUCT& cs)
{
cs.style &= ~WS_BORDER;
return CFormView::PreCreateWindow (cs);
}
doesn't work. However, as I've already said, this does work:
ModifyStyleEx(WS_EX_CLIENT EDGE, 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
The solution you proposed:
CMyFormView::PreCreateWind
{
cs.style &= ~WS_BORDER;
return CFormView::PreCreateWindow
}
doesn't work. However, as I've already said, this does work:
ModifyStyleEx(WS_EX_CLIENT
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
ASKER
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
https://www.experts-exchange.com/questions/22746178/MFC-Restoring-Windows-xp-style-scrollbars.html
Thanks for everything Andy,
Regards,
Sternocera
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;
....