MFC EndDialog crashes when modal dialog does not have focus - c++

I am closing a modal dialog after I end a task, inside a separate thread from where I created a modal dialog:
void CmodguiApp::_notify_task_end() {
processingDialog->EndDialog(0);
}
This works fine if my application has focus (therefore the modal dialog has focus). But this causes the application to crash if I change window while the modal dialog is on (for instance, if I leave the application processing and switch to Firefox or so).
What could be wrong?

Do not end the dialog with EndDialog. Instead PostMessage with WM_CLOSE or WM_QUIT to the dialog window.

Related

open a dialog behind a modal dialog or after it is closed

I have an application which has several background tasks running in non-GUI threads which may time to time require some user interaction so they send signal to the main thread and the corresponding slot creates a dialog and shows it to the user. Meanwhile the task thread is waiting in a blocking event loop. Once the user answers the dialog and closes it, the task event loop is signaled to quit and the task resumes.
There is however a problem. In the GUI thread I can still use the application which time to time shows some modal dialogs. When there is a modal dialog already shown and then the background tasks requests another dialog to be opened, this task-related dialog is displayed in front of the modal dialog. But this new dialog is not modal and the modal dialog is hidden behind it. The non-modal one therefore is not responsive, the application feels like it got stuck.
My idea was to display the new dialog always behind the modal dialog, which I believe I can get with QApplication::activeModalWidget(). But I do not know how to do this. How can I show a dialog behind another dialog but still in front of the main window (which is a parent of both dialogs)? I tried to call QApplication::activeModalWidget()->activateWindow() after I show the non-modal one but his causes blinking of windows and moreover I can still click into the new non-modal dialog hiding the modal one. So this is not a perfect solution.
Or do you see any other solution?
Maybe I could implement a queue of dialogs, and once there is any modal dialog visible, then the new background task-related dialog would not be shown, only enqueued and shown once the modal dialog is closed. However this feels more fragile solution to me.
Any ideas?
UPDATE: I redefined the question by adding "or after it is closed" becasue this works for me too.
I found a solution which seems to work well and which shows the non-modal dialog only after the modal dialog is closed.
QWidget *nonModalDialog = ... // creates the non-modal dialog
nonModalDialog->setAttribute(Qt::WA_DeleteOnClose);
QWidget *modalDialog = qApp->activeModalWidget();
if (modalDialog == nullptr)
{
// no modal dialog, we can show the non-modal one now
dialog->show();
}
else
{
// we must wait until the modal one is closed
QObject::connect(modalDialog, &QWidget::destroyed, nonModalDialog, &QWidget::show);
}
This seems simple and robust.
I think you are looking for QWidget::raise(). You should be able to use QApplication::activeModalWidget()->raise() after calling dialog->show() on your non-modal dialog.
If you're getting into situations where you have multiple modal and non-modal dialogs, all launched in different order, this might not be enough to solve the problem. You might raise one modal dialog only to have others behind other non-modal dialogs and end up stuck again. You should consider keeping a reference to the collection of modal dialogs that are currently active so that you can make sure they are always on top of the non-modal dialogs.

MFC, How to close Modal Dialogs from Main Window?

We have a dialog X, that can be opened by different threads (also main thread) as modal. Multiple modal X dialogs, at the same time from different threads is possible.
We want to close all X (only X) modal dialogs, if the main window handles a SC_CLOSE message.
The question is; From the main window, how can we close all modal X dialogs, when it recieves the SC_CLOSE message? Only X! So, PostQuitMessage doesn't help, because it closes whole app, we don't want to close the app, if other modal dialogs like Y, Z ... are opened.
Info: Main window can handle SC_CLOSE message, when we have the opened modal X dialog from main thread also from other threads. And X has no child modal window.
My current solution is;
All modal X dialogs have been registered in a collection (thread-safely), during openning of them.
if the main window becomes the SC_CLOSE-message, then iterating all dialog is now possible. Then use one of those 2 lines, for each dialog.
::PostMessage(pDlg->GetSafeHwnd(), WM_COMMAND, IDOK, 0); //end dialog with idok
::PostMessage(pDlg->GetSafeHwnd(), WM_CLOSE, 0, 0); //or, close dialog
I know it is not the best solution, but hope helps someone.
Don't try to iterate all childs of main window to find dialogs. it won't help. Owner of the dialogs, doesn't mean that it is the parent of the dialogs.

Detecting the WM_CLOSE event on main application window when a modal dialog is active?

Is it possible for the the main application window to detect a WM_CLOSE event when a modal dialog is active?. If not, is there any way of detecting the WM_CLOSE event because the event handler for the dialog does not detect it either.
To observe this behaviour for yourself, open the 'about' menu of notepad and then right-click on the notepad icon in the taskbar and select the 'close window' option, it will have no effect.
Does anyone know of a way to catch a WM_CLOSE event for an application when a modal window is active?
When selecting "close" by right-clicking an application's icon in the task bar no WM_CLOSE is sent.
What happens is that the application's main window is sent a WM_SYSCOMMAND with wParam set to SC_CLOSE.
For an example on how to exploit this "feature" please see my other answer on this here.

c++, mfc - messagebox display, main window has a cancel button. prevent cancel while messagebox is active

I have a queer sort of problem. Consider the following scenario:
Main window creates a child dialog on click of some button.
Child dialog does some task in a worker thread.
An error occurs during that task causing a message box to be displayed saying something along the lines of "Yikes! Something went wrong.".
Child dialog's 'Cancel' button is clicked on causing the child dialog to be closed.
Message box is still active! Clicking on anything in the message box = crash.
Pseudocode of how things are happening: (please ignore syntactic correctness here)
MainWindowClass mainObj;
void MainWindowClass::OnSomeButtonClick()
{
SomeDialogClass someDialogObj;
someDialogObj.DoModal();
}
int MainWindowClass::doTask()
{
// Do work
if(ERROR)
{
MessageBox("Yikes! Something went wrong.", "Error", MB_OK);
return ERROR;
}
}
///////////////////////////////////////////////////////////////////
// Meanwhile, in another file,
extern MainWindowClass mainObj;
void SomeDialogClass::OnCancel()
{
// Do all cleanup and close dialog
}
int SomeDialogClass::workerThreadFunc()
{
return mainObj.doTask();
}
int SomeDialogClass::DoModal()
{
AfxBeginThread(workerThreadFunc);
// Do all other work and then wait for the worker thread
}
My question is twofold:
On cancel, is there a way to know if any message boxes/dialogs in
the same application are active?
Is my entire design wrong and should I be doing something else altogether?
I thought one of the main reasons to use a modal dialog was its ability to prevent focus from going to parent dialogs. Yet, when that error message box is shown, I can happily click on the dialog and then click on 'Cancel' and pretend that the error message box never showed.
Rather than have your worker thread bring up a modal message box, it should signal the error condition back to the UI thread. My experience of MFC and multi-threading is that having all UI handled in the same thread makes things much simpler and removes these types of error. Having two threads potentially bringing up modal dialogs at the same time is asking for trouble.
I'm kind of new at this, but my suggestions would be if the child is going to be cancelled when there is an error then destroy it before launching the error.
As far as I can tell, the SomeDialogClass is modal, then the parent launches another modal dialog, the MessageBox. Now, if SomeDialogClass was modeless then the message box would appear on top since SomeDialogClass would then be a child of the entire desktop and the sole child of MainWindowClass would be the MessageBox.
I'm under the impression that modal only prevents focus from going to things underneath the modal dialog, not what is launched on top or after it is open. I've done a modal dialog, launched a modeless from it, and could move both around the screen just fine. Additionally, I created a WM_MESSAGE from the modeless dialog back to the modal to announce when it was done so the modeless could finish.

Opening a window in other method than `DialogBox`

I have a dialog IDD_WINDOW_INFO that has to be opened when the user clicks a button or a menu item in my C++ Win32 application. The method that I use to open the dialog is in the following line:
DialogBox(hInstance, MAKEINTRESOURCE(IDD_WINDOW_INFO), hMainWindow, WindowInfoProc);
but my problem is that when that dialog box opens, the user cannot operate with the main window of my application. So what can I do to have both windows active?
You are calling DialogBox which shows the dialog modally. When a modal dialog is shown, the other owning windows are disabled and only the modal dialog can accept input. That is the very essence and intent of a modal dialog. The idea is that you can interact only with the dialog, and cannot interact with the other windows.
Another answer suggests passing NULL as the hWndParent parameter to DialogBox. That's not the solution. That will result in you having an unowned window. Yes, you will be able to interact with the main window, but when you do so your main window will appear on top of the dialog. That's because the ownership is set incorrectly. I recommend that you read about window ownership to better understand the issue.
The correct solution to your problem is to show a modeless dialog. A modeless dialog allows you to interact with the other windows in your application. And that's exactly what you ask for in the question.
You show modeless dialogs by calling CreateDialog followed by ShowWindow. This MSDN article shows an example: Using Dialog Boxes.
If I recall correctly, you can either pass NULL instead of the handle to the parent window or change the dialogbox type in the resource editor.
That is an easy way to do it, however the following is certainly better - since having an unowned dialog isn't your best choice.
The point is that DialogBox() will create a modal dialog window, while CreateDialog does not. Modal dialogs disable the parent window.
From MSDN: A modeless dialog box neither disables the owner window nor sends messages to it.
That should solve your problem.
CreateDialog(hInstance, MAKEINTRESOURCE(IDD_WINDOW_INFO), hMainWindow, WindowInfoProc);
ShowWindow(hWnd, SW_SHOW);