Wait cursors and ignoring messages
Peter Moss -- pmoss@bbn.com
Thursday, December 26, 1996
Environment: VC++ 4.2-flat, NT 4.0
I have a funny little problem that I was wondering if anyone has a neat
solution for.
I have a case where I need to let my process go off and compute its little
head off for anywhere from 5-30 seconds. During that time, I don't want the
user to do anything since the computation affects the state of the Doc, so I
don't want the user to be able to change anything else. I thought, I would
just do the following and be done with it:
CWaitCursor wait;
DoLengthyProcessing();
return;
The hourglass appears and the code works great except for one thing: if the
user clicks around with the hourglass cursor, messages get accumulated in the
queue and are processed after we return from the above code. This caused
undesirable effects on the state of my Doc.
My solution was to create a worker thread to do the lengthy processing. Then
in the UI thread I had to do a few kludgy things to get the desired behavior.
1. In CMainFrame, I maintain a BOOL m_bThreadActive variable. I set this to
TRUE when the worker thread is doing its thing, and FALSE when it is suspended
waiting for a call to wake up.
2. I override CMainFrame::OnSetCursor(). I set the cursor to IDC_WAIT when
m_bThreadActive = TRUE.
3. The only way I could figure out how to ignore messages was to provide a
CMainFrame::PreTranslateMessage() override that checks for m_bThreadActive.
It looks something like:
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class
if (m_bThreadActive) {
// Worker thread active. Filter any msgs you want to go thru here
if (pMsg->message != WM_WORKER_THREAD_FINISHED)
return(TRUE);
}
return CFrameWnd::PreTranslateMessage(pMsg);
}
where WM_WORKER_THREAD_FINISHED is a user-defined msg that is sent via
PostMessage() by the worker thread when it is done. (I use the technique
modeled after the MTDemo app in Jeff Prosise's "Programming Windows 95 with
MFC".) This works, but I'm a bit uneasy about filtering all msgs except the
one that signals that the worker thread is done.
Although this works for me, I was wondering if there was an easier way to
accomplish what I'm trying. Any thoughts?
Thanks,
Pete Moss
pmoss@domaincorp.com
Avinash Saxena -- avi@outlooksoftware.com
Friday, December 27, 1996
Why don't you disable the window which accumulates messages while the =
lengthy processing is going on ? That it it will not accept any messages =
for accumulation.
Else, after your lengthy processing go thru the message queue and =
discard all the messages not required.
Avi
----------
From: Peter Moss[SMTP:pmoss@bbn.com]
Sent: Thursday, December 26, 1996 2:29 PM
To: mfc-l@netcom.com
Cc: Peter Moss
Subject: Wait cursors and ignoring messages
Environment: VC++ 4.2-flat, NT 4.0
I have a funny little problem that I was wondering if anyone has a neat=20
solution for.
I have a case where I need to let my process go off and compute its =
little=20
head off for anywhere from 5-30 seconds. During that time, I don't want =
the=20
user to do anything since the computation affects the state of the Doc, =
so I=20
don't want the user to be able to change anything else. I thought, I =
would=20
just do the following and be done with it:
CWaitCursor wait;
DoLengthyProcessing();
return;
The hourglass appears and the code works great except for one thing: if =
the=20
user clicks around with the hourglass cursor, messages get accumulated =
in the=20
queue and are processed after we return from the above code. This =
caused=20
undesirable effects on the state of my Doc.
My solution was to create a worker thread to do the lengthy processing. =
Then=20
in the UI thread I had to do a few kludgy things to get the desired =
behavior.
1. In CMainFrame, I maintain a BOOL m_bThreadActive variable. I set =
this to=20
TRUE when the worker thread is doing its thing, and FALSE when it is =
suspended=20
waiting for a call to wake up.
2. I override CMainFrame::OnSetCursor(). I set the cursor to IDC_WAIT =
when=20
m_bThreadActive =3D TRUE.
3. The only way I could figure out how to ignore messages was to provide =
a=20
CMainFrame::PreTranslateMessage() override that checks for =
m_bThreadActive. =20
It looks something like:
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)=20
{
// TODO: Add your specialized code here and/or call the base class
if (m_bThreadActive) {
// Worker thread active. Filter any msgs you want to go thru here
if (pMsg->message !=3D WM_WORKER_THREAD_FINISHED)
return(TRUE);
}
return CFrameWnd::PreTranslateMessage(pMsg);
}
where WM_WORKER_THREAD_FINISHED is a user-defined msg that is sent via=20
PostMessage() by the worker thread when it is done. (I use the technique =
modeled after the MTDemo app in Jeff Prosise's "Programming Windows 95 =
with=20
MFC".) This works, but I'm a bit uneasy about filtering all msgs except =
the=20
one that signals that the worker thread is done.
Although this works for me, I was wondering if there was an easier way =
to=20
accomplish what I'm trying. Any thoughts?
Thanks,
Pete Moss
pmoss@domaincorp.com
Kevin Johnson -- kevinj@microcrafts.com
Friday, December 27, 1996
[Mini-digest: 2 responses]
I'm not certain what your needs are for this, but would putting up a
little dialog box (progress type thing, whatever seems best to you) that
eats up whatever messages the user ends up creating (and having your
worker thread busy doing its job).
This way, you can pull up a dialog and ignore the cancel/close/terminate
messages as you see fit, without affecting the rest of your application.
Best of luck,
--Kevin
>----------
>From: pmoss@bbn.com[SMTP:pmoss@bbn.com]
>Sent: Thursday, December 26, 1996 12:29 PM
>To: mfc-l@netcom.com
>Cc: pmoss@bbn.com
>Subject: Wait cursors and ignoring messages
>
>Environment: VC++ 4.2-flat, NT 4.0
>
>
>1. In CMainFrame, I maintain a BOOL m_bThreadActive variable. I set
>this to
>TRUE when the worker thread is doing its thing, and FALSE when it is
>suspended
>waiting for a call to wake up.
>
>2. I override CMainFrame::OnSetCursor(). I set the cursor to IDC_WAIT
>when
>m_bThreadActive = TRUE.
>
>3. The only way I could figure out how to ignore messages was to
>provide a
>CMainFrame::PreTranslateMessage() override that checks for
>m_bThreadActive.
>It looks something like:
>
>BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
>{
> // TODO: Add your specialized code here and/or call the base class
> if (m_bThreadActive) {
> // Worker thread active. Filter any msgs you want to go thru here
> if (pMsg->message != WM_WORKER_THREAD_FINISHED)
> return(TRUE);
> }
>
> return CFrameWnd::PreTranslateMessage(pMsg);
>}
>
>where WM_WORKER_THREAD_FINISHED is a user-defined msg that is sent via
>PostMessage() by the worker thread when it is done. (I use the
>technique
>modeled after the MTDemo app in Jeff Prosise's "Programming Windows 95
>with
>MFC".) This works, but I'm a bit uneasy about filtering all msgs
>except the
-----From: Michael McIntosh
Try this,
CWaitCursor wait;
AfxGetMainWnd()->EnableWindow(FALSE);
DoLengthyProcessing();
AfxGetMainWnd()->EnableWindow(TRUE);
return;
[Michael McIntosh]
P.J. Tezza -- pj@exemplarsoftware.com
Tuesday, December 31, 1996
Use a progress dialog of some kind with a cancel button. In your =
computation code, call the progress dialog's message loop occasionally =
(or post a message to do so). This will give you these benefits:
1) The user can cancel. For a ten second delay, anything less is poor =
software design.
2) The user can estimate how long the work will take and possibly =
perform other tasks (such as getting coffee).
3) The clicks will not accumulate.
4) You can remove your message filter hack. I am not sure what the =
thread is for, but maybe you can remove it as well.
Your choice of progress indicator can be a standard progress meter, text =
messages, etc. For a quick example, use the Component Gallery to insert =
a Progress Dialog.
PJ
pj@exemplarsoftware.com
| Вернуться в корень Архива
|