i know there is a previous thread with this topic, but it didn't
really solve the problem.
I added some additional event handlers and logfile output to the
minimal sample to describe the problem.
When the application is started and quit using the menu entry or close
button of the main frame, i get this log:
04/08/08 17:33:59: MyFrame::OnClose
04/08/08 17:33:59: MyFrame::~MyFrame()
04/08/08 17:33:59: MyApp::OnExit
OnClose handler called, then frame dtor, then wxApp::Exit().
That's what i expected.
However, when the system is shut down, i get this:
04/08/08 17:34:45: MyApp::OnAppQueryEndSession
04/08/08 17:34:45: MyFrame::OnClose
04/08/08 17:34:45: MyApp::OnAppEndSession
04/08/08 17:34:45: MyFrame::OnClose
I receive the EVT_END_SESSION events and the OnClose handler gets
called, but neither the frame dtor nor wxApp::Exit().
Event if i explicitly call frame->Destroy() or wxApp()::ExitMainLoop()
in the EVT_END_SESSION event handler this does not change.
Is this the "normal" expected behavior?
Problem is that my application settings are saved in the dtors of
several different classes and calling them all explictly from the
EVT_END_SESSION event will be quite some work (and probably lead to
pretty ugly code)
Thanks for any input.
Eric.
EJ> However, when the system is shut down, i get this:
EJ>
EJ> 04/08/08 17:34:45: MyApp::OnAppQueryEndSession
EJ> 04/08/08 17:34:45: MyFrame::OnClose
EJ> 04/08/08 17:34:45: MyApp::OnAppEndSession
EJ> 04/08/08 17:34:45: MyFrame::OnClose
EJ>
EJ> I receive the EVT_END_SESSION events and the OnClose handler gets
EJ> called, but neither the frame dtor nor wxApp::Exit().
EJ>
EJ> Event if i explicitly call frame->Destroy() or wxApp()::ExitMainLoop()
EJ> in the EVT_END_SESSION event handler this does not change.
EJ>
EJ> Is this the "normal" expected behavior?
No, absolutely not. But I don't understand how can the dtor not be called,
could it be that the output just doesn't end up in the file somehow?
Regards,
VZ
--
TT-Solutions: wxWidgets consultancy and technical support
http://www.tt-solutions.com/
This is bug 1428691 "[wxMSW] wxApp::OnExit never called on log off"
http://sourceforge.net/tracker/index.php?func=detail&aid=1428691&group_id=9863&atid=109863
EJ> Is there a way to emulate system shutdown without actually doing it?
I hoped to be able to do it using a Windows copy in a VM but unfortunately
this didn't work -- Windows doesn't want to resume properly in a VM for me.
So debugging this is indeed rather painful...
On Tue, 8 Apr 2008, Vadim Zeitlin wrote:
> On Tue, 8 Apr 2008 20:21:45 +0200 Eric Jensen <m...@j-dev.de> wrote:
>
> EJ> Is there a way to emulate system shutdown without actually doing it?
>
> I hoped to be able to do it using a Windows copy in a VM but unfortunately
> this didn't work -- Windows doesn't want to resume properly in a VM for me.
> So debugging this is indeed rather painful...
Doesn't Windows inform the app by sending a message? If so, couldn't the
app send this message to itself with PostMessage()?
Cheers, Chris.
--
_____ __ _
\ __/ / ,__(_)_ | Chris Wilson <0000 at qwirx.com> - Cambs UK |
/ (_/ ,\/ _/ /_ \ | Security/C/C++/Java/Ruby/Perl/SQL Developer |
\ _/_/_/_//_/___/ | We are GNU : free your mind & your software |
CW> Doesn't Windows inform the app by sending a message? If so, couldn't the
CW> app send this message to itself with PostMessage()?
If this is enough to reproduce the bug, it could indeed be done. But
somehow I don't think it's going to be enough to just do this. I'd like to
be proved wrong, of course.
I have such problem in my app too:( I have tested a lot of variants a
few days ago including:
wxExit(); ExitMainLoop(); direct deletion of main frame delete pMainFrame,
or such code
mainWindow->CloseFlashnote(true);
wxIdleEvent t;
mainWindow->AddPendingEvent(t);
while (Pending()) Dispatch();
Nothing works correctly simultaneously under XP, Windows 2000 and Vista .
So, now I am not trying to _close_ app when EVT_QUERY_END_SESSION is
arrived. I just save my settings and all. It seems to work well.
> i know there is a previous thread with this topic, but it didn't
> really solve the problem.
>
> I added some additional event handlers and logfile output to the
> minimal sample to describe the problem.
>
> When the application is started and quit using the menu entry or close
> button of the main frame, i get this log:
>
> 04/08/08 17:33:59: MyFrame::OnClose
> 04/08/08 17:33:59: MyFrame::~MyFrame()
> 04/08/08 17:33:59: MyApp::OnExit
>
> OnClose handler called, then frame dtor, then wxApp::Exit().
> That's what i expected.
>
> However, when the system is shut down, i get this:
>
> 04/08/08 17:34:45: MyApp::OnAppQueryEndSession
> 04/08/08 17:34:45: MyFrame::OnClose
> 04/08/08 17:34:45: MyApp::OnAppEndSession
> 04/08/08 17:34:45: MyFrame::OnClose
>
> I receive the EVT_END_SESSION events and the OnClose handler gets
> called, but neither the frame dtor nor wxApp::Exit().
>
> Event if i explicitly call frame->Destroy() or wxApp()::ExitMainLoop()
> in the EVT_END_SESSION event handler this does not change.
>
> Is this the "normal" expected behavior?
>
> Problem is that my application settings are saved in the dtors of
> several different classes and calling them all explictly from the
> EVT_END_SESSION event will be quite some work (and probably lead to
> pretty ugly code)
>
> Thanks for any input.
>
--
Best regards, Nikolay Tyushkov
http://softvoile.com - Work with texts faster!
EJ>> However, when the system is shut down, i get this:
EJ>>
EJ>> 04/08/08 17:34:45: MyApp::OnAppQueryEndSession
EJ>> 04/08/08 17:34:45: MyFrame::OnClose
EJ>> 04/08/08 17:34:45: MyApp::OnAppEndSession
EJ>> 04/08/08 17:34:45: MyFrame::OnClose
EJ>>
EJ>> I receive the EVT_END_SESSION events and the OnClose handler gets
EJ>> called, but neither the frame dtor nor wxApp::Exit().
EJ>>
EJ>> Event if i explicitly call frame->Destroy() or wxApp()::ExitMainLoop()
EJ>> in the EVT_END_SESSION event handler this does not change.
EJ>>
EJ>> Is this the "normal" expected behavior?
i did some more research on this topic, putting debug output into a
file at many places in the wx eventloop and message handlers.
Although i can't provide a definite proof, i think the behavior i
described is indeed "normal":
After responding to the WM_ENDSESSION handler, Windows immediately
kills the process without any chance to do any more work.
Various posts regarding this topic i found in Google seem to support
that theory, although none of them explicitly states it.
MSDN says:
"Once an application responds to WM_ENDSESSION, Windows closes it."
Source:
http://msdn2.microsoft.com/en-us/library/ms700677.aspx
Unfortunately this is not very clear, but "might" mean what i
observed.
Regards, Eric
EJ> i did some more research on this topic, putting debug output into a
EJ> file at many places in the wx eventloop and message handlers.
EJ>
EJ> Although i can't provide a definite proof, i think the behavior i
EJ> described is indeed "normal":
Thanks for your investigations! I didn't know that Windows terminated the
process forcefully in this case (this seems such a nasty thing to do, too)
but http://blogs.msdn.com/michen/archive/2008/04/04/Application-termination-when-user-logs-off.aspx
confirms that this is indeed the case. It also indirectly confirmed by the
MSDN documentation of WM_ENDSESSION:
If the wParam parameter is TRUE, the session can end any time after
all applications have returned from processing this message.
Therefore, an application should perform all tasks required for
termination before returning from this message.
So we clearly have to destroy all the existing windows from either
wxWindowMSW::HandleEndSession() or, if we want this to be configurable,
from wxEVT_END_SESSION handler in wxApp. If you (or anybody) else can write
a patch adding such handler, it would be great. Of course, writing it
should be simple enough, it's the testing part which risks to be
time-consuming.
Friday, April 11, 2008, 7:54:46 PM, you wrote:
VZ> So we clearly have to destroy all the existing windows from either
VZ> wxWindowMSW::HandleEndSession() or, if we want this to be configurable,
VZ> from wxEVT_END_SESSION handler in wxApp. If you (or anybody) else can write
VZ> a patch adding such handler, it would be great. Of course, writing it
VZ> should be simple enough, it's the testing part which risks to be
VZ> time-consuming.
i tried to implement what you described and looked through the
wx-sources if there was already a function that did that and found
wxApp::CleanUp(). I tried the following change:
void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
{
if (GetTopWindow())
GetTopWindow()->Close(true);
// two new lines here
this->CleanUp();
this->ExitMainLoop();
}
This seems to work fine in the sample i tried and one of my own,
bigger and more complex application.
However i don't know the internal workings of wxwidgets well enough to
estimate any possible side-effects of this code. And i'm a little
concerned about what would happen, if Windows - for whatever reason -
does *not* forcefully kill the process. In that case i'd be cutting the
tree i'm sitting on by destroying all toplevel windows there (because
the code is in effect called by an event handler in wxTopLevelWindow)
Regards, Eric
EJ> Friday, April 11, 2008, 7:54:46 PM, you wrote:
EJ>
EJ> VZ> So we clearly have to destroy all the existing windows from either
EJ> VZ> wxWindowMSW::HandleEndSession() or, if we want this to be configurable,
EJ> VZ> from wxEVT_END_SESSION handler in wxApp. If you (or anybody) else can write
EJ> VZ> a patch adding such handler, it would be great. Of course, writing it
EJ> VZ> should be simple enough, it's the testing part which risks to be
EJ> VZ> time-consuming.
EJ>
EJ> i tried to implement what you described and looked through the
EJ> wx-sources if there was already a function that did that and found
EJ> wxApp::CleanUp(). I tried the following change:
Hello,
Thanks for looking at this!
EJ> void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
EJ> {
EJ> if (GetTopWindow())
EJ> GetTopWindow()->Close(true);
EJ>
EJ> // two new lines here
EJ> this->CleanUp();
EJ> this->ExitMainLoop();
EJ> }
EJ>
EJ> This seems to work fine in the sample i tried and one of my own,
EJ> bigger and more complex application.
From looking at the code it looks like we should rather be calling
OnExit() and then wxEntryCleanup() here -- at least this is what's done
during the normal application termination and so should fit the
expectations best.
OnExit() should be called to execute any cleanup code the user program
might put there and wxEntryCleanup() will call wxApp::CleanUp() and do a
few more things.
EJ> However i don't know the internal workings of wxwidgets well enough to
EJ> estimate any possible side-effects of this code. And i'm a little
EJ> concerned about what would happen, if Windows - for whatever reason -
EJ> does not forcefully kill the process. In that case i'd be cutting the
EJ> tree i'm sitting on by destroying all toplevel windows there (because
EJ> the code is in effect called by an event handler in wxTopLevelWindow)
Normally the code should work fine when the event handler destroys the
object it was called on. However to make it totally "safe" we could call
exit() ourselves from here. This would have a (big) added benefit of
executing dtors of global objects too.
Could you please check if
void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
{
// Windows will terminate the process soon after we
// return from WM_ENDSESSION handler anyhow, so make
// sure we at least execute our cleanup code before
const int rc = OnExit();
wxEntryCleanup();
// calling exit() instead of ExitProcess() or not doing
// anything at all and being killed by Windows has the
// advantage of executing the dtors of global objects
exit(rc);
}
works correctly?
Thanks!
Monday, April 14, 2008, 9:48:05 PM, you wrote:
VZ> Normally the code should work fine when the event handler destroys the
VZ> object it was called on. However to make it totally "safe" we could call
VZ> exit() ourselves from here. This would have a (big) added benefit of
VZ> executing dtors of global objects too.
VZ> Could you please check if
VZ> void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
VZ> {
VZ> // Windows will terminate the process soon after we
VZ> // return from WM_ENDSESSION handler anyhow, so make
VZ> // sure we at least execute our cleanup code before
VZ> const int rc = OnExit();
VZ> wxEntryCleanup();
VZ> // calling exit() instead of ExitProcess() or not doing
VZ> // anything at all and being killed by Windows has the
VZ> // advantage of executing the dtors of global objects
VZ> exit(rc);
VZ> }
VZ> works correctly?
this code seems to work fine. I tested once again with a small sample
any my own application. I also tested it using the RMTool.exe from the
Vista SDK (which sends the WM_ENDSESSION, but does not kill the
application). I didn't have an debugging environment on the Vista
machine, just my logging output which looked fine. And Vista didn't
complain about anything either ;)
If this code makes it into the library, maybe it should be mentioned
in the docs that the wxApp dtor will not get called if the
WM_ENDSESSION is received, so if anyone has important cleanup code in
his dtor, he can move the code to wxApp::OnExit().
Thanks, Eric
EJ> VZ> Could you please check if
EJ>
EJ> VZ> void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
EJ> VZ> {
EJ> VZ> // Windows will terminate the process soon after we
EJ> VZ> // return from WM_ENDSESSION handler anyhow, so make
EJ> VZ> // sure we at least execute our cleanup code before
EJ> VZ> const int rc = OnExit();
EJ>
EJ> VZ> wxEntryCleanup();
EJ>
EJ> VZ> // calling exit() instead of ExitProcess() or not doing
EJ> VZ> // anything at all and being killed by Windows has the
EJ> VZ> // advantage of executing the dtors of global objects
EJ> VZ> exit(rc);
EJ> VZ> }
EJ>
EJ> VZ> works correctly?
EJ>
EJ> this code seems to work fine. I tested once again with a small sample
EJ> any my own application. I also tested it using the RMTool.exe from the
EJ> Vista SDK (which sends the WM_ENDSESSION, but does not kill the
EJ> application).
Interesting, I didn't know about it, what exactly is it part of? I don't
have it in my VC9 installation (which normally installs Vista SDK).
EJ> I didn't have an debugging environment on the Vista machine
You probably should be able to use remote debugging from the machine where
VC is installed.
EJ> If this code makes it into the library, maybe it should be mentioned
EJ> in the docs that the wxApp dtor will not get called if the
EJ> WM_ENDSESSION is received, so if anyone has important cleanup code in
EJ> his dtor, he can move the code to wxApp::OnExit().
Normally wxEntryCleanup will delete the application, do you mean it
doesn't happen during your test?
Thanks,
I tested the code (realy log off/shut down system) and works fine.
The call stack:
MyApp::MyApp
MyFrame::MyFrame
MyApp::OnQueryEndSession
MyFrame::OnClose
MyApp::OnEndSession
MyApp::OnExit
MyFrame::~MyFrame
I thoought about adding GetTopWindow()->Destroy() before OnExit() to
call windows and controlls destructors, because user could have done
various usefull things in them such as writing changes to file or
freeing resources.
Tests shown that it isn't required, because MyFrames destructor is
called, so everything should work right.
--
Pozdrowienia,
Marcin 'Malcom' Malich
me /at/ malcom.pl
http://malcom.pl
Wednesday, April 16, 2008, 12:14:53 AM, you wrote:
EJ>> this code seems to work fine. I tested once again with a small sample
EJ>> any my own application. I also tested it using the RMTool.exe from the
EJ>> Vista SDK (which sends the WM_ENDSESSION, but does not kill the
EJ>> application).
VZ> Interesting, I didn't know about it, what exactly is it part of? I don't
VZ> have it in my VC9 installation (which normally installs Vista SDK).
my mistake. It's part of the Vista Logo testing tools, pretty well
hidden on msdn ;)
http://download.microsoft.com/download/d/2/5/d2522ce4-a441-459d-8302-be8f3321823c/LogoToolsv1.0.msi
EJ>> I didn't have an debugging environment on the Vista machine
VZ> You probably should be able to use remote debugging from the machine where
VZ> VC is installed.
unfortunately that doesn't work, because when i log off on the Vista
machine, it also kills the debugging session immediately.
VZ> Normally wxEntryCleanup will delete the application, do you mean it
VZ> doesn't happen during your test?
yes, the wxApp dtor will not get called. Actually it seems the very
last call that is executed during cleanup is
::DestroyWindow(GetHwnd())
in
wxWindowMSW::~wxWindowMSW()
if it's called for the last toplevel window.
Regards,
Eric
EJ> VZ> Normally wxEntryCleanup will delete the application, do you mean it
EJ> VZ> doesn't happen during your test?
EJ> yes, the wxApp dtor will not get called. Actually it seems the very
EJ> last call that is executed during cleanup is
EJ> ::DestroyWindow(GetHwnd())
EJ> in
EJ> wxWindowMSW::~wxWindowMSW()
EJ> if it's called for the last toplevel window.
Hmm, this is still pretty bad then as plenty of cleanup code (at least
dtor of wxApp and also dtors of all the global objects) is still not being
executed. I wonder if we can work around this by creating a dummy window in
wxApp::OnEndSession() (not a wxWindow as it would be destroyed too, but
just call ::CreateWindow())? If this fails (because I can imagine Windows
refusing to create windows during shutdown) we'd have to somehow avoid the
destruction of the existing windows, e.g. maybe by setting their m_hWnd
member to NULL from OnEndSession().
But before embarking on this, could you please test if the simple solution
of calling CreateWindow() works by chance?
Thanks again for your testing!
EJ> just creating a dummy window did not work. Even creating a dummy
EJ> wxFrame in wxApp::OnEndSession() did not change the behavior. Which
EJ> confused me a bit, cause i'm still not 100% sure what actually
EJ> triggers killing the process.
My hypothesis is that the destruction of the last window belonging to it
does.
EJ> Your second idea worked though. Here is what we have now:
EJ>
EJ> void wxApp::OnEndSession(wxCloseEvent& WXUNUSED(event))
EJ> {
EJ> //if(GetTopWindow()) GetTopWindow()->Close(true);
EJ> // Windows will terminate the process soon after we
EJ> // return from WM_ENDSESSION handler anyhow, so make
EJ> // sure we at least execute our cleanup code before
EJ>
EJ> if(wxTopLevelWindows.GetCount()>0) {
EJ> wxTopLevelWindows[0]->SetHWND(NULL);
EJ> }
EJ>
EJ> const int rc = OnExit();
EJ> wxEntryCleanup();
EJ>
EJ> // calling exit() instead of ExitProcess() or not doing
EJ> // anything at all and being killed by Windows has the
EJ> // advantage of executing the dtors of global objects
EJ> exit(rc);
EJ> }
EJ>
EJ> Saving the old HWND and calling ::DestroyWindow() on it later does not
EJ> work (leads to a crash).
EJ>
EJ> I'll do some more testing tomorrow.
Please let me know if you discover any problems with this or whether I
should go ahead and apply it.
Thanks,
VZ> Please let me know if you discover any problems with this or whether I
VZ> should go ahead and apply it.
sorry for the late reply, only now had time to do some more testing.
The above code seems to work fine. It still feels a bit like
a dirty hack, but i guess that's what you need to circumvent Windows
behavior sometimes ;)
Thanks for your help.
Regards, Eric
EJ> The above code seems to work fine. It still feels a bit like
EJ> a dirty hack, but i guess that's what you need to circumvent Windows
EJ> behavior sometimes ;)
Thanks for testing, I've applied this change to the trunk too now. And I
hope that our understanding of what goes on is correct, the hack is not
especially dirty but it's just a pity that this behaviour is not documented
anywhere (AFAIK) and we have to second guess it.
Thanks again,