My App (Windows 7, Visual C++, Release-Build) needs to write some data when windows is shutting down (restarting, user logs off). After all I want to do the same stuff as on receiving WM_CLOSE-Message which gets called during a regular close of my App (Alt-f4, Closing the window,...)
I don't need any User-Input, Dialogs and so on. Just silent writing. The writing itself should last less than a second.
To do so I do the following:
LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_CLOSE){
TRACE(_T("got WM_CLOSE"));
SaveMyData();
}
switch (message) {
case WM_QUERYENDSESSION:
TRACE(_T("findme: WM_QUERYENDSESSION"));
{
BOOL bShutdownBlocked = ShutdownBlockReasonCreate(theApp.m_pMainWnd->GetSafeHwnd(), _T("Save data"));
TRACE(_T("blocked: %d, GetlastError: %d"), bShutdownBlocked, GetLastError());
}
//continue shutdown-sequence
return TRUE;
case WM_ENDSESSION:
TRACE(_T("findme: WM_ENDSESSION. Store? %d"), FALSE != (BOOL)wParam);
if (FALSE != (BOOL)wParam)
{
long lTickStart = GetTickCount();
TRACE(_T(">>> Sleep..."));
::Sleep(1000); //just a Test: do something while shutting down
TRACE(_T("<<< Sleep: %d ms"), GetTickCount()-lTickStart);
BOOL bUnblockShutdown = ShutdownBlockReasonDestroy(theApp.m_pMainWnd->GetSafeHwnd());
TRACE(_T("unblock: %d"), bUnblockShutdown);
}
return 0L;
default:
return CMDIFrameWnd::WindowProc(message, wParam, lParam);
}
}
All Traces are redirected into a file which I examine.
I tested this with the Restart Manager from the Logo Testing Tools for Windows under Win7 (rmtool -S -pid) and it works fine. I get the following Trace-Output:
findme: WM_QUERYENDSESSION
blocked 1, GetlastError: 0
findme: WM_ENDSESSION. Store? 1
>>> Sleep...
<<< Sleep: 1015 ms
unblock: 1
got WM_CLOSE
Notice the "got WM_CLOSE" which calls code to save my Data.
But it does not work, when I actually shut windows down or logoff or use rmtool with the -lr option. In this case I just get the following output:
findme: WM_QUERYENDSESSION
blocked 1, GetlastError: 0
findme: WM_ENDSESSION. Store? 1
>>> Sleep...
<<< Sleep: 1000 ms
unblock: 1
Here the WM_CLOSE-Message is no received.
Is it wrong to rely on that Message when shutting down or what to I make wrong?
As MSDN says
When an application returns TRUE for WM_QUERYENDSESSION, it receives the WM_ENDSESSION message and it is terminated, regardless of how the other applications respond to the WM_QUERYENDSESSION message.
MSDN does not specify that Windows terminates your application by sending a WM_CLOSE to it. You should do the saving in the WM_ENDSESSION handler to be on the safe side.
BTW, you should remove the call to ShutdownBlockReasonCreate. What is the use of it? If it worked you would not get a WM_ENDSESSION anymore. That function should be called in advance if you want to prevent a shutdown.
I think the trick is do do everything right in WM_ENDSESSION without sending or receiving any Windows-Messages.
Also you shouldn't rely too much on the rmtool.exe, at least when used without the -l-Parameter. It just behaves diffent than shutting down windows. So to test shutdown-scenarios you really have to shutdown (logoff, restart) instead of just simulating it.
Related
I have to catch the click on close button of console event, which corresponds to ctrl-break event SIGBREAK but apparently there is a kind of timeout which does not allows me to do anything longer than 5 seconds, after what program seems to be aborted (so the message "Properly ended !" will never happend).
How can I force the system to perform closing operations until the end (or to extend the timeout to 60 seconds) ?
Note: with the same method, I can successfully handle CTRL+C event (SIGINT) using fflush(stdout); just before doStuffs();
Note 2: my code is based on this answer: https://stackoverflow.com/a/181594/1529139
#include <csignal>
void foo(int sig)
{
signal(sig, foo); // (reset for next signal)
// do stuffs which takes aboutly 15 seconds
doStuffs();
cout << "Properly ended !";
}
int main(DWORD argc, LPWSTR *argv)
{
signal(SIGBREAK, foo);
myProgramLoop();
}
I believe the timeout is fixed and there is no way to modify it, see API reference:
The system also displays the dialog box if the process does not respond within a certain time-out period (5 seconds for CTRL_CLOSE_EVENT, and 20 seconds for CTRL_LOGOFF_EVENT and CTRL_SHUTDOWN_EVENT).
A process can use the SetProcessShutdownParameters function to prevent the CTRL_LOGOFF_EVENT and CTRL_SHUTDOWN_EVENT dialog box from being displayed. In this case, the system just terminates the process when a HandlerRoutine returns TRUE or when the time-out period elapses.
How about an alternative approach, just disable the close button of your console window:
GUITHREADINFO info = {0};
info.cbSize=sizeof(info);
GetGUIThreadInfo(NULL, &info);
HMENU hSysMenu = GetSystemMenu(info.hwndActive, FALSE);
EnableMenuItem(hSysMenu, SC_CLOSE, MF_GRAYED);
Now you can safely handling closing through Ctrl-C or other keyboard input instead.
How can i detect when a users computer goes into sleep (laptop lid closes, sleep mode due to inactivity, etc)?
I need to do this to disconnect the users TCP connection. Basically we got a simple chat application where we want to take the user off-line.
There is no Qt way to detect when computer goes to sleep or hibernation. But there are some platform dependent ways to do it.
On Windows you can listen for the WM_POWERBROADCAST message in your WindowProc handler:
LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (WM_POWERBROADCAST == message && PBT_APMSUSPEND == wParam) {
// Going to sleep
}
}
On linux you can put the following shell script in /etc/pm/sleep.d which executes a program with arguments. You can start a program and notify your main application in some way:
#!/bin/bash
case $1 in
suspend)
#suspending to RAM
/Path/to/Program/executable Sleeping
;;
resume)
#resume from suspend
sleep 3
/Path/to/Program/executable Woken
;;
esac
For OS X you can see this.
You need to use the QNetworkConfigurationManager class available in Qt 4.7 and above.
QNetworkConfigurationManager provides access to the network
configurations known to the system and enables applications to detect
the system capabilities (with regards to network sessions) at runtime.
In particular look at the void QNetworkConfigurationManager::onlineStateChanged(bool isOnline) signal.
You may use 2 QTimers. One timer to activate slot every period of time and the second is to keep time tracking. Something like this:
// Header
QTimer timerPeriod;
QTimer timerTracker;
// Source
timerPeriod.setInterval(60*1000);
connect(&timerPeriod, SIGNAL(timeout()), this, SLOT(timerTimeout()));
// Track time to the next midnight
timerTracking.setInterval(QDateTime::currentDateTime().msecsTo(QDateTime(QDate::currentDate().addDays(1), QTime(00, 00))));
timerPeriod.start();
timerTracking.start();
// SLOT
void timerTimeout() {
int difference = abs(timerTracking.remainingTime() - QDateTime::currentDateTime().msecsTo(QDateTime(QDate::currentDate().addDays(1), QTime(00, 00))));
// There are some diffrences in times but it is rather irrelevant. If
if (difference > 500) {
diffrence > 500 timerTracking should be reset
// If diffrence is > 2000 it is sure hibernation or sleep happend
if (difference > 2000) {
// Hibernation or sleep action
}
// Taking care of small and big diffrences by reseting timerTracking
timerTracking.stop();
timerTracking.setInterval(QDateTime::currentDateTime().msecsTo(QDateTime(QDate::currentDate().addDays(1), QTime(00, 00))));
timerTracking.start();
}
}
I have an application (native C++, Windows), that cannot be run simultaneously on one machine. The behavior that I want to implement is this: on the attempt to run second instance of the application the first one stops running.
To do so I want to use WinApi function BroadcastSystemMessage() something like an example below.
When the application start it sends:
BroadcastSystemMessage(BSF_POSTMESSAGE, &dwRecepients, 0x666, 0, 0);
But, when I run my application in debug mode it doesn't hit
case 0x666:
int iClose = 0 + 1;
break;
when I start another instance. The other messages are nandled correctly (WM_KEYDOWN, WM_ACTIVATE and others).
What I'm I doing wrong?
In order to broadcast a custom message you need to create an id for it with the RegisterWindowMessage function, for example:
UINT msg666 = RegisterWindowMessage(L"custom_devil_message");
and use it both in the sending and receiving code:
// sending code
BroadcastSystemMessage(BSF_POSTMESSAGE, &dwRecepients, msg666, 0, 0);
// receiving code
case msg666:
int iClose = 0 + 1;
break;
Remember that messages do not work for console applications.
The solution began to work after I changed the type of the message to WM_APPCOMMAND + 10. Anyway, it didn't help, because BroadcastSystemMessage() doesn't broadcast messages to tabs in a browser, which is my case. Also, I couldn't find the range of message types that are allowed to be sent with BroadcastSystemMessage().
Background:
In my application written in C++, I create a worker thread which in turn creates two threads using CreateThread(). The two threads which worker thread creates, talk to WCF Service through a client which is implemented using Windows Web Services API which offers C/C++ application programming interface (API) for building SOAP based web services and clients to them. My application implements only the client using this API.
Problem:
The problem I'm facing is that all other threads exit gracefully, except the worker thread, as you can see yourself, in the image below that WorkerThreadProc uses no CPU cycles yet it doesn't exit. There are also few other threads running which are not created by me, but by the runtime.
The thread states are as follows (as reported by ProcessExplorer):
WorkerThreadProc is in Wait:WrUserRequest state.
wWinMainCRTStartup is in Wait:UserRequest state.
All TpCallbackIndependent are in Wait:WrQueue state.
What are they waiting for? What could be possible causes that I need to look into? Also, what is the difference between WrUserRequest and UserRequest? And what does WrQueue mean? I've absolutely no idea what is going on here.
Here is my WorkerThreadProc code. I've removed all the logging statements except the last one at the bottom of the function:
DWORD WINAPI WorkerThreadProc(PVOID pVoid)
{
//Initialize GDI+
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Status status = GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
if ( status != Status::Ok )
{
return 1;
}
GuiThreadData *pGuiData = (GuiThreadData*)pVoid;
auto patternIdRequestQueue= new PatternIdRequestQueue();
auto resultQueue = new ResultQueue();
auto patternManager = new PatternManager(patternIdRequestQueue);
LocalScheduler *pScheduler = new LocalScheduler(resultQueue, patternManager);
bool bInitializationDone = pScheduler->Initialize(pGuiData->m_lpCmdLine);
if ( !bInitializationDone )
{
return 0;
}
//PatternIdThread
PatternIdThread patternIdThread(patternIdRequestQueue);
DWORD dwPatternIdThreadId;
HANDLE hPatternIdThread = CreateThread(NULL, 0, PatternIdThreadProc, &patternIdThread, 0, &dwPatternIdThreadId);
ResultPersistence resultPersistence(resultQueue);
DWORD dwResultPersistenceThreadId;
HANDLE hResultPersistenceThread = CreateThread(NULL, 0, ResultPersistenceThreadProc, &resultPersistence, 0, &dwResultPersistenceThreadId);
pScheduler->ScheduleWork(pGuiData->m_hWnd, pGuiData->m_hInstance, ss.str());
pScheduler->WaitTillDone();
patternIdThread.Close();
resultPersistence.Close();
delete pScheduler;
//Uninitialize GDI+
GdiplusShutdown(gdiplusToken);
dwRet = WaitForSingleObject(hPatternIdThread, INFINITE);
CloseHandle(hPatternIdThread);
dwRet = WaitForSingleObject(hResultPersistenceThread,INFINITE);
CloseHandle(hResultPersistenceThread);
SendMessage(pGuiData->m_hWnd, WM_CLOSE, 0, 0);
//IMPORTANT : this verbose message is getting logged!
T_VERBOSE(EvtSrcInsightAnalysis, 0, 0, "After sending message to destroy window");
delete patternManager;
delete patternIdRequestQueue;
delete resultQueue;
return 0;
}
Please see the T_VERBOSE macro, it is used to log verbose message. I see the message is getting logged, yet the thread doesn't exit!
EDIT:
I just commented the following line in my WorkerThreadProc, then worker thread exits gracefully!
SendMessage(pGuiData->m_hWnd, WM_CLOSE, 0, 0);
Does it mean that SendMessage is the culprit? Why would it block the thread the calling thread?
If we look at the docs for SendMessage, you can see this little quote:
To send a message and return immediately, use the SendMessageCallback
or SendNotifyMessage function. To post a message to a thread's message
queue and return immediately, use the PostMessage or PostThreadMessage
function.
and this:
Messages sent between threads are processed only when the receiving
thread executes message retrieval code. The sending thread is blocked
until the receiving thread processes the message. However, the sending
thread will process incoming nonqueued messages while waiting for its
message to be processed. To prevent this, use SendMessageTimeout with
SMTO_BLOCK set. For more information on nonqueued messages, see
Nonqueued Messages.
so from this we can see SendMessage will block till the message is processed, which may somehow lead to a deadlock in your code, as the msgproc doesn't reside in your worker thread, leading to a context switch (which is only triggered when the thread's queue is pumped for messages). Try using PostMessage, which immediately returns.
EDIT: there is also a nice little piece of info here on message deadlocks from SendMessage
I'm writting a console application in C++.
I use SetConsoleCtrlHandler to trap close and CTRL+C button. This allows for all my threads to stop and exit properly.
One of the thread performs some saving that require some time to complete and I have some code to wait in the console crtl handle routine. MSDN specify that a box should pop up after 5 seconds for CTRL_CLOSE_EVENT, but instead my process exits.
This is annoying for debugging console application too as the process exits before you can step through and I don't know what may be the problem (I have Windows 7 64bits).
Also, strangely if my routine returns TRUE (to simply disable the close action), it still closes the application. The routine does get called, so the SetConsoleCtrlHandler was successful installed.
e.g.:
BOOL WINAPI ConsoleHandlerRoutine(DWORD dwCtrlType)
{
if (dwCtrlType == CTRL_CLOSE_EVENT)
{
return TRUE;
}
return FALSE;
}
int _tmain(int argc, _TCHAR* argv[])
{
BOOL ret = SetConsoleCtrlHandler(ConsoleHandlerRoutine, TRUE);
while (true)
{
Sleep(1000);
}
return 0;
}
Any ideas?
It looks like you can no longer ignore close requests on Windows 7.
You do get the CTRL_CLOSE_EVENT event though, and from that moment on, you get 10 seconds to do whatever you need to do before it auto-closes. So you can either do whatever work you need to do in the handler or set a global flag.
case CTRL_CLOSE_EVENT: // CTRL-CLOSE: confirm that the user wants to exit.
close_flag = 1;
while(close_flag != 2)
Sleep(100);
return TRUE;
Fun fact: While the code in your CTRL_CLOSE_EVENT event runs, the main program keeps on running. So you'll be able to check for the flag and do a 'close_flag = 2;' somewhere. But remember, you only have 10 seconds. (So keep in mind you don't want to hang up your main program flow waiting on keyboard input for example.)
I suspect that this is by-design on Windows 7 - if the user wants to quit your application, you're not allowed to tell him "No".
There is no need to wait for any flag from the main thread, the handler terminates as soon as the main thread exits (or after 10s).
BOOL WINAPI ConsoleHandler(DWORD dwType)
{
switch(dwType) {
case CTRL_CLOSE_EVENT:
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT:
set_done();//signal the main thread to terminate
//Returning would make the process exit!
//We just make the handler sleep until the main thread exits,
//or until the maximum execution time for this handler is reached.
Sleep(10000);
return TRUE;
default:
break;
}
return FALSE;
}
Xavier's comment is slightly wrong.
Windows 7 allows your code in the event handler ~10 seconds. If you haven't exited the event handler in 10 seconds you are terminated. If you exit the event handler you are terminated immediately. Returning TRUE does not post a dialog. It just exits.
You're making this more complicated than it needs to be. I don't know exactly why your app is closing, but SetConsoleCtrlHandler(NULL, TRUE) should do what you want:
http://msdn.microsoft.com/en-us/library/ms686016(VS.85).aspx
If the HandlerRoutine parameter is NULL, a TRUE value causes the calling process to ignore CTRL+C input, and a FALSE value restores normal processing of CTRL+C input.