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

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.

Related

Using a dialog box as a main window. Not receiving WM_INITDIALOG messages

I have created a modeless dialog as a main window, but the window procedure isn't being sent WM_INITDIALOG messages.
Here's what I've done.
Created a dialog template using Visual Studio's resource editor, and
set its class name to a custom class.
Used WNDCLASSEX to register the class, window procedure, as well as
some icons and brush etc.
Used CreateDialog() with the last two parameters set to NULL, (Parent
window, and window procedure).
Created the message loop using IsDialogMessage(), TranslateMessage()
and DispatchMessage();
Returned DefDlgProc() in the window procedure as the default if no
messages were processed.
I can't think of anything else significant. Everything works well except for not receiving WM_INITDIALOG messages.
I've done it this way so the app minimises to the task bar, and I can have a menu if needed.
So my first question is, Have I done anything stupid?
Secondly, should I expect to receive WM_INITDIALOG messages using this system?
And if not, what is a good way to initialise say a combobox with strings.
(I've looked at things like WM_ACTIVATE, WM_ACTIVATEAPP etc, but nothing seems appropriate.
And the combobox isn't created yet at WM_CREATE.)
Thanks in advance.
I realised the answer shortly after posting.
As mentioned in the comments above, it's a window procedure, not a dialog procedure, so I shouldn't have been trying to initialize child windows within the procedure.
So I initialized them outside the procedure, after creating the dialog box and before the message loop.
All the dialog features are working as expected, but it's a main window that can have a menu and minimizes to the taskbar.

Passing values between dialog boxes in mfc

When using MFC, if i have a main dialog box, then I another dialog box is called from the main, what message is sent to the main dialogue box to let it know it has focus, is it WM_SETFOCUS()? If so, what paramaters are needed? The problem I have is, a value is selected in the child dialog and I want it copied to an edit control in the main dialog box once it (the child dialog) closes. Right now, I have it so the second dialog box copies its value to a global variable, but once the second dialog box closes, I wanted to the main dialog box to grab the global variable and display in the edit control.
You can also use a member variable in the child dialog box, like
CChildDialogBox dlg;
if (dlg.DoModal() == IDOK) // child dialog saves the value in a CString member variable m_str
{ GetDlgItem(IDC_EDIT1)->SetWindowText(dlg.m_str);
}
This MSDN article describes how you can set up member variables connected to controls in a dialog box.
I realized my problem, really a beginner's mistake, I though after a DoModal call a function would immediately exit. I didn't know I could perform additional code(assigning the edit control variable a new value and then SetWindowText) after the call, before the function ended.

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.

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) ) {
...
}

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.