1) when you delete a pointer to a modeless dialog box, what happens to it?
MSDN says "the objects destructor is called".
Does this also call DestroyWindow on these windows? Or must I call
DestroyWindow myself, before deleting the pointer?
2) If this modeless window I am destroying owns a modal dlg window, what
happens to it? MSDN says "child windows or windows it owns get destroyed".
But how? OnClose or OnDestroy of modal dlg boxes seems not to get called.
My problem is, I have a windows hierarchy like this:
- CMainWindow (CDialog, CFrameWnd or whatever window)
- CModelessDlg
- CModalDlg
BOOL CModelessDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// show this window
ShowWindow(SW_SHOW);
// bring up the modal window
CModalDlg dlg;
dlg.DoModal();
// we're done, end
EndDialog(IDCANCEL);
return FALSE;
}
CMainWindow::Init()
{
m_pModelessDlg = new CModelessDlg;
m_pModelessDlg->Create(IDD_MODELESS_DLG, this);
...
}
CMainWindow::CloseWindows()
{
if ( m_pModelessDlg )
{
// is this necessary?
if ( ::IsWindow( m_pModelessDlg->m_hWnd ) )
m_pModelessDlg->DestroyWindow();
// delete the pointer
delete m_pModelessDlg;
}
}
Ok, so questions:
1) is DestroyWidow necessary in the above example ?
2) what happens to CModalDlg which is in its DoModal loop in
CModelessDlg::OnInitDialog() ?
3) delete m_pModelessDlg seems to take CModelessDlg and CModalDlg off the
screen. Is this safe or have they just become invisible/orphaned and/or
cause memory leak?
Lisa
Yes
> 2) what happens to CModalDlg which is in its DoModal loop in
> CModelessDlg::OnInitDialog() ?
I am not 100% sure of this but to be safe, I would think it a good idea to
call EndDialog for the modal, prior to deleting the ptr to the modeless
dlg....or in the modeless destructor on the way out.
> 3) delete m_pModelessDlg seems to take CModelessDlg and CModalDlg off the
> screen. Is this safe or have they just become invisible/orphaned and/or
> cause memory leak?
Probably so, but I would safeguard with the above suggestion.
-Mark
I'm probably not the expert you are seeking, but:
- reference KB103788
Generally, derive from CDialog to get a modal. No overrides necessary.
A modeless needs:
void CMadDog::OnCancel()
{
if(p_MadDogsBite != 0)
{
: :SendMessage(p_MadDogsBite->m_hWnd, WM_COMMAND, IDCANCEL, 0);
}
p_MadDog = 0;
DestroyWindow();
}
void CMadDog::PostNcDestroy()
{
delete this;
}
MadDog is a modeless. It msgs MadDogBite, a modal it started, to cancel
itself.
I've learned to never take object destruction for granted, 'cause they do
get stranded, especially when you forget to do something. So, I always keep
track of the dialogs and message them to kill themselves. Kinda hafta do
that anyway, since sometimes an object other than the immediate parent wants
to clean up.
Help this helps.
Jack
"Lisa Pearlson" <n...@spam.plz> wrote in message
news:%238GV55r...@TK2MSFTNGP11.phx.gbl...
I am not doing it, and things are working just fine (so it seems). I see
that article refers to up to version 4.0 of VC, so maybe it's just outdated.
I was wrong when I said OnDestroy wasn't being called. It seems to be called
so the dialog window does get destroyed.
Now the question is whether this causes any memory leaks. The debugger
doesn't report so, so I wouldn't know how to test it, other than loop a
create and destroy of a window automatically and watch the amount of
available memory.
No, I am not going to purchase bounds checker or such ;-)
Lisa
"Jax" <jaxn...@mailhot.com> wrote in message
news:bZednS1pBtF...@comcast.com...
see inline
"Lisa Pearlson" <n...@spam.plz> wrote in message
news:%238GV55r...@TK2MSFTNGP11.phx.gbl...
> Posting on WinCE newsgroups are rearely useful when it comes to C++ stuff,
> so I'm trying just here.
> I have a few questions.
>
> 1) when you delete a pointer to a modeless dialog box, what happens to it?
> MSDN says "the objects destructor is called".
> Does this also call DestroyWindow on these windows? Or must I call
> DestroyWindow myself, before deleting the pointer?
>
Up front, I must announce that I have no experience in WinCE. With that
disclaimer:
Have a look at Technical Note TN017 Destroying Window Objects. Assuming
that you modeless dialog does NOT have functional OnCancel and OnOK handlers
(especially, they shoudl have null implementations), add a message handler
for the WM_CLOSE message. In this message handler, call the base class,
then call DestroyWindow. This will handle the case of closing the Modeless
dialog from within the Modeless dialog. Provide a message handler for the
PostNCDestroy message, and in there you would post a message back to the
parent to indicate that the Modeless dialog has been destroyed, and if there
are any pointer to such, set them to null. This is where you would
typically delete the class object for the Modeless dialog (i.e. the "delete
this" as the last statement in the method).
Here is a quick code hack that may provide a better explanation:
/* ---------------------------------------------------------- */
void CModelessDlg::OnClose(){
CDialog::OnClose();
DestroyWindow();
}
/* CModelessDlg message handlers for OK and Cancel buttons */
/* ---------------------------------------------------------- */
void CModelessDlg::OnBnClickedOk(){}
void CModelessDlg::OnBnClickedCancel(){}
/* ---------------------------------------------------------- */
void CModelessDlg::OnNcDestroy(){
pParent->SendMessage(msgClosingID,0,0);
CDialog::OnNcDestroy();
delete this;
}
/* ---------------------------------------------------------- */
If you want to close and destroy the Modeless dialog from the parent, then
use the pointer that was obtained when the window was created, and simply
call the DestroyWindow from there.
Again, here is a quick code hack that may provide a better explanation:
/* ---------------------------------------------------------- */
/* ---------------------------------------------------------- */
LRESULT CModelessTestView::fnDlgClosing(WPARAM wp, LPARAM lp){
pWinDlg = 0;
return 0;
}
/* ---------------------------------------------------------- */
void CModelessTestView::OnBnStartModeless(){
if(pWinDlg == NULL) {
CModelessDlg *p = new CModelessDlg();
p->SetParentPtr(this,UWM_MODELESS_CLOSING);
p->Create(p->IDD);
pWinDlg = p;
}
pWinDlg->ShowWindow(SW_SHOW);
}
/* ---------------------------------------------------------- */
void CModelessTestView::OnBnKillModeless(){
if(pWinDlg) pWinDlg->DestroyWindow();
}
/* ---------------------------------------------------------- */
> 2) If this modeless window I am destroying owns a modal dlg window, what
> happens to it? MSDN says "child windows or windows it owns get destroyed".
> But how? OnClose or OnDestroy of modal dlg boxes seems not to get called.
>
If there is a Modal dialog box that is currently enabled (created and
running a RunModalLoop) then I think you will have some problems when
forcably destroying the parent window. The modal dialog is quite different
from standard child window controls - because it is running its own message
loop. That said, if you are trying to close the Modeless dialog (that has a
child Modal dialog running), be aware that the user interface for all
windows in the heirarchy will be disabled - the Modal dialog has it all tied
up. If you are responding to someother event, then I would recommend that
yoy first get the Modal dialog closed. That is easy enough to do, first
testing to see if the current active window is of CDialog type, then
checking to see if it has the WF_MODALLOOP state. On may be tempted to post
a WM_QUIT to the Modal dialog to get it to stop runing, and that would work,
but in the case we are working with here, the message pump is not runing, so
we take another tact.
As is becoming the habit, here is a quick code hack that may provide a
better explanation:
/* ---------------------------------------------------------- */
/* ---------------------------------------------------------- */
void CModelessTestView::CloseModelessDialog(){
CWnd *pwnd = GetActiveWindow();
if(pwnd->IsKindOf(RUNTIME_CLASS(CDialog))) {
CDialog *pd = (CDialog *)pwnd;
if(pd->m_nFlags & WF_MODALLOOP)
pd->EndModalLoop(1);
}
OnBnKillModeless();
}
/* ---------------------------------------------------------- */
I think the salient points here are:
1) you must call the DestroyWindow to close the Modeless dialog
2) signal any ModalLoops to terminate
3) delete the modeless dialog object in the CModelessDlg::OnNcDestroy()
handler
4) communicate the destruction of the modeless object to the parent window
and account for the pointer to a deleted object
I hope this helps...
Roy Fine
Let's look at this interesting piece of code:
/* ---------------------------------------------------------- */
void CModelessTestView::CloseModelessDialog(){
CWnd *pwnd = GetActiveWindow();
if(pwnd->IsKindOf(RUNTIME_CLASS(CDialog))) {
CDialog *pd = (CDialog *)pwnd;
if(pd->m_nFlags & WF_MODALLOOP)
pd->EndModalLoop(1);
}
OnBnKillModeless();
}
/* ----------------------------------------------------------
First, You use GetActiveWindow().
Is this function system wide? If so, how do I know the active window even
belongs to my own application?
How is it different from using GetForegroundWindow()? (I know, first one
gets the active window, this one gets the perhaps inactive foreground
window, but does it make a difference to my problem of not knowing whether
it belongs to my own application?)
Even if my application is not the active application running, it still needs
to be able to find and close this modal dialog window.
(This closing could be triggered by a timer or data from serial port or
other ways that do not require my application to be the active application
running on PC or CE.)
Second, you call EndModalLoop. Why not do pd->EndDialog(IDCANCEL) ?
EndDialog does more than just end the modal loop. It also performs some
cleanup I think, from locked dialog template resources.
Lisa
I wonder if enumerating child windows will also include modal windows, to
which the main window is the parent, or only the child windows (Controls) ?
Lisa
"Roy Fine" <rlf...@twt.obfuscate.net> wrote in message
news:eV$GAzt5D...@TK2MSFTNGP10.phx.gbl...
Actually, both are flawed -- consider the case where the app is not the
current active app, and does not have focus. Second, it is real tough to
find an active modeless dialog given that it may not be active, and given
just the handle or CWnd ptr to the object that created it (i.e. owner and
parent are not the same because an app can create a dialog box that has no
owner).. Note that the parent for a modeless dialog would be the app main
window - from AfxGetMainWnd, not necessarily the window from which the
modeless dialog was created.
And more on flaws for the code I posted -- there is no error cheking - a lot
of those pointers could be NULL, and I left all of the requisite error
checking out.. I suspect many will jump all over that one (and rightly
so...)
The easiest way to get at the potential Modal dialog box is to save a
pointer to it when it is created - after the object instantiation, but
before the call to DoModal(). Make the stored value a public variable of
the modeless dialog class (or provide an accessor), and do the necessary
initialization and state management.
Do something like the following in the Modeless dlg when the Modal dialog is
created:
/* ---------------------------------------------------------- */
CModalDlg dl;
activeModal= &dl; // init to null in ctor
dl.DoModal();
activeModal = NULL;
/* ---------------------------------------------------------- */
And now the task of closing the Modeless becomes quite trivial...
/* ---------------------------------------------------------- */
void CModelessTestView::CloseModelessDialog(){
if(pWinDlg == NULL) return;
CDialog *pwnd = ((CModelessDlg *)pWinDlg)->activeModal;
if(pwnd != NULL) pwnd->EndModalLoop(1);
pWinDlg->DestroyWindow();
}
> Even if my application is not the active application running, it still
needs
> to be able to find and close this modal dialog window.
> (This closing could be triggered by a timer or data from serial port or
> other ways that do not require my application to be the active application
> running on PC or CE.)
>
> Second, you call EndModalLoop. Why not do pd->EndDialog(IDCANCEL) ?
> EndDialog does more than just end the modal loop. It also performs some
> cleanup I think, from locked dialog template resources.
>
I used EndModalLoop because I wanted to send a signal MFC to get out of the
loop. EndDialog calls EndModalLoop in the MFC class, then calls the API
version. I suppose each would work equally well, but both are equally bad.
Any approach that did not use either would certainly be preferred.
best regards
roy fine
> Lisa
>
>
Neat perhaps, but I don't think it is necessary because delete does seem to
call DestroyWindow.
Below is my code, it works, but I don't understand why I must return
processing to the message queue, to not get a stack overflow.
My other question is, .. shouldn't it be possible to write some code in the
OnDestroy to verify that it is not called before the modal loop is ended?
CMyModalORModelessBaseClass::OnDestroy()
{
// if we're a modal window and the loop is still active
if( m_nFlags & WF_MODALLOOP )
PostMessage(WM_CLOSE); // do this instead
else
CDialog::OnDestroy();
}
My code:
// responds to a WM_USER_SWITCHDIALOG, DlgID, DlgPageIfPropertyPageDlg=0
LONG CMyMainModalWindow::OnSwitchDialog(WPARAM wParam, LPARAM lParam)
{
if( !DestroyAllWindows() )
{
// failed to destroy window or needs some time
// simply repost this message to try again later
PostMessage( WM_USER_SWITCHDIALOG, wParam, lParam);
return 0;
}
switch( wParam )
{
CASE 1:
m_pDialog = new CPage1;
m_pDialog->Create(IDD_PAGE1, this);
break;
CASE 2:
m_pDialog = new CPage2;
m_pDialog->Create(IDD_PAGE2, this);
break;
...
default:
m_pDialog = NULL;
}
if( m_pDialog )
m_pDialog->SetFocus();
else if( wParam == 0 )
{
// quit
m_serialport.Close();
EndDialog(IDCANCEL); // cancel this modal window
}
return 0;
}
BOOL CMyMainModalWindow::DestroyAllWindows()
{
// if there is no window, return successful
if( m_pDialog==NULL )
return TRUE;
// if it is a window, destroy it
if ( ::IsWindow(m_pDialog->GetSafeHwnd()) )
{
// destroy modal top window ? possible ones are listed below
LPCTSTR lpszText[] = {
_T("Modal Dialog 1"),
_T("Modal Dialog 2"),
_T("Modal Dialog 3")
};
int nSize = sizeof( lpszText ) / sizeof( LPCTSTR );
HWND hModal = NULL;
for( int w=0; w<nSize; ++w)
{
hModal = ::FindWindow(NULL, lpszText[w]);
if( hModal )
{
TRACE(_T("Modal window requested to close first\n"));
::SendMessage(hModal, WM_CLOSE, 0L, 0L);
return FALSE; // STACK OVERFLOW IF I WOULD CONTINUE!???
}
}
// okay, we got here because there is no modal window up
// destroy this modeless window now
m_pDialog->DestroyWindow();
}
// I am not calling "delete this" in PostNcDestroy
// I'm doing it here instead:
delete m_pDialog;
m_pDialog = NULL;
return TRUE;
}
By the way, if my modeless window is destroyed via the task manager, then I
get in trouble because of not calling delete this in PostNcDestroy, right?
Lisa
In general, the code snip you provided below is pretty bad form -- the
WM_DESTROY is sent to a window that is being destroyed, and after it has
been removed from the active/visible windows list. It makes no sense to
send a WM_CLOSE to a window (ostensibly to signal the control mechanism to
close the window) that is in the final stages of being destroyed. The
WM_DESTROY message is typically used by the app to clean up any resources
that may be associated with the window, but that are not directly managed by
the system. The WM_CLOSE message should come first, and the associated
handler should call DestroyWindow, and the OnDestroy handler does tidying
up, etc., etc., etc... That is a general rant on form -- our problem at
hand is a bit more involved...
One needs to be somewhat aware of the differences between an MFC Modal
dialog and the system Modal dialog. They are supposed to be functionally
equivalent, but MFC emulates a lot of the system behavior. And because of
this, when we try to do things that related to the creation and destroying
of dialog resources in MFC, we must be aware of the differences.
For instance, the CDialog derived classes and the DoModal method do not
start a Modal dialog box! CDialog always creates a modeless dialog. The
CDialog::DoModal call eventually gets around to calling the
CreateDialogIndirect API function. (in the system API world, modeless
dialog boxes are created using CreateDialog/CreateDialogIndirect, and modal
dialog are created using DialogBox/DialogBoxIndirect) When a system created
modal dialog is activated, the "system" will deactivate the owner of the
dialog, and any enabled child windows of the parent. The disabled windows
are enabled only when the dialog is destroyed. Additionally, when the
system starts a modal dialog for an application, it creates a dialog message
loop, taking control of the message queue for the entire application. Any
messages that you app receives while a system modal dialog created by your
app is active are sent only at the pleasure of the system dialog message
loop.
For modeless dialog boxes, the system does little of the above -- the
modeless dialog is maintained above the Z-Order of all other owner windows,
and the application is responsible for processing the message queue
(retrieving and dispatching messages to the dialog)
If any of this rant is even remotely relevant, it is this - MFC implements
"all" of the Modal dialog control processing using a modeless dialog - for
many reasons I suspect, a few being to keep timers runing and to keep the
OnIdle processing alive and well so MFC can stay on top of its garbage
collection.. But remember this -- sending messages to lots of windows in
the heirarchy while an MFC Modal dialog is active takes quite a bit of
research to verify that it works -- the RunModalLoop for the modal dialog
object has hijacked the message queue processing and will keep it until
either a WM_QUIT is posted or the CONTINUE_MODAL flag is turned off in the
modeless dialog object (and MFC is quite picky about who resets the flag and
when - notice all of the ASSERT(ContinueModal) statements in in
RunModalLoop)
An application that creates a modeless dialog, and the modeless dialog
creates a modal dialog is not an uncommon scenario. But! extending that to
a situation where the main app needs to close the modeless while the modal
dialog is open is a bit further out there (not at all hard to implement -
but just something that is better considered during initial app design)
Just from the name of the class below, it seems that you are creating a
dialog that can be opened either modal or modeless - again, not a serious
technical problem (and not something that I would frequently do) - but
managing the resource should not be handled in the WM_DESTROY handler.
I am not sure what you mean with regards to the "stack overflow if you don't
return processing to the message queue".
Additionally, you don't want to call DestroyWindow on the modal dialog,
because that wont get it out of the RunModalLoop, and DoModal calls
DestroyWindow for you as soon as the RunModalLoop returns. Use EndModalLoop
for MFC Modal dialogs. DestroyWindow is OK for the MFC modeless, because
that is just another window, and it uses the app message processing loop.
I hope this helps a bit --
regards
roy fine
"Lisa Pearlson" <n...@spam.plz> wrote in message
news:eln7%23K85D...@TK2MSFTNGP12.phx.gbl...
Check also http://support.microsoft.com/default.aspx?scid=kb;en-us;84133,
the third case. But I didn't really wanted to get involved in this thread -
an automatically destroyed modal dialog is really a paradox :-))) Read more
here (and I agree wholeheartedly on those guidelines):
Especially this section, which I allow myself to quote:
"To support the different ways applications use dialog boxes, there are two
types of dialog box: modal and modeless. A modal dialog box requires the
user to supply information or cancel the dialog box before allowing the
application to continue. "
Two years of developing for Mac OS made some marks :-)))
Johan Rosengren
Abstrakt Mekanik AB
But, I can not relate your answer to my code, because it seems either you
are confused about my code or I am not understanding your answer.
CMyMainModalWindow is the application's MAIN MODAL window, created in
InitInstance of my application, as is usually the case with a dialog based
application, which is the basis of my application.
CMyMainModalWindow opens Several possible modeless dialog windows, one at a
time. If it wants to open another modeless window, it has to destroy the
previous one it created. The modeless windows can not close themselves, but
their existence is always controlled by the CMyMainModalWindow. Only one
modeless window can exist at one time.
m_pDialog points to this CModelessDialogWindowX where X indicates one of
several different modeless dialog boxes.
The switch statement opens the right modeless dialog box.
Modeless dialog boxes can be destroyed using DestroyWindow.
Thus my code:
if( m_pDialog )
{
m_pDialog->DestroyWindow();
delete m_pDialog;
m_pDialog = NULL;
}
However, this modeless dialog I'm destroying may have a modal dialog up. I
can not destroy the modeless, or else it will destroy the modal, using
destroy window rather than EndDialog or whatever.
So, that's where I add code before the m_pDialog->DestroyWindow(), which is
using ::FindWindow to find a possible MODAL window only. I do not have
windows that can be both modal as well as modeless. I have "Modal Dialog" 1,
2 and 3.
These Modal windows are closed using WM_CLOSE (
::FromHandle(hModal)->EndDialog(IDCANCEL) also works).
I use SendMessage. Only THEN do I call DestroyWindow on the MODELESS window.
So, I am always destroying modal windows with SendMessage(WM_CLOSE) and
Modeless windows with DestroyWindow.
This is fine, isn't it? What part of what I did does not comply with what
you said?
Lisa
"Roy Fine" <rlf...@twt.obfuscate.net> wrote in message
news:uyZnLXB6...@tk2msftngp13.phx.gbl...
The user could have a modal window open when a signal from the serial port
must force to close those windows and open another.
So I must have a "CLOSE NOW, DON'T WAIT FOR USER!" feature. It's
implementing this feature without memory leaks that's the problem.
I read the website, but it really doesn't help me with this existing
application.
None of the symptoms mentioned on that website, is something that my
application seems to suffer.
Lisa
"Johan Rosengren" <johan.r...@telia.com> wrote in message
news:%23jLVKmC...@TK2MSFTNGP11.phx.gbl...
> But, I can not relate your answer to my code, because it seems either you
> are confused about my code or I am not understanding your answer.
Most definitely - most likely both, probably more of the first...
My strong suggestion, after reading the description of your app, would be to
scrap the Dialog based app approach and go to an SDI app with multiple
views - and leave the dialog boxes for the kinds of things Johan eludes
to...
I have been where you are -- way down the wrong path, too much invested to
go back, too steep a hill in front to get it working correctly.
I tend to stay far, far, far away from FindWindow - but if you fnd it works
reliably for you, that's quite a different story. I would tend to favor a
scheme that allows each dialog to register itself with a controller when it
is activated, and remove itself when closed. The controller would know the
exact state at all times -- ergo, no need for the search and destroy
mission. The controller would also have enough information to control the
dialogs (i.e. minimally a CWnd ptr and the HWND). And just to make it
*fully* emulate the "noddy user" that is not really there, I would implement
the controller in a separate thread - that way the message queue of the main
app is always being services.
Before you scream at me again for not addressing your problems, consider
this: I would not solve the problems that you are facing, I would design
them out. Imagine the headache you have when some app is introduced to your
same machine that has a window that has the same name as one of your dialog
boxes -- FindWindow can get it all messed up then... But, if the
probability of such is low, then your approach does seem to be viable.
regards
roy fine
Comments inline
"Lisa Pearlson" <n...@spam.plz> a écrit dans le message de
news:O7hGtjE6...@TK2MSFTNGP11.phx.gbl...
> The reason why I can not rely on user input to close the modal window is
> that the correct view to display is dependent on real time information
from
> the serial port.
>
> The user could have a modal window open when a signal from the serial port
> must force to close those windows and open another.
> So I must have a "CLOSE NOW, DON'T WAIT FOR USER!" feature. It's
> implementing this feature without memory leaks that's the problem.
>
There is something fundamentally... I will not use "wrong", perhaps
"unorthodox in a slightly negative sense" ? - with a situation where the
application is locked and waiting for input from a user (which is the modal
state, in other words) that has to be interupted by the application by
blowing away the dialog. In a general sense, and my opinion, I would tend to
think that some user input that *must* be interuptable at any time should be
modeless, not modal.
> I read the website, but it really doesn't help me with this existing
> application.
It states the rules and goal for modality. Modal - the app is disabled and
waits for input. Modeless - the application continues to work and can
interact and be interacted with. A very brief discussion from Apple (who
*really* do know what they're talking about when it comes to user
interaction :-))) can be found here:
http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/index.html
From this not-so-small document (which can be downloaded as a PDF and
enjoyed together with a hot bath and a 6-pack), we learn that application
modal states should be kept to a minimum, and that the user should be in
control, not the application.
An application should thus not suddenly discard a dialog which the user
might be entering information into, and a dialog should not be application
modal (which is the only modality in Windows - Mac OS also has the concept
of document modality) unless absolutely necessary. And I have difficultes
consolidating a necessary modal state with a simultaneous necessity to run
in a non-modal state - perhaps I'm just stupid :-)))
> None of the symptoms mentioned on that website, is something that my
> application seems to suffer.
>
Imagine the surprise of the user if the modeless dialog is destroyed while
the modal dialog is handling WM_INITDIALOG, and another app suddenly pops up
to the foreground :-))) I think this is an even worse case compared to the
one where a modal dialog suddenly disappears.
Johan Rosengren
Abstrakt Mekanik AB
<snip>