Qt detect when computer goes into sleep? - c++

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();
}
}

Related

Saving Data on Windows Shutdown

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.

Why does my Windows Console Close Event Handler time out?

I build the following program in VS2017/Windows 10. When I run it, I hit close and ctrl_handler() is called as expected, but after ~three seconds the process is forcefully terminated anyway.
This is a problem because my real application writes large log files and three seconds is not long enough to get them onto disk.
Where is the documentation that describes this behaviour? Its not in those for the CTRL+CLOSE signal.
Where is the timeout set? Can it be modified at the application level? Or with a group policy?
#include <Windows.h>
bool mainThreadRunning;
bool mainThreadFinished;
BOOL ctrl_handler(DWORD event)
{
if (event == CTRL_CLOSE_EVENT) {
mainThreadRunning = false;
while (!mainThreadFinished) {
Sleep(100);
}
return TRUE;
}
return FALSE;
}
int main()
{
mainThreadRunning = true;
mainThreadFinished = false;
SetConsoleCtrlHandler((PHANDLER_ROUTINE)(ctrl_handler), TRUE); // make sure when the user hits the close button in the console we shut down cleanly
while (true)
{
}
return 0;
}
I suppose this is the reference you were looking for:
Unfortunately, this is determined by the OS. There is documentation describing the behavior in the HandlerRoutine Callback docs:
" In this case, no other handler functions are called, and the system displays a pop-up dialog box that asks the user whether to terminate the process. The system also displays this 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 or CTRL_SHUTDOWN_EVENT)."
There is no (at least public, documented) API to change this timeout.
Note:
A process can use the SetProcessShutdownParameters function to prevent the system from displaying a dialog box to the user during logoff or shutdown. In this case,the system terminates the process when HandlerRoutine returns TRUE or when the time-out period elapses.
The operating system intentionally forces termination if it considers handler is taking too much time to complete.
Important note pulled from comments below:
... Ctrl+C is not subject to the time-out (I've tested it, and that's what I am using now).

How to properly handle SIGBREAK when closing console?

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.

MFC application hangs in thread signaled for termination

I'm writing an app in MFC with a background worker thread (created via _beginthreadex) and UI thread. A button is clicked from the UI thread to begin and end the worker thread. It starts the background thread if the m_threadRunning flag is false, and stops the background thread if it is true. The way I go about stopping the thread is I set the m_threadRunning flag to false and call WaitForSingleObject to let the background thread finish what it is doing.
My app has four different states. I had the first three states working properly, and adding the fourth state is what caused my problem. For the fourth state I want to be able to sample the desktop and send average RGB values to the COM port for processing. When in any of the first three states, if I want to stop execution of sending data to the COM port, it will terminate normally and without problems. If I am in the fourth state and click "stop", the application will hang since I have no time out on my call to WaitForSingleObject.
I also have a custom CEdit box CColorEdit that shows the current RGB values. I update this from the background thread when I'm in either state 3 or 4 (since they both change the colors dynamically). I've narrowed down the problem to a call to when I'm setting the color in which I call either Invalidate or RedrawWindow.
I've come up with a few solutions, but I don't like any of them and would rather understand what is causing the problem since my goal in writing this in MFC is to learn and understand MFC. Here is what has resolved the problem:
I call Sleep() in my worker thread already at about 60 samples/second. Changing this to a lower value, like 30 samples/second, resolved the problem most of the time.
I poll m_threadRunning in my worker thread to check if the thread should be terminated. If I poll it after sampling the screen but before updating the edit control, this resolves the problem most of the time.
I do a timeout of 5 seconds when calling WaitForSingleObject and call TerminateThread to manually kill the thread when it fails to wait, this resolves the problem all of the time. This is my solution in place for now.
Here are the relevant code bits (I lock around any use of outBytes):
void CLightControlDlg::UpdateOutputLabel()
{
CSingleLock locker(&m_crit);
locker.Lock();
m_outLabel.SetColor(outBytes[1], outBytes[2], outBytes[3]); //the call to this freezes the program
CString str;
str.Format(L"R = %d; G = %d; B = %d;", outBytes[1], outBytes[2], outBytes[3]);
m_outLabel.SetWindowText(str);
}
This section of code is for terminating the worker thread
m_threadRunning = false;
locker.Unlock(); //release the lock...
//omitted re-enabling of some controls
//normally this is just WaitForSingleObject(m_threadHand, INFINITE);
if(WaitForSingleObject(m_threadHand, 5000) == WAIT_TIMEOUT)
{
MessageBox(L"There was an error cancelling the I/O operation to the COM port. Forcing a close.");
TerminateThread(m_threadHand, 0);
}
CloseHandle(m_threadHand);
CloseHandle(m_comPort);
m_threadHand = INVALID_HANDLE_VALUE;
m_comPort = INVALID_HANDLE_VALUE;
The code in my derived edit control that updates the text color:
void SetColor(byte r, byte g, byte b)
{
_r = r;
_g = g;
_b = b;
br.DeleteObject();
br.CreateSolidBrush(RGB(r,g,b));
Invalidate(); //RedrawWindow() freezes as well
}
And finally, the code for my thread procedure:
unsigned int __stdcall SendToComProc(void * param)
{
CLightControlDlg *dlg = (CLightControlDlg*)param;
while(1)
{
if(!dlg->IsThreadRunning())
break;
switch(dlg->GetCurrentState())
{
case TransitionColor: //state 3
dlg->DoTransition();
dlg->UpdateOutputLabel();
break;
case ScreenColor: //state 4
dlg->DoGetScreenAverages();
//if(!dlg->IsThreadRunning()) break; //second poll to IsThreadRunning()
dlg->UpdateOutputLabel();
break;
}
dlg->SendToCom();
Sleep(17); // Sleep for 1020 / 60 = 17 = ~60samples/sec
}
return 0;
}
Any help you can provide is greatly appreciated!
You get a deadlock when the worker thread attempts to access controls that were created in the main thread and the main thread is suspended in WaitForSingleObject. Updating controls from the worker thread can only proceed when the main thread accepts the associated message to the control.
Remove all accesses to the controls from the worker thread. Instead, PostMessage a custom message to a window in the main thread. An example is here:
http://vcfaq.mvps.org/mfc/12.htm
The same technique could be used to notify the main thread that the worker thread has completed, so you could avoid WaitForSingleObject.

SetConsoleCtrlHandler routine issue

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.