Jump to content
dummzeuch

Blocking the Windows Screen Saver in Delphi

Recommended Posts

Sometimes your program needs to block the screen saver from automatically kicking in. My use case was that the program was recording data and whenever the screen saver was active, the data was lost (No idea why, it probably had something to do with the way HID is implemented in Windows.)
So I was looking for a way to fix that without forcing the user to turn off the screen saver. The methods that used to work under Windows XP no longer work in Windows 7 and later (I don’t care about Vista), so I googled and found this question on StackOverflow. The Windows API functions PowerCreateRequest + PowerSetRequest mentioned in the highest voted answer looked promising. Unfortunately they don’t seem bo be available in Delphi

read on in my blog post

 

  • Like 3
  • Thanks 1

Share this post


Link to post

The code is now in dzlib (see blog post for links).

  • Like 1
  • Thanks 1

Share this post


Link to post

Our laptops are under company governance, so we can't prevent the session from locking as it is dictated by the policies.  In addition, the idle time is really short.

So - we wrote LockBlock which basically simulates toggling the numlock every 60 seconds.  That prevented the auto locking, but it also prevented the screensaver from kicking in when the dektkop was locked, so I added handling of lock/unlock to disable the fake keystrokes when it was locked.

Super simple UI: 60 second interval, starting at 8 in the morning, lasting for 8 hours - with an add an extra hour button for off work hours lock blocking.

 

image.thumb.png.8d81afd5e92eefe56b17e83172bbe364.png

 

Project source attached.  

LockBlock.zip

  • Like 1

Share this post


Link to post

I used a similar hack in an earlier version of my program: Wiggling the mouse pointer. While that worked fine under Windows XP, it doesn't under Windows 10 (no idea about Windows 7 and 8). That was the reason I researched how to do it "right".

 

Toggling numlock all the time would drive me crazy.

Share this post


Link to post

I use this function for this:

https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-setthreadexecutionstate

Share this post


Link to post
2 hours ago, Lykke said:

setthreadexecutionstate

Yes, that is what I used but it doesn't take care of the screen saver.
Thanks to this blog post I updated to use PowerCfg when (OS>=Win7).

 

Share this post


Link to post
On 6/14/2019 at 5:09 PM, dummzeuch said:

I used a similar hack in an earlier version of my program: Wiggling the mouse pointer. While that worked fine under Windows XP, it doesn't under Windows 10 (no idea about Windows 7 and 8). That was the reason I researched how to do it "right".

 

Toggling numlock all the time would drive me crazy.

It turns NumLock off/on/off (or on/off/on) within less than 30 ms every 60 seconds, so I don't think it will bother you much. 

Share this post


Link to post
12 minutes ago, Lars Fosdal said:

NumLock off/on/off

Sounds like an 'Awesome' feature while entering financial data into a spreadsheet :classic_laugh:

Share this post


Link to post

This is a bit old, but I've been blocking the windows screen-saver for media playback in every version of windows since 1999.

 

I'm actually handling several actions, first I capture the WM_POWERBROADCAST message in the form:

procedure WMPOWERBROADCAST(var M : TMessage); message WM_POWERBROADCAST;
const
  PBT_APMQUERYSUSPEND       = $0000;
  PBT_APMQUERYSTANDBY       = $0001;
  PBT_APMQUERYSUSPENDFAILED = $0002;
  PBT_APMQUERYSTANDBYFAILED = $0003;
  PBT_APMSUSPEND            = $0004;
  PBT_APMSTANDBY            = $0005;
  PBT_APMRESUMECRITICAL     = $0006;
  PBT_APMRESUMESUSPEND      = $0007;
  PBT_APMRESUMESTANDBY      = $0008;
  PBT_APMBATTERYLOW         = $0009;
  PBT_APMPOWERSTATUSCHANGE  = $000A;
  PBT_APMOEMEVENT           = $000B;
  PBT_APMRESUMEAUTOMATIC    = $0012;
begin
  Inherited;
  Case M.WParam of
    PBT_APMQUERYSUSPEND,
    PBT_APMSUSPEND       :
    Begin
      If SystemSuspended = False then
      Begin
        SystemSuspended   := True;
      End;
    End;
    PBT_APMRESUMEAUTOMATIC :
    Begin
      SystemSuspended := False;
    End;
  End;
  M.Result := Integer(True);
end;

 

Then on WinMsg (Application.OnMessage := WinMsg;) I do:

procedure TMainForm.WinMsg(var Msg: TMsg; var Handled: Boolean);
var
  ModWnd : HWnd;
  I      : Integer;
  mPos   : TPoint;
begin
  If (Msg.Message = SC_SCREENSAVE) or (Msg.Message = SC_MONITORPOWER) then
  Begin
      Msg.wParam  := 0;
      Msg.Message := SC_Move;
      Handled     := True;
  End
    else
  If (Msg.Message = WM_SYSCOMMAND) then
  Begin
    Case Msg.WPARAM of
      SC_MONITORPOWER,
      SC_SCREENSAVE    :
      Begin
          Msg.wParam  := 0;
          Msg.Message := SC_Move;
          Handled     := True;
      End;
    End;
  End;
End;

 

Then on WndProc ( procedure WndProc(var Msg : TMessage); override;) :

Quote

 

procedure TMainForm.WndProc(var Msg : TMessage);

begin

  If Msg.Msg = WM_SYSCOMMAND then
  Begin
    {$IFDEF LOCAL}
    //If Msg.WParam and $FFF0 = SC_MAXIMIZE then ShowMessage('maximized');
    {$ENDIF}
    If ((OPOTScreenSaver = True) and (plState <> stClosed) and (MainForm.WindowState = wsMaximized)) or (OPScreenSaverAW = True) then
    Begin
      Case Msg.WParam and $FFF0 of
        SC_SCREENSAVE,
        SC_MONITORPOWER :
        Begin
          Msg.Result := 0;
        End;
        else inherited WndProc(Msg);
      End;
    End
    Else inherited WndProc(Msg);
  End
    else
  If (Msg.Msg = SC_SCREENSAVE) or (Msg.Msg = SC_MONITORPOWER) then

  Begin

    Msg.Result := 0;

  End
  Else inherited WndProc(Msg);
End;

 

 

 

 

Hope this helps.

Edited by Yaron

Share this post


Link to post
10 hours ago, Yaron said:

This is a bit old, but I've been blocking the windows screen-saver for media playback in every version of windows since 1999.

The *correct* way to block the screen saver on XP and later is to use SetThreadExecutionState() with the ES_DISPLAY_REQUIRED flag:

Quote

Multimedia applications, such as video players and presentation applications, must use ES_DISPLAY_REQUIRED when they display video for long periods of time without user input.

Quote

I'm actually handling several actions, first I capture the WM_POWERBROADCAST message in the form:

PBT_APMQUERYSUSPEND is just a *request for permission* to suspend the system.  The system is not actually suspended yet.  So you should not be setting your SystemSuspended flag to True in reply to PBT_APMQUERYSUSPEND, only for PBT_APMSUSPEND.  And then clearing the Flag in reply to PBT_APMRESUMESUSPEND and PBT_APMRESUMECRITICAL, not just for PBT_APMRESUMEAUTOMATIC.

Quote

Then on WinMsg (Application.OnMessage := WinMsg;) I do:

SC_SCREENSAVE and SC_MONITORPOWER are not window messages, they are flags of WM_SYSCOMMAND, so your 1st "If" block will never evaluate as True.

Quote

Then on WndProc ( procedure WndProc(var Msg : TMessage); override;) :

Same with the last "If" block here, too.

 

Also, per the WM_SYSCOMMAND documentation:

Quote

If password protection is enabled by policy, the screen saver is started regardless of what an application does with the SC_SCREENSAVE notification even if fails to pass it to DefWindowProc.

 

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post
11 hours ago, Remy Lebeau said:

The *correct* way to block the screen saver on XP and later is to use SetThreadExecutionState() with the ES_DISPLAY_REQUIRED flag:

Hmm..

 

and starting with W7 you have to use the

 

function PowerCreateRequest(REASON_CONTEXT: TREASON_CONTEXT): THandle; stdcall; external 'kernel32.dll' name 'PowerCreateRequest';
function PowerSetRequest(PowerRequestHandle: THandle; PowerRequestType : TPowerRequestType): BOOL; stdcall; external 'kernel32.dll' name 'PowerSetRequest';
function PowerClearRequest(PowerRequestHandle: THandle; PowerRequestType : TPowerRequestType): BOOL; stdcall; external 'kernel32.dll' name 'PowerClearRequest';

 

Share this post


Link to post
14 hours ago, HolgerX said:

and starting with W7 you have to use the


function PowerCreateRequest(REASON_CONTEXT: TREASON_CONTEXT): THandle; stdcall; external 'kernel32.dll' name 'PowerCreateRequest';
function PowerSetRequest(PowerRequestHandle: THandle; PowerRequestType : TPowerRequestType): BOOL; stdcall; external 'kernel32.dll' name 'PowerSetRequest';
function PowerClearRequest(PowerRequestHandle: THandle; PowerRequestType : TPowerRequestType): BOOL; stdcall; external 'kernel32.dll' name 'PowerClearRequest';

If you do that, you will need to use the 'delayed' keyword on those function declarations if you want your app to run on pre-W7 systems.  Then you can use the functions on W7+ only, and fall back to SetThreadExecutionState() on pre-W7 systems.

 

If you really wanted to be slick, you could then use SetDliFailureHook2() to detect when the Power...() functions are not available at runtime and re-map them to custom functions that allocate a memory object and pass its details to SetThreadExecutionState() as needed.  Then your code could call the Power...() functions unconditionally on all Windows versions.

Edited by Remy Lebeau

Share this post


Link to post

Right,

 

I just wanted to give the declaration as a hint. 😉

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×