Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding waitable timers

There's something I don't understand about waitable timers. I searched online and read specs on MSDN plus whatever I could find on stackoverflow (such as link) but my timers fire almost immediately after the PC is suspended.

To focus on the problem I wrote a small test app using XE5 (64-bit) in Windows 7 and tried to duplicate the project found here: http://www.codeproject.com/Articles/49798/Wake-the-PC-from-standby-or-hibernation

I thought the problem was the way I derive time but I can't seem to find the problem.

The test app looks like this (very simple):

enter image description here

I declare the thread type as follows

type
   TWakeupThread    = class(TThread)
   private
     FTime:         LARGE_INTEGER;
  protected
     procedure      Execute; override;
  public
     constructor    Create(Time: LARGE_INTEGER);
  end;

...

constructor TWakeupThread.Create(Time: LARGE_INTEGER);
begin
  inherited Create(False);
  FreeOnTerminate:=True;
  FTime:=Time;
end;


procedure TWakeupThread.Execute;
var
   hTimer:     THandle;
begin
  // Create a waitable timer.
  hTimer:=CreateWaitableTimer(nil, True, 'WakeupThread');
  if (hTimer <> 0) then
  begin
     //CancelWaitableTimer(hTimer);
     if (SetWaitableTimer(hTimer, FTime.QuadPart, 0, nil, nil, True)) then
     begin
        WaitForSingleObject(hTimer, INFINITE);
     end;
     CloseHandle(hTimer);
  end;
end;

Now when the "Set timer" button is clicked I calculate file time this way and create the thread.

procedure TForm1.btnSetTimerClick(Sender: TObject);
var
   iUTCTime : LARGE_INTEGER;
   SysTime  : _SystemTime;
   FTime    : _FileTime;
   hHandle  : THandle;
   dt       : TDateTime;
begin
   ReplaceDate(dt,uiDate.DateTime);
   ReplaceTime(dt,uiTime.DateTime);
   DateTimeToSystemTime(dt, SysTime);
   SystemTimeToFileTime(SysTime, FTime);
   LocalFileTimeToFileTime(FTime, FTime);
   iUTCTime.LowPart := FTime.dwLowDateTime;
   iUTCTime.HighPart := FTime.dwHighDateTime;
   TWakeupThread.Create(iUTCTime);
end;

This does not work. The timer seems to fire less then 2 minutes after the system enters suspend mode regardless of the amount of time selected. Any pointers as to what I am doing wrong would be appreciated.

EDIT

Found this interesting command line tool that let's us inspect the waitable timers. From command you can "see" the state of your waitable timers by typing:

powercfg -waketimers

I can use this to confirm that my timers are being set properly. I can also use this to confirm that my timers are still running when the PCs prematurely wake-up.

Using the same tool you can get a list of devices that are able to wake from hibernation (mouse, keyboard, network in my case):

powercfg -devicequery wake_armed

On all systems tested, the command "powercfg -lastwake" returns the following which I do not know how to decipher:

Wake History Count - 1
Wake History [0]
   Wake Source Count - 0

I enabled both sleep and hibernate in Windows and both will wake up after a few seconds. There's no keyboard / mouse activity and we don't have devices sending WOL (wake-on-lan) requests to these PCs.

I'm wondering if there's something special I need to do when calling SetSuspendState; here's my code:

function SetSuspendState(Hibernate, ForceCritical, DisableWakeEvent: Boolean): Boolean;
//Hibernate = False : system suspends
//Hibernate = True : system hibernates
begin
  if not Assigned(_SetSuspendState) then
     @_SetSuspendState := LinkAPI('POWRPROF.dll', 'SetSuspendState');
  if Assigned(_SetSuspendState) then
     Result := _SetSuspendState(Hibernate, ForceCritical, DisableWakeEvent)
  else
     Result := False;
end;
function LinkAPI(const module, functionname: string): Pointer;
var
   hLib: HMODULE;
begin
     hLib := GetModuleHandle(PChar(module));
     if hLib =0 then
        hLib := LoadLibrary(PChar(module));
     if hLib <> 0 then
        Result := getProcAddress(hLib, PChar(functionname))
     else
        Result := nil;
end;
procedure TForm1.btnSuspendClick(Sender: TObject);
begin
     SetSuspendState(True, False, False);
end;
like image 537
Alain Thiffault Avatar asked Oct 19 '22 19:10

Alain Thiffault


1 Answers

The problem was not related to delphi or the code in any way. The problem was created by a Windows 7 feature that enables more than magic packets when WOL is enabled. Forcing Windows to only listen for magic packets solved the problem.

MS Link: http://support.microsoft.com/kb/941145

Thank you to everyone who tried to help and especially to David Heffernan who alluded to the possibility that something else was waking up the PC.

like image 53
Alain Thiffault Avatar answered Oct 22 '22 23:10

Alain Thiffault