I have a multi-threaded application with several supporting DLLs and several popup dialogs. My Main App loads all DLLs on startup, which creates all popups, but they are kept hidden until needed.
When the user presses a button in the main app, a particular popup is shown (from the DLL) by calling ShowWindow( SW_SHOW ) (modeless)
Sometimes (1-in-10 times?) the popup simply fails to show and the Main App hangs. OnShowWindow of the Popup dialog is never called. I have tried calling ShowWindowASync instead, and it still fails to show the popup sometimes, but this call doesn't lock the Main App.
This problem only affects popups within a single DLL.
If the popup shows the first time ShowWindow is called, it can be closed and re-opened indefinitely throughout the lifetime of the Main App. If (using ShowWindowASync), the popup fails to show, it will never show during the lifetime of the Main App. I can re-run the application (without rebuilding anything) and there is a hit-or-miss chance that it will work or fail. I haven't been able to identify any predictive conditions or properties.
I have used tools to renumber all of my resource elements so that there are no conflicts throughout the solution.
UPDATE:
I used Winspector to get some information about the dialog when it does and does not work.
When the dialog works (shows properly), Winspector reports that my dialog has valid position (10,96, 1015, 514), style attributes that match the resource template, ID of 0 (not sure what ID means), and the "Owner EXE" is "MyApp.exe" - I can see many messages passing in and out of the dialog, including WM_SHOWWINDOW.
When the dialog fails (does not get a show window message), Winspector reports position (-1512, 190, -517, 634), style attributes that do not match the resource template, ID 509290824, and owner EXE is "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe" - I also do not see ANY messages in the message viewer within Winspector when the dialog fails to show.
Clearly, the dialog is not getting created correctly. CDialog::Create never fails for me. Additionally, I experience this problem in release (not using Visual Studio) so it's not a VS-specific problem.
The dialog was getting created from a thread that was not executing a message pump. This was the root problem. I had tried to initialize all dialogs (i.e. call "Create") from an "init" thread, and then use them later on in the winproc/GUI thread. Can't do this apparently.
A pointer to the dialog was then retrieved into the winproc/GUI (which has a message pump), but by that time, the dialog was already "corrupt" and no longer responding to winproc properly
Related
I've been trying to implement a file dialog into my C++ application for a while now, and I achieved good success with the code described in this article (It is german, but the code should be understandable):
https://msdn.microsoft.com/de-de/library/windows/desktop/ff485843(v=vs.85).aspx
However, using this code in my window class, which is a CDialogImpl, I just can't find out how to make this file picker modal. If I use this code, I can just spawn multiple file picker.
Of course, I could just keep track of the state by adding a member variable representing the state, but it would still not solve the problem of being able to click around in the main window while the dialog is opened.
Is there any way which would allow me to make this window modal? I've been trying to scan through all available methods, but I couldn't find anything. I didn't find any flags which could be passed in the creation, neither any options which I could set after creation.
Any help is appreciated!
The example you link to is very simple and has no UI other than the file dialog. Your program is more complex, having a window from which the file dialog is invoked.
You need to provide an owner for the file dialog. When you do that the owner is disabled, part of what makes the dialog modal. Failing to pass an owner means that the other windows are not disabled and so still respond to user input.
The example code provides no owner, but since there are no other windows in that program, that is benign. Modality is only meaningful when there are multiple windows.
So, to solve the problem, pass the owner, the handle of your window, to the Show method of the file dialog.
Disabling owner windows is one of the key parts of a modal dialog. You will find that any API for modal dialogs expects you to specify an owner. Get into the habit of expecting to provide that ownwr window, and looking for the means to do so.
When my application is minimized, and the application programmatically closes a child window, the state of the child window between my framework and MFC goes out of sync because MFC will not send a WM_SHOWWINDOW message when the application is minimized. I noticed that Qt had the same problem: https://codereview.qt-project.org/#/c/93410/
Things that I have tried:
Override OnShowWindow() -- if the states are out of sync, then I alter the BOOL parameter before passing it to the CDialog::OnShowWindow. But doing so does nothing. It is as if the BOOL parameter given to the override is read-only.
Handle WM_SHOWWINDOW in PreTranslateMessage -- this does not work because WM_SHOWWINDOW does not appear here.
I know I can check SW_PARENTOPENING to know when to look for out-of-sync problems and handle it, but I just don't know where is the best place to do it.
My current solution is to override OnShowWindow, check for SW_PARENTOPENING, then post a SW_HIDE. It works, but it feels like a waste because I should be able to prevent it from restoring entirely rather than defer it.
Summary:
Basically, I am just programmatically closing a window, say from a timer call, or user's remote command, or whatever, while the main application is minimized. The dialog will be temporarily hidden when minimized (the MFC framework will automatically call ShowWindow(SW_HIDE) but with an internal flag to re-open when the app is restored). If my program sends ShowWindow(SW_HIDE) now, this call will not be registered, and the window will be re-opened by MFC when the app is maximized. To my user, he/she has closed the window remotely and does not expect the window to re-appear, so I need to re-call my ignored ShowWindow(SW_HIDE) somehow when restoring the main app.
Since Windows 7 it is possible to close my application via Tasklist -> close even when a modal dialog is on the screen.
In earlier Versions of Windows the "close" button in the task line of the desktop is disabled as are all parent windows of the dialog in my application.
In Win7 (and above) the parent windows of the dialog are still disabled, but not the close() item in the taskbar.
Such a scenario (close of while while modal dialog is on the screen) yields a crash of my application.
(Note: we are talking about >>100 dialogs, and >>100 .exe files. )
i am even happy with a completly removing this close item in the taskbar.
Any ideas?
Closing an application is signaled with the WM_SYSCOMMAND message with a parameter of SC_CLOSE. If you put in a message handler you can intercept the request and do whatever you want.
Be aware of two things: applications that don't close when you want them to are very frustrating for a user, and it's always possible to force-close an application via the Task Manager.
I have a strange error and spend hours in the debugger without finding a solution.
(But it helped me to fixed another error that you should never call EndDialog from a WM_KICKIDLE task).
My problem is that i have a main window and a modeless dialog window wich raises a modal subdialog window. When the subdialog window is closed. The modeless dialog window turns itself into a modal window. My code really does leave the modal loop. And if i close the now modal window it behaves like an invisble modal window is active, meaning no interaction is possible anymore.
When i only run a modal dialog on top of the main window it is closed fine.
BTW: The main window is not the one available view CWinApp::m_pMainWnd but a new create FrameWindow. I hide the p_MainWnd and use it as an invisible message only window. From some comments and my debugging session i found that the pMainWnd has some special meaning but i could figure what exactly it has to do with modal windows (there is an undocumented "CWinApp::DoEnableModeless" for example).
EDIT: I'm posting a WM_CLOSE to the dialog and then use EndDialog(0) from the OnClose() handler to exit the modal state. I also tried to use EndDialog(0) directly. There is no difference between this two methods.
When MFC creates a modal dialog, it makes it modal by disabling the windows above it. The code that reenables those windows occurs when the dialog ends normally with a call to EndDialog. If anything prevents that code from running, the other windows will be locked out.
Modeless dialogs are a different beast, and there's a note specifically in the EndDialog documentation warning you to use DestroyWindow instead.
Maybe this is justifiable but I have a question:
why are you using hidden window? Was it created as message only window (passing HWND_MESSAGE as a parent handle and Message as a class) or you just call it message only?
OK, a little more info about MFC and dialogs.
MFC does not use Windows modal dialog. It always creates modeless dialog; either Create or DoModal call in turn ::CreateDlgIndirect windows API.
Modeless dialof rely on the main window message dispatch, while modal calls RunModalLoop that is similar to MFC window message pupmp (not a message loop).
It runs in the main thread of execussion without freezing because it allows for idle processing (calls OnIdle).
How do you dismiss the modeless dialog? As Mark pointed you should use DestroyWindow.
As for m_pMainWnd, MFC framework uses it extensively to determine may things that control main window behavior. By changing it you may have created the behavior you experience.
Did you set the value to a newly created frame you treat as a main window?
What kind of MFC application is it? SDI or MDI?
Would it be possible to create test app to duplicate this behavior and post it somewhere for download?
By the way, you do not have to be concern about DoEnableModeless, since it does not do anything but calls hook (COleFrameHook type) that is spasly used, unless you are trying to implement some functionality using OLE or ActiveX or you are trying to marry MFC and .NET Windows Forms.
In conclusion if your (or third party code uses this hook, I would suggest checking the code in the COleFrameHook class.
I have an MFC application that spawns a number of different worker threads and is compiled with VS2003.
When calling CTreeCtrl::GetItemState() I'm occasionally getting a debug assertion dialog popup. I'm assuming that this is because I've passed in a handle to an invalid item but this isn't my immediate concern.
My concern is: From my logs, it looks as though the MFC thread continues to service a number of windows messages whilst the assert dialog is being displayed. I thought the assert dialog was modal so I was wondering if this was even possible?
The message box that shows the assertion failure has a message pump for its own purposes. But it'll dispatch all messages that come in, not just those for the message box (otherwise things could get blocked).
With a normal modal dialog, this isn't a problem because the parent window is typically disabled for the duration of the dialog.
The code that launches the assertion dialog must've failed to figure out the parent window, and thus it wasn't disabled. This can happen if your main window isn't the active window at the time of the assertion. Other things can go wrong as well.
You can change how Visual Studio's C run-time library reports assertion failures with _CrtSetReportMode. You can make it stop in the debugger and/or log to the output window instead of trying to show the dialog.
Dialogs (even a messagebox) need to pump the message queue, even if they're modal. Otherwise how would they know you clicked on the "OK" button?
If you need to stop everything when an assert triggers it's usually not too difficult to write your own implementation of assert() (or ASSERT() or whatever) that will break into the debugger instead of displaying a messagebox that asks if you want to break into the debugger (maybe only if it determines that the debugger is attached).