ホーム >プログラム >Delphi 6 ローテクTips

スクリーンセーバーの抑止

長時間の動画を見ている時に途中でスクリーンセーバーが立ち上がったりモニターの電源がオフになってしまったりすると気がそがれてしまいます。
ということでスクリーンセーバーおよびモニターの電源オフの抑止の方法を探しました。

参考サイト


モニター電源オフの抑止

Win98, Win2000以降でのみ有効なんだそうですが、SetThreadExecutionStateというAPIを使います。
が、このAPIはDelphi6では宣言されていないようなので自分で宣言して使います。

function SetThreadExecutionState(esFlags: DWORD): DWORD; stdcall external 'kernel32.dll';

const
  ES_SYSTEM_REQUIRED  = $00000001;
  ES_DISPLAY_REQUIRED = $00000002;
  ES_CONTINUOUS       = $80000000;

上のサイトをみると動画の再生開始時にSetThreadExecutionState(ES_DISPLAY_REQUIRED or ES_CONTINUOUS);として再生が終わったらSetThreadExecutionState(ES_CONTINUOUS);とすれば良さそうに思えたのですがだめでした。
タイマーを使って一定間隔でSetThreadExecutionState(ES_DISPLAY_REQUIRED);としなければなりませんでした。
というか、一定間隔でこのAPIを呼ぶとモニター電源オフの抑止だけでなくスクリーンセーバーも抑止されます。

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  SetThreadExecutionState(ES_DISPLAY_REQUIRED);
end;

タイマーを使って一定間隔でAPIを呼びますが、スクリーンセーバーの起動とモニター電源オフの間隔よりも小さくしなければなりません。

「画面のプロパティ」の「スクリーンセーバー」の設定や「電源オプションのプロパティ」の設定では最短間隔が1分になっているので30秒とか50秒間隔程度で通常は充分と思います。
ただしAPIを使うと最短間隔を1秒にすることもできるので、完璧を求めるなら0.9秒とか0.5秒間隔にする必要があるかと思います。

また下記のようにES_DISPLAY_REQUIREDとES_CONTINUOUSを一緒に使うと効き目がなくなります。
ES_DISPLAY_REQUIRED単独で使わないとなりません。

  SetThreadExecutionState(ES_DISPLAY_REQUIRED or ES_CONTINUOUS);  //NG

ES_CONTINUOUSは後述するシステムスタンバイの抑止のみに使います。

サンプルプログラム

unit Unit1;

interface

uses
  Windows, SysUtils, Forms, Classes, ExtCtrls, Controls, StdCtrls, ComCtrls;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function SetThreadExecutionState(esFlags: DWORD): DWORD; stdcall external 'kernel32.dll';

const
  ES_SYSTEM_REQUIRED  = $00000001;
  ES_DISPLAY_REQUIRED = $00000002;
  ES_CONTINUOUS       = $80000000;


procedure TForm1.Timer1Timer(Sender: TObject);
begin
  SetThreadExecutionState(ES_DISPLAY_REQUIRED);
end;

end.

起動してる間モニター電源オフとスクリーンセーバーの抑止を行います

ちなみにモニターの電源がオフになっているときにSetThreadExecutionState(ES_DISPLAY_REQUIRED);を呼ぶとモニターの電源が入ります。
けれどもスクリーンセーバーが立ち上がっているときにSetThreadExecutionState(ES_DISPLAY_REQUIRED);を呼んでもスクリーンセーバーは終了しません。
スクリーンセーバーに関しては立ち上がらないようにするだけです。

別の方法

タイマーを使ってSetThreadExecutionStateを呼ぶ以外に、メッセージを捕まえる方法もあるようです。
フォームにApplicationEventコンポーネントを貼り付け、オブジェクトインスペクタでOnMessageイベントをダブルクリックして下記のようにします。

procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
begin
  if (Msg.hwnd = Handle) and (Msg.message = WM_SYSCOMMAND) then begin
    if ((Msg.wParam and $FFF0) = SC_MONITORPOWER)
    or ((Msg.wParam and $FFF0) = SC_SCREENSAVE)
    then begin
      Handled := True;
      SetThreadExecutionState(ES_DISPLAY_REQUIRED);
    end;
  end;
end;

この場合SetThreadExecutionStateはなくても抑止されますがこれ以後約一秒間隔で同じメッセージが飛んできてしまいます。

Vista以降だと電源管理の挙動が変わったようなのでどうなのか分かりませんが、XPではこの方法でシステムスタンバイや休止状態も同時に抑止されるようです。
ただスクリーンセーバーやモニターの電源オフを「なし」の設定にしているとこのメッセージは飛んでこないので、この方法だけでシステムスタンバイや休止状態の抑止を済ませてしまうわけにはいきません。
またこのメッセージはフォームがアクティブな状態でないと飛んでこないので、なおさらこの方法だけで済ますことはできません。
まぁ、保険的な運用ということで。

システムスタンバイの抑止

システムスタンバイや休止状態になってしまうのを防ぐためには、

  SetThreadExecutionState(ES_SYSTEM_REQUIRED or ES_CONTINUOUS);

とします。

抑止を解除するには

  SetThreadExecutionState(ES_CONTINUOUS);

とします。


モニター電源オフの抑止とは違い、タイマーなどを使っての定期的な呼び出しを繰り返す必要はありません。
一度SetThreadExecutionState(ES_SYSTEM_REQUIRED or ES_CONTINUOUS);としておけば、それ以降SetThreadExecutionState(ES_CONTINUOUS);を呼び出すまでシステムスタンバイは抑止されます。
アプリケーションを起動している間スタンバイの抑止をしたいのであれば起動時のOnCreateイベントでSetThreadExecutionState(ES_SYSTEM_REQUIRED or ES_CONTINUOUS);としておいて終了時のOnDestroyイベントでSetThreadExecutionState(ES_CONTINUOUS);とすればよいということです。

ちなみにタイマーなどでSetThreadExecutionState(ES_SYSTEM_REQUIRED);を定期的に呼んでもOKでした。

※以前試したときにはだめで、そのようにも書いたのですが改めて試してみるとちゃんとシステムスタンバイは抑止されています。
どうやら何かをかん違いしていたようです。

注意点

スクリーンセーバーの抑止、モニターの電源オフの抑止、システムスタンバイの抑止を一緒に行おうとして

  SetThreadExecutionState(ES_SYSTEM_REQUIRED or ES_DISPLAY_REQUIRED or ES_CONTINUOUS);

というようにまとめてしまうと、システムスタンバイの抑止は効きますがスクリーンセーバーの抑止とモニターの電源オフの抑止は効きません。
タイマーを使って定期的に呼んでも結果は同じです。

以下のようにES_CONTINUOUSを外してタイマーを使って定期的に呼び出すと両方まとめて抑止されます。

  SetThreadExecutionState(ES_SYSTEM_REQUIRED or ES_DISPLAY_REQUIRED);

スクリーンセーバーおよびモニター電源オフの抑止にはES_CONTINUOUSを一緒に指定してはならないということのようです。

もしかしたらVista以降だとES_CONTINUOUSを併用してもOKなのかも知れませんが、少なくともXPではだめなようです。

参考プログラム

scv - スクリーンセーバー抑止プログラム。
上記のサンプルプログラムに手を加えてタスクトレイに常駐するようにしたプログラムです。
scvx - scvに付加機能をつけた版。
scvに、登録したプログラムが起動していたら抑止する機能を足したプログラムです。


2009-03-30:定期的にSetThreadExecutionState(ES_SYSTEM_REQUIRED);を呼んでもシステムスタンバイにならないとしていたのを再検証の結果できることを確認、訂正。
2009-03-29:SetThreadExecutionState(ES_DISPLAY_REQUIRED or ES_CONTINUOUS);とすると効き目がなくなることを追記。
2009-02-06:SetThreadExecutionState(ES_DISPLAY_REQUIRED)でモニターの電源は入ることを確認。
2008-11-19:システムスタンバイの抑止を追加。
2008-03-25