Alternative Sleep Methods

Post date: Mar 9, 2010 11:53:39 AM

It is possible for emulators to skip over sleep commands called using the Sleep API. Therefore, alternative methods prove useful in ensuring that your commands are not skipped; for application stability as well as security.

The advantages and disadvantages of each method are discussed.

1. Delay.

procedure Delay(dwMilliseconds: Longint);
var
  iStart, iStop: DWORD;
begin
  iStart := GetTickCount;
  repeat
    iStop := GetTickCount;
    Application.ProcessMessages;
    Sleep(1); // addition from Christian Scheffler to avoid high CPU last
  until (iStop - iStart) >= dwMilliseconds;
end;

2. Delay: with API.

procedure Delay(msecs: Longint);
var
  targettime: Longint;
  Msg: TMsg;
begin
  targettime := GetTickCount + msecs;
  while targettime > GetTickCount do
    if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
    begin
      if Msg.message = WM_QUIT then
      begin
        PostQuitMessage(Msg.wParam);
        Break;
      end;
      TranslateMessage(Msg);
      DispatchMessage(Msg);
    end;
end;
{
  Note:
  The elapsed time is stored as a DWORD value.
  Therefore, the time will wrap around to zero if the system is
  run continuously for 49.7 days.
}

3. Sleep - suspends the execution of the current thread for a specified interval.

Sleep(dwMilliseconds: Word);

4. Combined Delay

Including the Sleep in the loop prevents the app from hogging 100% of the CPU for doing practically nothing but running around the loop.

procedure PauseFunc(delay: DWORD);
var
  lTicks: DWORD;
begin
  lTicks := GetTickCount + delay;
  repeat
    Sleep(100);
    Application.ProcessMessages;
  until (lTicks <= GetTickCount) or Application.Terminated;
end;

5. Delay - more resource sparing.

procedure Delay(Milliseconds: Integer);
  {by Hagen Reddmann}
var
  Tick: DWORD;
  Event: THandle;
begin
  Event := CreateEvent(nil, False, False, nil);
  try
    Tick := GetTickCount + DWORD(Milliseconds);
    while (Milliseconds > 0) and
      (MsgWaitForMultipleObjects(1, Event, False, Milliseconds,
      QS_ALLINPUT) <> WAIT_TIMEOUT) do
    begin
      Application.ProcessMessages;
      Milliseconds := Tick - GetTickCount;
    end;
  finally
    CloseHandle(Event);
  end;
end;