Method to Temporarily Suspend the GUI? ( VC++ MFC)

I have a GUI with many buttons and other controls in it.   I have a long-running ( 10 seconds ) algorithm that gets launched in some situations.  While it is running, I don't want the user to poke any of the GUI buttons or controls.   I can put code at the top of each handler to check if its OK or not -- but, as there are many controls, this is a bit onerous.   So, I was wondering if there is some 'global' way that I can temporarily disable the GUI?    Suspend the windows message loop?    My world is:  MS  MFC   VC++

Thanks for ideas.
tullheadAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

ZoppoCommented:
Hi tullhead,

supsending the windows message loop is not a good idea, coz this means even the GUI won't be redrawn in case i.e. an overlapping window is closed.

I think best way to acchive this is to override the PreTranslateMessage function of the main window and to catch all keyboard and mouse-key messages, i.e. somehow like this (pseudo-code):
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
{
	// 'm_bGuiLocked' is a bool member I would add to CMainFrame class, if set to 'true' the messages are blocked
	if ( false != m_bGuiLocked )
	{
		if ( pMsg->message >= WM_MOUSEFIRST && pMsg->message <= WM_MOUSELAST )
		{
			return TRUE;
		}

		if ( pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST )
		{
			return TRUE;
		}
	}

	return CMainFrame::PreTranslateMessage( pMsg );
}

Open in new window

Thus you can even filter some special messages, i.e. if you want to be able to skip the lengthy processing with ESCAPE you can add this as before the two inner 'if's:
	...
	if ( false != m_bGuiLocked )
	{
		if ( pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_TAB )
		{
			// cancel the processing somehow
			...
			// unlock GUI
			m_bGuiLocked = false;
			return TRUE;
		}
		...
	}

Open in new window

This sample is from a Doc/View application, if you need it in a dialog application you can put this into the main dialog's PreTranslateMessage, or, as an alternative, it should be possible to put this into the CWinApp-derived application class, but I wouldn't recommend this, because it's 'deeper' inside as really needed.

Hope this helps,

ZOPPO
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
sarabandeCommented:
I have a long-running ( 10 seconds ) algorithm that gets launched in some situations.  
the solution for this is to put the lengthy function call into a thread. when the function finished it would put the results into a container and send a message to the main (gui) thread such that the results can be shown in the gui thread. the advantage of this is that the gui is responsive while the thread is running asynchronously. the user could cancel the function with a button. in OnButtonClicked you would set a shared bool member variable (defined as 'volatile bool m_bStopThread;') to true. the thread function would check that bool member periodically and return from function if it recognized the stop request.

note, you have to stop the thread when the user tries to exit from dialog in order to prevent from crashing.

alternatively to a thread you could try to put the algorithm into the OnTimer handler of your dialog. if for example you have a loop with 1000 iterations, you could do 100 steps with 10 iterations in the OnTimer, i. e. the MFC framework would call OnTimer periodically and you would do the job in steps of 10 iterations.

a further alternative is to add the following loop in your lengthy function:

            MSG msg = { 0 };
            while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }

Open in new window


this loop will make your gui responsive while the function is running. however, some messages might require a handling which is not suitable to be done while an evaluation was running.

Sara
1
ZoppoCommented:
@sara: I think you probably misunderstood the problem:

As I understand it the lengthy process is already running asynchronous, and what asker wants is to disable the GUI while the asynchronous job is running, without the need to disable each single control by code.

So it's just the other way round: instead of making GUI available during a lengthy process the GUI should be disabled.
0
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

sarabandeCommented:
yes, the asker wanted to disable the gui. but my advice is to let the gui enabled and make the asynchronously running thread safe against user operations. if GUI is disabled the GUI looks freezed (the user won't know whether the program still is working correct) and Windows would add 'no response' to the title if no messages were processed.

if the lengthy function really is runing asynchronously in a thread, there are only a few valid reasons why the user should not be able to use the gui for example to move the progress bar window away from some chart. generally, the gui never should be freezed but if necessary only run with reduced functionality. for that you don't need to disable all controls but simply make your progress bar a modal dialog. then, it is not possible to click outside to the parent dialog and you can provide a cancel button in the progress bar which allows to abort the current evaluation.

Sara
0
ZoppoCommented:
Well, I assume the lengthy process is already running asynchronous (otherwise the question doesn't make much sense because GUI already would be locked and, as you described, replaces with a 'no repsone' ghost-window).

Therefore I suggested the use of PreTranslateMessage to only filter and block input from mouse and keyboard, thus the window won't be locked with 'no repsone', it's i.e. still possible to de-/activate it via CTRL-TAB or the system's task-bar. And, without too much effort it's even possible to i.e. don't filter mouse clicks on some special buttons like i.e. Cancel oder Minimize/Maximize/Close.

Of course I don't know what the lengthy processing does, so I can only guess if it possible to break it intermediateley - probably some request is sent to a server and the caller must wait for a repsonse in order to grant data-consistence between client and a DB running on a server

One disadvantage of my suggeste solution is the user doesn't see a different vizualisation of common controls between the locked and unlocked GUI. If this is wanted/needed I would imlement some kind of loop which in a generic way disables/enables child-controls of the GUI's main window.
0
sarabandeCommented:
a long-running ( 10 seconds ) algorithm that gets launched

actually, the word 'launched' more often was used for invoking another program rather than starting a thread.

anyway, using a modal progress bar would solve the requirement that the user can't use the dialog controls or menus. and by showing the progress the user gets feedback that the evaluation doesn't hang. as already told, i additionally would add a cancel button to the progress bar and if clicked would try to gently stop the asynchronous thread or process, for example by sending a windows message if it is a second process or use a shared bool if it is a thread.

Sara
0
tullheadAuthor Commented:
Zoppo and Sarabande -- you guys are my two favorite experts!  Always very helpful!   So Zoppo's suggestion is exactly what I was looking for.   Sarabande's comments are all good and correct --it may be on a longer term that I re-implement this, but for a quick patch I need to try Zoppo's idea.  I am running the long-calc in its own thread, and so my GUI stays responsive -- the problem is that the long-calc is reading various contextual info that it uses in its calc -- and if the user changes some of that context mid-way thru the calc, it can cause various weird results.   All my fault for how I've set things up -- but in practice, the user shouldn't need or even want to touch the GUI during this calc, so I really don't think anybody will mind if its sort of inactive.  I just want to protect against a user who pokes it at just the wrong time.  In fact, it may only be for a second or 2 that I need to block GUI changes...   Anyway, I haven't yet, but will soon try Zoppo's suggestion.  Thanks!
0
tullheadAuthor Commented:
Thanks - trying to split some points to Sarabande if I can figure out how....
0
ZoppoCommented:
Just one thing: You could even try to simply disable and re-enable all controls in the GUI's main window.

In MFC this can be done in one line by sending a WM_ENABLE or WM_DISABLE message via CWnd::SendMessageToDescendants.
0
sarabandeCommented:
the problem is that the long-calc is reading various contextual info that it uses in its calc -- and if the user changes some of that context mid-way thru the calc, it can cause various weird results.
 

to solve this issue, you could conserve the info from screen needed by the thread in some structure (call it ThreadInfo or similar) and pass a pointer of the ThreadInfo as argument to the thread. since you used MFC you could get data from screen by calling UpdateData(TRUE). then, let the ThreadInfo hold a copy of all the member data owned by the dialog. you also could store the hwnd of the dialog in the ThreadInfo (you get it by 'this->m_hWnd') and if the thread is finished use a

::PostMessage(pThreadInfo->hwndDlg, WM_USER + 123, (LPARAM)(pointer_to_results), 0);

Open in new window


message which the dialog could handle with an ON_MESSAGE handler in the message map. that is much safer than to using the dialog pointer from asynchronously running thread. especially if the user exited from dialog while the thread was running. with the hwnd you always could check with ::IsWindow(pThreadInfo->hwndDlg) whether the dialog window is still valid.

trying to split some points to Sarabande

thanks, but points are not so important and it is balancing on the long run between me and Zoppo ;-)

if you close a question you can both accept one comment as the solution and give assist points to other comments (total should be 1000).

Sara
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.