Link to home
Start Free TrialLog in
Avatar of tullhead
tullheadFlag for United States of America

asked on

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.
ASKER CERTIFIED SOLUTION
Avatar of Zoppo
Zoppo
Flag of Germany 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
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
@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.
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
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.
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
Avatar of tullhead

ASKER

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!
Thanks - trying to split some points to Sarabande if I can figure out how....
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.
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