Re: Batch file and MFC (Properly Terminating Application)
- From: Joseph M. Newcomer <newcomer@xxxxxxxxxxxx>
- Date: Fri, 02 Feb 2007 11:52:56 -0500
So why change the title?
On 2 Feb 2007 06:45:20 -0800, "one-trick-pony" <worldofpain.aamir@xxxxxxxxx> wrote:
Greetings,*****
This is continuation of previous post listed under Subject: Batch file
and MFC. This is how basically I implemented my MFC based program to
mimic batch file execution behavior. But challenge is how to properly
terminate it, when user clicks Cancel or Exit button.
BOOL CDummyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this
automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
BOOL result = AutoRun( ); // mimics
batch file behavior
Simple: there is no way to terminate this if you click a button. This is because the
button click will not be seen. Now, if your AutoRun spins off a *thread*, then things
make sense, in which case the lines below must be omitted.
if (result == FALSE)*****
NEVER, EVER write something as ridiculous as comparing a BOOL to TRUE or FALSE!!!! EVER!
Since a BOOL is ALREADY a boolean value, and 'if' wants a boolean value, why do you need
to decorate it with a gratuitous comparison???? It is as foolish as writing
if( (a > b) == TRUE)
instead of writing
if( a > b )
The correct code would have been
if(!result)
But I believe your whole paradigm is deeply flawed, because you want to abort the
execution, and therefore presuming synchronous execution is completely wrong.
****
{****
EndDialog(1);
What is '1'? What possible meaning does this have. Did you perhaps mean IDOK? If so,
why not write IDOK?
****
}***
Foo( );
Test( );
return TRUE; // return TRUE unless you set the focus to a
control
}
Bool CDummyDlg::AutoRun()
{
// Execute User Code
...
...
// Abort auto execution if an error occurs
if ( FileMissing == TRUE)
return FALSE; // Scott Solution-MVP suggested this
solution it works
...
...
// User clicks on Cancel button to exit (at any time)
/* This is where I have trouble-Upon OnCancel function(below)
execution, SendMessage */
/* and PostMessage are executed but after that OnCancel()
execution program returns */
/* back to execute code
below.
*/
Your fundamental architecture precludes doing this. You need to spin off a thread, and
instead of returning FALSE you should consider aborting by throwing a CException-derived
exception.
****
*****
...
...
...
return TRUE;
}
void CDummyDlg::OnCancel( )
{
// TODO: Add your control notification handler code here
CDialog::OnCancel();
SendMessage(WM_CLOSE);
PostMessage(WM_QUIT);
ABSOLUTELY POSITIVELY WRONG!!!!! NEVER, UNDER ANY CIRCUMSTANCES, POST A WM_QUIT MESSAGE
FROM WITHIN THE MAIN GUI THREAD!!!! Just pretend you never heard of WM_QUIT.
*****
// This should end the execution of program but instead*****
execution continues in AutoRun()
Sure. That's what is SUPPOSED to happen! Because you are off doing whatever AutoRun
does, you will see no mouse clicks, no interaction with the user of any sort, until you
return from the AutoRun function. Only the message pump can do this, and you don't have a
working message pump. You've blocked it.
*****
// function. And after AutoRun is done Foo() and Test() are****
executed and then program
// terminates. Main or overlapped program window disappears but
code continues to run.
Sure. That's what you designed your program to do, and that's what it does.
****
}
I hope this helps in understanding the problem. I would appreciate
any good software engineering solution. Thanks.
BOOL CMyDialog::OnInitDialog()
{
... AppWizard fluff here
PostMessage(UWM_RUN);
return TRUE;
}
LRESULT CMyDialog::OnRun(WPARAM, LPARAM)
{
running = TRUE; // volatile BOOL running declared in dialog class
AfxBeginThread(RunCommands, this);
return 0;
}
****
See my essay on message management on my MVP Tips site
****
/* static */ UINT CMyDialog::RunCommands(LPVOID p)
{
CMyDialog * self = (CMyDialog *)p;
self->RunCommands();
return 0;
}
****
See my essay on worker threads on my MVP Tips site
****
void CMyDialog::RunCommands()
{
BOOL ok = TRUE;
try {
DoFirstThing();
DoSecondThing();
...
DoUmpteenthThing();
}
catch(CMyCommandFailure * e)
{
CString * s = new CString(e->Explanation());
PostMessage(UWM_THREAD_ERROR, (WPARAM)s);
e->Delete();
ok = FALSE;
}
catch(CException * e)
{
CString * s = new CString(IDS_UNKNOWN_EXCEPTION);
PostMessage(UWM_THREAD_ERROR, (WPARAM)s);
e->Delete();
ok = FALSE;
}
PostMessage(UWM_THREAD_FINISHED, TRUE);
}
class CMyCommandFailure : public CException {
public:
CMyCommandFailure(CString s) { why = s; }
CMyCommandFailure(UINT id) { why.LoadString(id); }
CMyCommandFailure() { why = _T(""); }
CString Explanation() { return why; }
protected:
CString why;
}
void DoNthThing()
{
CheckForAbort(IDS_DO_NTH_THING_ABORTED);
if(!SomeAPI())
{
DWORD err = ::GetLastError();
CString msg;
msg.LoadString(IDS_NTH_THING_FAILED);
msg += _T("\n");
msg += ErrorString(err);
throw new CMyCommandFailure(msg);
}
}
void CheckForAbort(UINT id)
{
if(!running)
throw new CMyCommandFailure(id);
}
}
****
Note that you might look into my Logging ListBox control.
Redefine CMyCommandFailure as
class CMyCommandFailure : public CException {
public:
CMyCommandFailure(TraceEvent * e, TraceEvent * err = NULL)
{ why = e; error = err; }
CMyCommandFailure() { why = NULL; error = NULL; }
TraceEvent * Explanation() { return why; }
TraceEvent * ErrorCode() { return error; }
protected:
TraceEvent * why;
TraceEvent * error;
}
So an abort would be
if(!SomeAPI())
{
DWORD err = ::GetLastError();
Fail(new Trace...whatever(...), new TraceFormatMessage(err));
}
void Fail(TraceEvent * e, TraceEvent * err /* = NULL */)
{
throw new CMyCommandFailure(e, err);
}
and the handler would be
catch(CMyCommandFailure * e)
{
PostMessage(UWM_LOG, e->Explanation());
TraceEvent * err = e->ErrorCode();
if(err != NULL)
PostMessage(UWM_LOG, err);
}
you can also post success messages, other useful messages, etc.; see my Logging ListBox
example. The log can be saved to a file, searched for errors, etc.
LRESULT OnThreadFinished(WPARAM wParam, LPARAM)
{
running = FALSE;
...note the thread is finished
if(!wParam)
{
Foo();
Test();
}
Note that if you use a logging listbox, you would NOT want to put the lines below, because
the user needs to see why it failed!
if(wParam)
OnOK();
else
OnCancel();
}
Check out my various essays, such as the logging listbox, worker threads, message
management, maybe even the whole dialog box series, for more detail
joe
Joseph M. Newcomer [MVP]
email: newcomer@xxxxxxxxxxxx
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
.
- Follow-Ups:
- Re: Batch file and MFC (Properly Terminating Application)
- From: one-trick-pony
- Re: Batch file and MFC (Properly Terminating Application)
- References:
- Batch file and MFC (Properly Terminating Application)
- From: one-trick-pony
- Batch file and MFC (Properly Terminating Application)
- Prev by Date: Re: data file: how to start its application
- Next by Date: Re: MFC, Threads, PostMessage, and Reentrant WindowProc
- Previous by thread: RE: Batch file and MFC (Properly Terminating Application)
- Next by thread: Re: Batch file and MFC (Properly Terminating Application)
- Index(es):