Reopen modal dialog in MFC - mfc

I need to open a dialog box instantiated from the same class twice. When I try this
CdelmeDlg dlg;
dlg.DoModal();
dlg.DoModal();
The second call opens the dialog only for a split second, then it is closed. My bet was there is a leftover message in the message queue, so I added this in between the calls
MSG msgCur;
while (::PeekMessage(&msgCur, NULL, NULL, NULL, PM_REMOVE))
;
This solves the problem, but it feels like a wrong kind of thing to do. Is there a way to process the leftover message properly?

Don't call EndDialog( IDOK );
To handle the ok or cancel buttons being pressed just inherit OnOk or OnCancel ... Otherwise EndDialog is going to be called twice and you'll get the problem you are getting!

I actually think that YeenFei has a good point here.
It's been a while since i've played with MFC (thank goodness), but from memory, a timer, may or may not be called from the UI thread depending on which one you use. If the timer is being raised on the main UI thread, then a modal dialog will likely halt the main thread until it is dismissed, after which it would be called by the next timer. If the timer is raised on a separate thread, then your dialog is not blocking the main UI thread as it is being shown on a separate thread.
It's does seem more conceivable as YeenFei has pointed out that you want to re-show your dialog each time the timer is raised, hiding it when the user clicks on the button to dismiss it. That way, if the time is raised again, all it does is re-show the dialog whether it is currently open or not.
There is a great post here (www.eggheadcafe.com) about Timers and concurrency that you may find interesting, and may make things clearer than what I managed to accomplish.

Why can't you code it like this:
CdelmeDlg dlg;
dlg.DoModal();
CdelmeDlg dlg1;
dlg1.DoModal();

If you want your application run in background without UI, why not juz temporary hide it? a simple function this->ShowWindow(SW_HIDE) will do the job for you.
i think you should revise your design decision as it seem illogical for an application to behave like what you wanted it to be.

I solved the problem by hiding the dialog instead of closing it and launching another thread that first sleeps and then unhides the dialog.

May be your code has line:
m_pMainWnd = &dlg;
If so, than application after first call of DoModal() will finished, all other call of DoModal() will return -1. From MSDN:
The Microsoft Foundation Class Library will automatically terminate your thread when the window referred to by m_pMainWnd is closed. If this thread is the primary thread for an application, the application will also be terminated.

Related

How to return from exec method programmatically in static singleton class

I am developing Qt application (Qt version 4.7.3) on SBC6000x board.
I have a MessageBox class derived from QDialog. I have made this class singleton.
Whenever a messagebox is to be show I am using .exec method to show it.
There are few places where I need to show messageboxes one after another.
So, to show new messagebox, I have to close previous one and show new one.
e.g. When Messagebox is open and at same time I receive an error from background I have to close the messagebox which is currently shown and show the one with error.
To closes previous dialog I have exposed CloseDlg method from messagebox class and trying to close it.
Inside this CloseDlg I am emitting finished signal.
void CMsgBox::CloseDlg()
{
if (NULL != CMsgBox::m_msgBox)
{
if(CMsgBox::m_msgBox->isVisible())
{
emit CMsgBox::m_msgBox->finished(0);
//QApplication::processEvents();
}
}
}
and calling it as
CMsgBox::CloseDlg();
My show method is :-
int CMsgBox::showMsgBox(Icon icon, const QString &textMsg, const QString &okBtnText)
{
if (CMsgBox::m_msgBox == NULL)
{
CMsgBox::m_msgBox = new CMsgBox();
}
CMsgBox::m_msgBox->setText(textMsg);
CMsgBox::m_msgBox->setIcon(icon);
CMsgBox::m_msgBox->setOkBtnText(okBtnText);
CMsgBox::m_msgBox->exec();
return CMsgBox::m_msgBox->m_btnPressed; //return, unblock the call
}
Again when I call showMsgBox,it is showing me following warning.
QDialog::exec: Recursive call detected
Problem is, it doesn’t return from previous exec call (unless we return, as commented above //).
I tried same with close(), accept(), reject() methods instead of finished() event but nothing worked.
What is the way to return from previous exe call and achieve above scenario? Any help is welcome.
What you have here looks like a race condition. A modal QDialog runs its own event loop, so your application behaves like a multithreaded application and you need to take care of concurrency and race conditions.
When you receive a second in your main event loop, you call CMsgBox::CloseDlg() and CMsgBox::showMsgBox() in quick succession. However, CloseDlg() tells the dialog's event loop to return, but CloseDlg() actually returns before the dialog's event loop is done cleaning up, and showMsgBox() attempts to call exec() on a dialog which hasn't finished exiting yet.
What you need to do is, when you call CMsgBox::CloseDlg(), connect to the finished(int) signal, and only when you receive the finished(int) can you safely exec() the dialog again.
NOTE: When connecting to the finished(int) signal, make sure to use a Qt::QueuedConnection instead of a Qt::DirectConnection which is the default.
So, you need modeless dialog box. As explained in their documentation :
Modeless dialogs are displayed using show(), which returns control to the caller immediately.
Therefore, instead of showing the box with exec(), show it with show().
Alternative to show() method suggested in another answer is, use QDialog::open(). It will return, but will still give you modal dialog, so the rest of the GUI will be disabled until you close it.

MFC dialog close issues

Good day!
I have an MFC dialog with progress.
Dialog automatically closes after reaching 100% using PostMessageW(WM_CLOSE).
The problem is, when, during progress, I'm moving dialog over the screen, dialog is not closing and WM_CLOSE message is ignored. Any suggestions? Thanks.
For a modal dialog you shouldn't really need to use a WM_CLOSE message.
Normally you'd use the OK or Cancel button events to close it, call the EndDialog method from functional code or just return when your processing is complete (assuming that its the process run as soon as the dialog is initialised). You can set your return value at the same time e.g. EndDialog(2);.
Either way the dialog will close once th current message handler returns, so there could well be a delay, in closure but it shouldn't be much.
Is the activity behind progress bar done in a separate thread? It look like to be the case otherwise when you drag the dialog the progress bar would have froze until you release the dialog than it would have resumed. This means you might have to look into inter thread communication, how the message is being posted to the dialog HWND.
It might have to do with the dialog being in freeze (no activity) state while you are dragging it which seems to be normal windows behavior. If that is the case you could use signals/CEvent to tell the dialog to close down.

Modeless dialogs and accelerators

I want to perform some action when the user presses CTRL+S inside a modeless dialog.
Accelerators would be perfect for this, except that I don't have control over the thread's message loop (think plugin), so I can't call TranslateAccelerator.
A nested message loop is not an option because the main application does a lot of processing in between calls to PeekMessage.
Is there some way I can 'force' the existing message loop to handle my accelerator?
Is there any other way besides accelerators to catch CTRL+S?
I thought about using a Window hooks on WH_GETMESSAGE, which gets called before returning from GetMessage or PeekMessage. But I'm not sure what would happen after I successfully called TranslateAcellerator, I can't let the application know I handled it.
WH_MSGFILTER would require the app to implement a call to CallMsgFilter, which it doesn't from a quick glance with a debugger.
My last idea was subclassing the control which is what I'm trying to avoid. That would require some mechanism to signal the keypress event to the parent window, which I don't think is a great design. Also, if I add more controls I would have to subclass every single one.
Thanks for any hints.
This is a fairly infamous interop problem, it rears its ugly head also when using Winforms to implement UI in a native program. Microsoft's recommendation is a rough-and-tumble one: start your own thread to display the window so you can pump your own message loop. You'll need SetParent() to avoid Z-order problems. Scary stuff, do consider a modal dialog thrice before committing to this.
Can you use WM_GETDLGCODE? http://msdn.microsoft.com/en-us/library/ms645425(v=vs.85).aspx

FindWindowEx not able to find window handle

I am trying to find the handle to a dialog as soon as it opens.
Now as soon as dialog is opened I try to call FindWindowEx for that dialog in a separate thread but it returns NULL.
I then put some sleep before calling FindWindowEx. It works some time after putting sleep.
It looks like FindWindowEx is getting called before even dialog is created and sleep is helping to create the dialog and hence some times it work.
Now I have put some random value in sleep. And it doesnot look a good approach since it can fail anytime.
Is there any full proof approach so that I may get handle every time via FindWindowEx without making thread to sleep.
If the dialog you are looking for is your dialog -- that is, you control the code -- then you can send a message from your dialog to your watching app that says, "Oh, hi there!"
If the dialog is not yours, and you don't want to spin, you can create a Windows hook on the WM_CREATE message.
A very straightforward solution would be to call FindWindowEx repeatedly in a loop.
HWND h = NULL;
while (1) {
h = FindWindowEx(...);
if (h) {
break;
}
Sleep(100);
}
That's not bullet-proof - it's an infinite loop if the dialog nevers opens, or is closed too quickly (although that's unlikely). To catch both cases, let the main thread (which creates and runs the dialog) maintain a simple bool property that the worker thread queries to find out whether there's still a dialog around.

Application wide periodic tasks with Dialog Based MFC application

In Single Document Interface (SDI) or Multiple Document Interface (MDI) MFC application, I created an application wide timer in the View. The timer will tick as long as the application is running and trigger some periodic actions.
How can I do the same with Dialog Based MFC application?
Should I create Thread's Timer (SetTimer with NULL HWND) and pass a callback function to it?
Should I create worker threads? My experience with other projects was when I tried to display some feedback GUI from non-GUI/worker threads, I need to roll out my own "delegate"/command pattern and a "delegate invoker"/command invoker. The worker thread will send message (I think using message is safer than direct function call when dealing across thread-boundary, CMIIW) to the UI-thread. and the UI-thread will be the "delegate"/command invoker. Failing to do this and to make sure that the windows/dialogs have the correct parent will result in bizzare behaviors such as the Application suddenly disappears to the background; Window/Dialog that is shown behind the current window/dialog and causing the current window to be unresponsive/unclickable. Probably I was doing something wrong but there were so much problems when dealing with threads.
Are there best practices for this?
A timer works as well in a dialog-based application as an SDI or MDI app. OTOH, timers are (mostly) a leftover from 16-bit Windows. If you want to do things periodically, a worker thread is usually a better way to do it (and yes, Windows Mobile supports multiple threads).
Edit: in a dialog-based application, the main dialog exists for (essentially) the entire life of the application. Unless you really need the timer during the milliseconds between application startup and dialog creation or dialog destruction and application exit, just attach it to the dialog. Otherwise, you can attach it to the main window -- which MFC creates and destroys, even though it's never displayed.
If you use the MFC Wizard to create the Dialog based app, you probably have a hidden view window as well as a dialog window. The view window creates the dialog with DoModal(), which runs the dialog in the same thread, effectively suspending the view window.
While the dialog is open, the view window will not process any events. So, if the view window owns the timer, it will not process the timer events.
The simplest solution is to create the timer in the dialog and let the dialog handle the timer messages.
IMO, use the Timer if it solves the problem. As you've mentioned a Worker Thread interacting with the UI, in MFC, can be more trouble than its worth sometimes.
If the problem is simple enough for a timer to suffice, thats what i'd use (Remember KISS)
SetTimer does not have to be handed a window to work, it can call a callback method.
You can use that in your application - declare in your CWinApp (or anywhere really)
static void CALLBACK OnTimer(HWND, UINT, UINT, DWORD);
Then in the InitInstance call SetTimer(0, [eventid], [time period], OnTimer);
In OnTimer you can get back to the CWinApp instance via AfxGetApp() or theApp since there is only one.
Second attempt: my previous answer was dne in a hurry and was not correct.
Your basic vanilla MFC Dialog app only uses one thread. The main thread starts with a class derived from CWinApp. In the InitInstance() method it launches the dialog using CDialog::DoModal(). This function doesn't return until the dialog is closed.
While the dialog is running, the CWinApp class does not process any messages, so won't see a WM_TIMER.
There are many ways around this.
Let the first dialog own the timer and make all other dialogs children of it. This might be OK, depending on your dialog requirements, but it might be too restrictive.
Launch the first Dialog as modeless, i.e. use Create() instead of DoModal(). Create() returns straight away (putting the Dialog into a different thread). You can then create a message loop in the CWinApp class and process timers there. You'll have to use thread timers instead of window timers as the CWinApp class doesn't have a window. (or you could create a hidden window if that is more convenient).
You can hack the dialog's mesage loop and make it pass messages to the CWinApp class' message handler. That is quite complex and not for the faint hearted.
You can create a dedicated timer thread. You'd probably do that from the CWinApp class before it creates the dialog, but other strategies are possible.
Do any of those schemes sound like they fit your needs? If not, maybe you can explain your needs more fully and we might be able to come up with something appropriate.