MFC dialog shows in task bar - mfc

I have an application that uses dialogs from two DLLs. The code for the dialogs in the two DLLs is almost identical:
Both dialogs have the same style in the RC file: DS_SETFONT |
DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU.
Both dialogs are shown using ShowWindow(SW_SHOW).
Both dialogs are created on demand and not when the application starts.
Both dialogs have a NULL parent.
Both dialogs inherit from CDialog.
The only differences are that:
In dialog A, AFX_MANAGE_STATE() is called before the dialog constructor, where as for dialog B it is called at the start of the constructor.
I don't see that any of these differences should make any difference and yet, when I show the dialogs, dialog A does NOT have an icon in the task bar and dialog B, and furthermore, bringing the main window to the front brings dialog A to the front (and visa versa) whereas dialog B behaves independently.
I want to make dialog B behave like dialog A. Can anyone tell me why this difference of behaviour is there and how to fix it so that dialog B behaves the same as A?
I know you would love sample code, but that would take some time (which I don't have) to craft. The existing code is all company commercial and also a huge app from which extracting the key parts would be very difficult indeed.
** UPDATE **
If I call ::AfxGetMainWnd() in dialog B's contrustor before I call AFX_MANAGE_STATE() I can get a pointer to the main window's pointer. Yay! When I then pass it to Create(), the program crashes. Boo!
It looks like I cannot set the main window as the owner of dialog B, which I think would solve my problem, presumably because dialog B is in a DLL. What I don't understand is why this works for dialog A.

The DLL that implements dialog A instantiates an instance of CWinApp. The DLL that implements dialog B doesn't.
To fix this simply add the following line to a CPP file, possibly a standalone "the_app.cpp" file:
CWinApp dummy;
I.e. "the_app.cpp":
#include "stdafx.h"
CWinApp dummy;

Related

Make focused modeless dialog topmost

So I have my main dialog that calls modeless dialogs through this function (this is the legacy code on the project):
void MyClass::ShowDialog(CDialog* dialog)
{
if (!IsWindow(dialog->m_hWnd))
{
return;
}
int nCmdshow1 = dialog->IsWindowVisible() ? SW_HIDE : SW_SHOW;
dialog->ShowWindow( nCmdshow1 );
}
Problem: all sub dialogs stay on top of my main dialog.
Desired behavior: whichever's focused (they are all modeless), be it the main dialog, or sub dialogs, I want it to be the topmost dialog. Thank you!
Note: I already tried on my main dialog's OnInitDialog() these but didn't work:
1. SetWindowPos(&this->wndTop,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
2.SetWindowPos(&this->wndTopMost,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
EDIT
Also, sub dialogs are created this way:
m_subDlg1->Create( SubDlg1::IDD, this );
As long as there is an owner relation between two windows. the owner of a window can never be on top of the owned window.
Windows in an owner, parent, child relation always behave the same. The owned/child window is always on top of the parent/owner.
If you want to break this, you have to break the owner/child relation. Let all dialog windows have no owner... than they may float freely.
But: I will expect the you program doesn't behave better. Even worse. User might search windows that are deep below covered under other windows. And they will never get in front, when your program gets active.
See the description about parent/child/owned windows here. Also this article might be helpful.
Edit: The problem is that internally the MFC sets the main window as an owner if no parent is given. Only the call to BOOL Wnd::CreateDlgIndirect(LPCDLGTEMPLATE lpDialogTemplate, CWnd* pParentWnd, HINSTANCE hInst) allows to leave pParentWnd NULL.
So you may create the window as normal, but use SetParent(NULL) after it was created. Again the MFC ASSERTs this. SO you may use the API function and the handle of your dialog.

Should a CDialog based app set AfxGetApp()->m_pMainWnd

EDIT:
I Need to Research some weird stuff first, is there some way to put the question "on hold"?
Original:
I am working on an existing codebase using a CDialog based GUI. The Application consists of a CDialog "MainWindow", which spawns other CDialog "SubWindow"s using CDialog.DoModal.
This does work, when showing the SubWindow, the MainWindow blocks, etc.
When we call AfxMessageBox from "SubWindow", the MainWindow gets reenabled and focused.
Debugging into AfxMessagebox shows, that the function gets the mainWindow and reenables it. This causes a lot of different bugs. Using ::MesageBox works correctly, but we have about 50 different SubWindows, and, if possible, i would like to make only small, localized changes.
C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\atlmfc\src\mfc\appui1.cpp
int CWinApp::ShowAppMessageBox(CWinApp *pApp, LPCTSTR lpszPrompt, UINT nType, UINT nIDPrompt)
...
HWND hWndTop;
HWND hWnd = CWnd::GetSafeOwner_(NULL, &hWndTop);
// re-enable the parent window, so that focus is restored
// correctly when the dialog is dismissed.
if (hWnd != hWndTop)
EnableWindow(hWnd, TRUE);
...
In our entry Point we do something like this:
::AfxGetApp()->m_pMainWnd = &mainDlg;
mainDlg.DoModal();
What is the prefered way? Should i comment the line so the member stays NULL?
or could that cause any side effects?
I guess (not yet tested) i could also set
AfxGetApp()->m_pMainWnd = &subDlg;
prior to subDlg.DoModal() and reset it afterwards, but that would also mean changing 50 different files, one for every SubWindow.
Does MFC depend on m_pMainWnd, or should i just let it stay at NULL?
Thanks.
EDIT:
I tried passing the MainWindow to the SubWindow's constructor, but to no avail.
This is, where MainWindow gets reenabled:
This is, where MFC finds the MainWindow:
Do i need to manually set m_pActiveWnd ?
(OFF Topic: I love that there is the source code for mfc available.)
EDIT 2:
The MFC App is actually a DLL, which can be invoked in two ways:
Either loaded by a simple loader.exe, or by anyther big application.
This other application may also use MFC, so there may be two different CWinApp objects.
If it is loaded by loader.exe, the error does not occur.
MFC depends on m_pMainWnd in a lot of cases. Leaving it NULL isn't a good approach and it doen't fix your problem.
The main problem seams to be more subtle. The question is why does AfxMessageBox find the main dialog as the last active and not you subdialog. And this can be only a problem if you don't define a pParent when you create a new subdialog based con CDialog.
Try to pass the dialog that is currently active to the sub dialog you are calling. CDialog find the parent "automatically". But sometimes it doen't worked for me.
I had the same problem that the wrond dialog was enabled again after a message box or DoModal.
I fixed it, in defining always the parent when I create the sub dialogs.

MFC DoModal Dialog

Okay, so I will admit I have no knowledge of windows API or even MFC.
I've got an error window popping up when things go hairy (illegal character in a filename string) and I want the error box to be modal.
For the life of me I can't figure out why it crashes when it hits doModal.
Here is the code where I think this can be fixed. This code is in the event handler of a button in the main window.
CDialog *BadFileD = new CDialog();
BadFileD->Create(IDD_STATUS, this);
BadFileD->DoModal();
Am I just being borderline retarded?
MFC dialog divides two pattern, modal dialog and modeless dialog.
(1) Modal dialog usage:
CDialog dlg;
dlg.DoModal();
(2) Modeless dialog usage:
CMyDialog *pDlg = new CMyDialog();
pDlg->Create(ID_DLG, this);
pDlg->ShowWindows(SW_SHOW);
As you can see, we need a new pointer, but do not delete it. So, you need to do the following in our CMyDialog class:
Add DestroyWindow() method in OnOk() and OnCancel().
Add "delete this;" in PostNcDestroy() method.
If you do not, your code may cause a memory leak. BadFileD is a class member, and you delete it in destructor. I suggest use Modeless dialog.
For display modal dialog you should use DoModal method only
CDialog *BadFileD = new CDialog(IDD_STATUS, this);
BadFileD->DoModal();
You can read remarks from article
If you desire to just display an error message, it may be that rather than creating your own dialog you can just use AfxMessageBox(). See Microsoft Developer Network - AfxMessageBox.
If you want to do your own dialog box typically with an MFC project you would normally:
create a dialog box template using the resource editor
create a class encapsulating the dialog box with the class wizard implementing the behavior desired
insert the code to create and display the dialog box into the appropriate place
However with a simple dialog box that requires no supporting class for complex behavior you can skip the step of creating the encapsulating class with the class wizard and just use CDialog directly.
One question that needs to be answered is the lifetime of the dialog as well as whether it is to be modal or modeless. A modal dialog box requires the user to do something for the application to continue past the modal dialog box. A modeless dialog box does not block the application the way a modal dialog box does. There is also a system modal dialog box style.
Since you say it will be a modal dialog then the lifetime will be short so the entire construction, display, and destruction will probably be in a series of lines of code. For instance in a CView class with a command handler displaying a modal dialog box you might have:
void CViewThing::OnCommandMenuItem ()
{
CDialog BadFileD(IDD_STATUS);
int iRetStatus = BadFileD.DoModal();
// check for status such as IDOK, etc.
// do whatever is necessary.
}
What the above does is create a dialog box using the dialog resource template IDD_STATUS and displays it as a modal dialog box. Since it is local object, when the variable BadFileD goes out of scope, the dialog box destructor will be triggered and resources cleaned up for you.
You can also have a modeless dialog box. In the case of a modeless dialog box you need to consider the variable lifetime because as soon as the variable goes out of scope, the destructor will trigger and the dialog box will disappear.
So for a modeless dialog box being used with some view class, perhaps providing a tool box of some kind, the CDialog variable will be a member of the CView class which is using it. After the modeless dialog box is created, it is displayed or not by using the ShowWindow() member function of the CDialog class (actually a member of the CWnd class from which CDialog is derived).
void CViewThing::OnCommandMenuItem ()
{
BadFileD.Create(IDD_STATUS, this);
BadFileD.ShowWindow(SW_SHOW); // display the dialog
}
and in the CViewThing class you would have a member variable CDialog BadFileD;.
Additional considerations
In all of the above examples we are not using pointers so that when the CDialog variable goes out of scope, either from exiting a member function or when the object using the dialog box is destroyed then the dialog box is as well. This object management is done for us.
One thing that you must take into consideration with a modeless dialog box is how to destroy it when you no longer need it.
Since a modal dialog box is usually a short term object, often created as a local variable on the stack, you normally just let it go out of scope to take care of everything dealing with destruction.
However the lifetime of a modeless dialog box requires that the DestroyWindow() method be used to destroy the dialog box when it is no longer needed. See Microsoft Developer Network - Destroying the Dialog Box.
A third usage scenario - embedding a dialog box
There is a third usage of a dialog box that sometimes comes in handy, embedding the dialog box into another window as a control.
In the above examples, the dialog box template specifies the WS_POPUP style for the dialog which is the standard style for a dialog box since the normal way that a dialog box is used is to display as a separate window.
However if you change the WS_POPUP style to WS_CHILD you can then embed the dialog box into another window as a control. You can remove the other style settings such as WS_SYSMENU, DS_MODALFRAME, and WS_CAPTION and remove the CAPTION line from the dialog template to further change the dialog box. So you will end up with something like:
IDD_STATUS DIALOGEX 0, 0, 435, 266
STYLE DS_SETFONT | WS_CHILD
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "this is some static text to display on this dialog.",IDC_STATIC,81,63,200,32
END
Then just use the resulting dialog box similar to how you would a modeless dialog box with ShowWindow().
If you need to reposition the embedded dialog box within its container window, you can use the SetWindowPos() method to do so. For instance the following would move the dialog box window within its containing window to be 20 pixels from the left and 10 pixels from the top of the containing window.
BadFileD.SetWindowPos(NULL, 20, 10, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER)

When an MFC dialog is hidden after DoModal, are its controls destroyed?

I've used MFC dialogs before where you do:
EnterNameDlg dlg;
dlg.DoModal();
string str = dlg.GetName();
However a dialog I have now actually looks at a list-box control in such a method and it's not working. Although the class instance clearly exists after DoModal(), does the actual dialog get destroyed? I noticed calling DoModal() a 2nd time leads to OnInitDialog() also being called again which seems to support this theory, the dialog is recreated from the template rather than simply made visible the 2nd time.
Yes, DoModal creates a dialog on each call and destroys the window before returning.
Only the data members will still be valid. Of course, you can add more data members in your EnterNameDlg class if you want to collect data during dialog's lifetime.
As soon as the dlg gets out of scope, everything will be deallocated.
After DoModal class instance still exists, but window and all its controls are destroyed. You can call only functions that don't work with dialog controls after DoModal. Inside of the dialog class, keep required values in class members like CString, when OK button is pressed. These members can be read after dialog is closed.
The entirety of MFC is built around an awkward pairing - the Windows window with its associated handle, and the MFC class object. The MFC classes are designed to outlast the window in most cases.

Creating multiple MFC dialogs through COM, strange behaviour

Updated: please see this other thread instead, all this COM stuff is not part of the problem.
One of our apps has a COM interface which will launch a dialog, e.g:
STDMETHODIMP CSomeClass::LaunchDialog(BSTR TextToDisplay)
{
CDialog *pDlg = new CSomeDialog(TextToDisplay);
pDlg->BringWindowToTop();
}
For some reason when the COM method is called several times at once by the server, we get odd behaviour:
We get multiple dialogs, but only one entry in the taskbar
Dialog Z-order is based on order created and can't be changed... the first dialog created is always shown under the 2nd one, 2nd under 3rd, etc, even when you drag them around
if N dialogs were created, closing one of them closes it and all the others created afterwards. e.g if 5 dialogsa re created and you close the 3rd one, #3,#4,#5 all get closed.
It's somehow like the dialogs are siblings but I don't see anything weird going on. Is it perhaps due to COM, or is this a weird MFC/Win32 issue?
EDIT: If the interface method is called several times separately, it works as expected. Only when the server component sends several through at once does it seem to mess up. Could threading/timings be to blame?
EDIT2:
I put this logging in:
std::stringstream ss;
HWND self = dlg->m_hWnd;
HWND parent = dlg->GetParent() ? dlg->GetParent()->m_hWnd : 0;
ss<<"Dlg created'. HWND = "<<self<<", Parent = "<<parent<<std::endl;
OutputDebugString(ss.str().c_str());
It gave:
Dlg created. HWND = 0013014A, Parent = 00000000
Dlg created. HWND = 001B0390, Parent = 0013014A
Dlg created. HWND = 000B03B0, Parent = 001B0390
So clearly the problem is the dialogs are being made children of each other. But the question is, WHY?! It seems Windows is doing this automatically...
This question seems to be slightly away from the main issue of parenting, so I have tried to separate out the main issue into a new question.
It sounds like the first dialog has been set as the owner of the second, and the second as the owner of the third. Can you change the dialog initialization to explicitly specify the owner window? Is there a window that makes sense to assign? Perhaps the Desktop window, if they're all intended to be top-level?
If you want to be able to access all three (or more), then they would need to be modeless. Try using Create(CSomeClass::IDD, CWnd::GetDesktopWindow()), and you ought to see sibling dialogs, all of which show up on the taskbar.