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

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.

Related

Capturing the window handle of a newly created modal dialog

I'm using and improving on an open source MFC work-alike library called FFC. Sometimes the library associates the wrong window handle to a dialog object, which means the C++ object can't be found later when the correct handle is looked up. In particular, this is happening when the application opens its root window, which is a dialog that it opens with a call to DoModal.
In its DoModal function, the FFC library uses a... "surprising" way to attach the handle to the dialog object. It stashes the "this" pointer in a global variable and hooks a function to be called on all window messages before calling the DialogBox function. This hook function it registered in term assumes the handle from the first message it receives is the handle for the window in the global variable, and attaches that handle to it.
Sometimes, this works. Often - and I don't know if it's because of intrusive things done by the McAfee scanner on my work computer, or because my program starts from a console window, or something else - many unrelated messages will be captured before a message actually meant for the modal dialog comes through.
At first I thought it was because FFC wasn't making sure the message it looks for is "WM_CREATE". I added this check, but it didn't fix the problem. Turns out one or more of the spurious messages are also WM_CREATE messages! Before it gets the one for the real dialog, the first WM_CREATE it receives is a handle for a window with blank window text and rectangle 0,0-0,0.
So is this really the correct or canonical way to get the handle for a modal dialog? It seems unreliable. (Note that because the dialog is modal, you can't use the return value from CreateWindowEx because the DialogBox function doesn't return until the modal dialog is closed.) Is this really how MFC does it? Is there a better way? Could I associate some data with the dialog or look for data that should be associated with it to make sure I have the right window handle? (For instance checking the template parameter passed to the dialog box call, if I can get that back from the handle somehow.)
I am sure this is published in books, but MFC sets a windows hook (WH_CBT) and then looks for the HCBT_CREATEWND code in the hook to marry the C++ object to the HWND.

Subdialog's hWnd resets to NULL after the dialog is initialized

I have stuck with the following problem. I have a main dialog that has some controls. It also displays several subdialogs (as subcontrols) that are set as a pointer members with their IDs. I create these dialogs with their IDs via CreateControl during OnInitDialog. Yesterday it worked very well. Today, I decided to add some controls to subdialogs and caught a problem.
That's how I create subdialogs in the main dialog durig OnInitDialog:
if (m_pNewObjDlg != NULL) m_pNewObjDlg->Create(m_nNewObjDlgID, this);
if (m_pSharedObjDlg != NULL) m_pSharedObjDlg->Create(m_nSharedObjDlgID, this);
That's how I set the subdialogs outside from one of my routines:
dlgSelectSharedObject.SetNewPageObject(&dlgSelectNewTableDialog, CSelectNewTableDialog::IDD);
dlgSelectSharedObject.SetSharedPageObject(&dlgSelectSharedTable, CSelectSharedTableDialog::IDD);
I see that OnInitDialog and DoDataExchange of the two subdialogs work well, subdialogs' m_hWnds are set, GetDlgItem methods return valid values. But when the execution comes back to main dialog's OnInitDialog I see that m_hWnd values of pNewObjDlg and pSharedObjDlg are NULL. I don't know how that could happen. I double checked the IDs, they don't overlap. As a result I get an ASSERT with nothing displayed except main dialog with its controls. Just any thoughts?
The problem is solved. When data was loading to the controls within DoDataExchange I occasionally called Fail. It was a combo box without items because DoDataExchange was called from base class before combo box's initialization. It wasn't expected by the logic and thus Fail was called. It lead to the destruction of these child windows.

Access variable in Dlg class from App class

I am writing a Dialog Based C++ application with MFC for the GUI. After the creation of the project, Visual Studio also created two classes ProjectNameApp and ProjectNameDlg. When adding a Menu to my application the menu item handlers (for the menu buttons) are added as methods to the ProjectNameApp class. In one of these handlers I want to access a variable of the ProjectNameDlg class, more specific, a CComboBox. But that, of course, is not possible. So I have two questions:
is there a way to acces that CComboBox variable from the Dlg class?
if not, how can I move the Menu handlers to the Dlg class to directly use the CComboBox variable?
Also, my application has to be dialog based, and it has to have a menu.
The last I dealt with any of this was in VS-2008, but if memory serves the CDialog object is probably allocated on the stack in CProjectNameApp::InitInstance(). There is probably some code there that looks kind of like:
CProjectNameDlg dlg;
int nResponse = dlg.DoModal();
One thing you could do is to add a pointer to the dialog as a member of the ProgramNameApp class. So in ProgramNameApp.h add a data element like:
std::tr1::unique_ptr<CProjectNameDlg> m_pDlg;
Then change the code in CProjectNameApp::InitInstance() to be:
m_pDlg = std::tr1::unique_ptr<CProjectNameDlg>(new CProjectNameDlg());
int nResponse = m_pDlg->DoModal();
Naturally you'd have to be on the lookout for any other uses of dlg and change them accordingly.

How to tell if a MFC Dialog has been created/initialized?

I have an OnMove handler in my dialog class, which does some stuff with control objects (i.e a CButton). I'm finding this handler gets called before the dialog OnInitDialog method is called, and hence when I try to call methods on the child controls, I get ASSERTS in debug as the controls don't yet exist... they are created in OnInitDialog.
There's two things I'd like to be able to check:
How do I tell the dialog has been initialized?
How do I check an individual CWnd control object's window has been created?
In both cases I'm looking for class members or method call results that can be used.
Set a flag in OnInitDialog
Use your dialog's m_hWnd:
if ( ::IsWindow(m_Ctrl.m_hWnd) ) {
...
}

Parent notification in MFC Dialog

I have a first dialog with a simple button on it and while clicking the button, a second dialog is created using CDialog::Create(IDD,this). I would like the parent to be notified when the second dialog is destroyed but without adding any code to the second dialog i.e., without adding a m_pParent->Notify() line in OnDestroy method.
I have tried OnParentNotify, PreTranslateMessage, SubclassWindow in the parent dialog with no success. I have not used the WS_CHILD style for the second dialog. Any idea?
To complete: in fact, I have a ComboBox derived class (but the issue is the same with buttons) and I'm displaying a modeless Dialog instead of displaying the listbox. But I would like the control to be as generic as possible so that any modeless dialog could be used. That's why I do not want to add a specific notification in the second dialog. If I'm obliged, I will use this trick but I asked for a more generic solution. PreTranslateMessage only catches WM_PAINT, WM_NCMOUSELEAVE and WM_NCMOUSEMOVE.
Use a base class and have your parent refer to the modeless child by base class only. In the base PostNcDestroy have it post to the parent.
It doesn't make sense to have the parent do a bunch of filtering / spying on all messages. It does make sense to implement behavior in a base class that you want to have common to all the different future flavors you might have of the modeless child.
OnParentNotify() is not called since dialog2 is not a child of dialog1.
PreTranslateMessage() should help here (although I don't like this bullet). The trick is that a modeless dialog doesn't destroy itself when it's closed. If you want the dialog to die, it must call DestroyWindow() when it closes, such in an OnCancel() override.
Of course, the first thing that comes to mind is t wonder why you don't want to add custom notification in your modeless dialog code.
EDIT: Another method would consist in installing a message hook (for the current thread, Not the whole system!). This would help you catch all messages for all windows associated to the same thread as dialog1. See SetWindowsHookEx()
How about posting a main parent form event to the message queue?