2026/01/25(日) [n年前の日記]
#1 [delphi] Delphiでスクリーンセーバを作りたい
Delphi 12 CE(Community Edition)で、Windows用のスクリーンセーバを作りたい。
C#で作るチュートリアル記事を参考にして試してみたので、手順をメモしておく。環境はWindows11 x64 25H2。
_Creating a Screen Saver with C#
かなり長くなってしまったけれど、一応これでスクリーンセーバは作れるはず…。
C#で作るチュートリアル記事を参考にして試してみたので、手順をメモしておく。環境はWindows11 x64 25H2。
_Creating a Screen Saver with C#
かなり長くなってしまったけれど、一応これでスクリーンセーバは作れるはず…。
◎ プロジェクトの新規作成 :
まずはプロジェクトを新規作成。Windows VCL アプリケーション、を選んで新規作成する。
作成されたら、早々にプロジェクトを保存しておく。ファイル → プロジェクトに名前をつけて保存、を選ぶ。
ちなみに、Delphi IDE には以下のショートカットキーが割り当てられている。
作成されたら、早々にプロジェクトを保存しておく。ファイル → プロジェクトに名前をつけて保存、を選ぶ。
- プロジェクト名は…「SSDelphiTest1」とでもしておこう…。「SS」は ScreenSaver の略。
- 最初に .pas を保存して、次に .dproj を保存するけれど、ここで SSDelphiTest1.dproj としておけばプロジェクト名にも反映される。
ちなみに、Delphi IDE には以下のショートカットキーが割り当てられている。
- Ctrl + S : 現在開いているソースコードを保存。
- Ctrl + Shift + S : プロジェクト全体を保存。
◎ フルスクリーン表示用フォームを作成 :
プロジェクトを新規作成した直後はフォームが1つ置かれてる状態になっている。このフォームを、フルスクリーン表示(全画面表示)用のフォームとして利用してしまおう…。
以下のような見た目のフォームを作っていく。
フルスクリーン表示をしてしまうとタイトルバーも消えるので、閉じるボタンが押せなくなる…。だから真っ先に、終了させるための処理を書いておく。
フォームを選択した状態で、オブジェクトインスペクタの「イベント」をクリックすると、利用できるイベントが一覧で表示されているので…。以下のイベント名の右側の入力欄(?)を順次ダブルクリックしていく。
それぞれをダブルクリックすると、イベントが発生した時に呼ばれるプロシージャ(メソッドだのサブルーチンに相当)がソースコード内に自動で作成されるので、その中にアプリを終了させるための1行 ―― Application.Terminate; を記述する。
この状態で F9キーを叩いて、ビルドと実行をしてみる。ウインドウ(フォーム)が1つ表示される。ウインドウをマウスクリックするか、何かのキーを押せば、終了できることが分かる。
実はこの終了のさせ方はよろしくないという話もあって…。動画再生ソフトの類が動いてたりすると、この方法では終了できなくなるとかなんとか…。
_Windows 令和のスクリーンセーバーの作り方 - Qiita
でもまあ、今回はとりあえずそれっぽく動けば、という方針で進めてしまおう…。
フォームのタイトルバーを消去する。プロパティの BorderStyle が bsSizeable になっているので、bsNone に変更。タイトルバーが消えてくれる。
フォームがデスクトップに表示された際、最前面表示になってほしい。FormStyle が fsNormal になっているので、fsStayOnTop に変更。
フォームの背景色を変えたい。Color を clBlack にすれば、真っ黒なフォームになる。
フォーム上に文字を表示したい。パレットから TLabel を選んでフォームに配置。パレットの検索欄で「label」と打ち込めば TLabel がリストアップされるので、フォーム上にドラッグアンドドロップしてやれば配置できる。好きな位置に配置。
この状態では TLabel の文字色が黒なので、背景色の黒と同じだから何が描かれてるのかわからない。TLable のプロパティの Font を変更して、文字色を白にする。ついでに、文字の種類や文字サイズも変更しておこう…。
表示する文字列は、TLabel のプロパティの Caption で指定できる。なんでもいいけど、「ScreenSaver by Delphi」とでも入力しておこう…。
この状態でF9キーを叩いて実行。タイトルバーの無いウインドウが表示された。クリックするかキーを押せば終了できる。
フルスクリーン表示にしたい。フォームが作成された直後に OnCreate イベントが発生するらしいので、そこでフルスクリーン表示になるように設定してしまおう。フォームを選択して、イベントで OnCreate をダブルクリック。以下のような1行を書く。
Delphi でGUIアプリを作る際は、「BorderStyle が bsNone」の状態で、「WindowState に wsMaximized (最大化)」を指定すれば、Windowsのタスクバー部分も覆い隠す感じの全画面表示になってくれるらしい。
あるいは、以下のような記述もできる。
ちなみに、この指定では、マルチディスプレイ環境上ではよろしくないらしい…。メインディスプレイ上だけでフルスクリーン表示になってしまうそうで…。自分はマルチディスプレイ環境を持ってないから確認のしようがないけれど。
一定時間毎に見た目がちょっと変化するようにしたい。とりあえず、ラベルの表示位置でも変えてみよう…。
TTimer を使えば、一定時間毎に何かしらの処理をさせられる。パレットから TTimer を選んで、フォーム上にドラッグアンドドロップして貼り付ける。貼り付ける位置はどこでもいい。
TTimer のプロパティの Interval で、呼び出す時間間隔を指定できる。単位はミリ秒。デフォルトでは 1000 が入っているので、1000ミリ秒 = 1秒毎に処理が呼ばれる。
TTimer のイベントで、OnTimer をダブルクリック。ここで自動作成されるプロシージャが、一定時間毎に呼び出される処理になる。今回は、ラベルの表示位置をランダムに変更する処理を入れてみる。
Random() を使うためにはランダムシードを設定しないといけない。フォームが Create された時に Randomize を呼んでおく。
F9キーを押して実行。1秒毎にラベルの位置が変わる状態になった。
忘れてた。マウスカーソルを消さないといかんよな…。FormCreate にマウスカーソルを非表示にする一行を追加。
これでかなり出来上がってきたけど、せっかくだからマウスカーソルを動かしたら終了するようにしておこう…。
((dx * dx) + (dy * dy)) > (DIST * DIST) のあたりは三平方の定理(ピタゴラスの定理) を使ってる。
_ピタゴラスの定理 - Wikipedia
三平方の定理を使うと、値がプラスかマイナスかを気にせずに掛け算だけで判定ができるから楽なのです…。
これで、マウスを動かしても終了できるようになった。スクリーンセーバのフルスクリーン表示モードは大体出来たかなと…。
以下のような見た目のフォームを作っていく。
フルスクリーン表示をしてしまうとタイトルバーも消えるので、閉じるボタンが押せなくなる…。だから真っ先に、終了させるための処理を書いておく。
フォームを選択した状態で、オブジェクトインスペクタの「イベント」をクリックすると、利用できるイベントが一覧で表示されているので…。以下のイベント名の右側の入力欄(?)を順次ダブルクリックしていく。
- OnClick (マウスクリック)
- OnKeyDown (キーの押し下げ)
- OnMouseDown (マウスボタンクリック)
それぞれをダブルクリックすると、イベントが発生した時に呼ばれるプロシージャ(メソッドだのサブルーチンに相当)がソースコード内に自動で作成されるので、その中にアプリを終了させるための1行 ―― Application.Terminate; を記述する。
procedure TForm1.FormClick(Sender: TObject);
begin
Application.Terminate;
end;
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift:
TShiftState);
begin
Application.Terminate;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift:
TShiftState; X, Y: Integer);
begin
Application.Terminate;
end;
この状態で F9キーを叩いて、ビルドと実行をしてみる。ウインドウ(フォーム)が1つ表示される。ウインドウをマウスクリックするか、何かのキーを押せば、終了できることが分かる。
実はこの終了のさせ方はよろしくないという話もあって…。動画再生ソフトの類が動いてたりすると、この方法では終了できなくなるとかなんとか…。
_Windows 令和のスクリーンセーバーの作り方 - Qiita
でもまあ、今回はとりあえずそれっぽく動けば、という方針で進めてしまおう…。
フォームのタイトルバーを消去する。プロパティの BorderStyle が bsSizeable になっているので、bsNone に変更。タイトルバーが消えてくれる。
フォームがデスクトップに表示された際、最前面表示になってほしい。FormStyle が fsNormal になっているので、fsStayOnTop に変更。
フォームの背景色を変えたい。Color を clBlack にすれば、真っ黒なフォームになる。
フォーム上に文字を表示したい。パレットから TLabel を選んでフォームに配置。パレットの検索欄で「label」と打ち込めば TLabel がリストアップされるので、フォーム上にドラッグアンドドロップしてやれば配置できる。好きな位置に配置。
この状態では TLabel の文字色が黒なので、背景色の黒と同じだから何が描かれてるのかわからない。TLable のプロパティの Font を変更して、文字色を白にする。ついでに、文字の種類や文字サイズも変更しておこう…。
表示する文字列は、TLabel のプロパティの Caption で指定できる。なんでもいいけど、「ScreenSaver by Delphi」とでも入力しておこう…。
この状態でF9キーを叩いて実行。タイトルバーの無いウインドウが表示された。クリックするかキーを押せば終了できる。
フルスクリーン表示にしたい。フォームが作成された直後に OnCreate イベントが発生するらしいので、そこでフルスクリーン表示になるように設定してしまおう。フォームを選択して、イベントで OnCreate をダブルクリック。以下のような1行を書く。
procedure TForm1.FormCreate(Sender: TObject); begin WindowState := TWindowState.wsMaximized; end;
Delphi でGUIアプリを作る際は、「BorderStyle が bsNone」の状態で、「WindowState に wsMaximized (最大化)」を指定すれば、Windowsのタスクバー部分も覆い隠す感じの全画面表示になってくれるらしい。
あるいは、以下のような記述もできる。
procedure TForm1.FormCreate(Sender: TObject); begin Left := 0; Top := 0; Width := Screen.Width; Height := Screen.Height; end;
- Left と Top がフォームの表示位置 x,y なので、(0, 0) にすれば表示位置をデスクトップ画面の左上にできる。
- Width と Height がフォームの横幅と縦幅なので、デスクトップ画面の横幅と縦幅 ―― Screen.Width と Screen.Height を指定すれば、デスクトップ画面と同じサイズのフォームになる。
ちなみに、この指定では、マルチディスプレイ環境上ではよろしくないらしい…。メインディスプレイ上だけでフルスクリーン表示になってしまうそうで…。自分はマルチディスプレイ環境を持ってないから確認のしようがないけれど。
一定時間毎に見た目がちょっと変化するようにしたい。とりあえず、ラベルの表示位置でも変えてみよう…。
TTimer を使えば、一定時間毎に何かしらの処理をさせられる。パレットから TTimer を選んで、フォーム上にドラッグアンドドロップして貼り付ける。貼り付ける位置はどこでもいい。
TTimer のプロパティの Interval で、呼び出す時間間隔を指定できる。単位はミリ秒。デフォルトでは 1000 が入っているので、1000ミリ秒 = 1秒毎に処理が呼ばれる。
TTimer のイベントで、OnTimer をダブルクリック。ここで自動作成されるプロシージャが、一定時間毎に呼び出される処理になる。今回は、ラベルの表示位置をランダムに変更する処理を入れてみる。
procedure TForm1.Timer1Timer(Sender: TObject); begin Label1.Left := Random(ClientWidth - Label1.Width); Label1.Top := Random(ClientHeight - Label1.Height); end;
- Random(N) で、0 - (N-1) までの乱数が生成される。
- ClientWidth, ClientHeight で、フォームのクライアント領域の横幅・縦幅が取得できる。
- Label1.Width, Label1.Height で、ラベルの横幅・縦幅を取得できる。
Random() を使うためにはランダムシードを設定しないといけない。フォームが Create された時に Randomize を呼んでおく。
procedure TForm1.FormCreate(Sender: TObject); begin WindowState := TWindowState.wsMaximized; Randomize; end;
F9キーを押して実行。1秒毎にラベルの位置が変わる状態になった。
忘れてた。マウスカーソルを消さないといかんよな…。FormCreate にマウスカーソルを非表示にする一行を追加。
procedure TForm1.FormCreate(Sender: TObject); begin WindowState := TWindowState.wsMaximized; ShowCursor(False); // マウスカーソル非表示 Randomize; end;
これでかなり出来上がってきたけど、せっかくだからマウスカーソルを動かしたら終了するようにしておこう…。
- マウスカーソルが動くと OnMouseMove イベントが発生するので、プロシージャを作成。
- 前回のマウスカーソル位置を覚えておかないとどのくらいマウスカーソルが動いたか計算できないので、TForm1クラス内に private変数も用意しておく。
- その private変数は、FormCreate の中で初期化しておく。
- インライン関数 Point を使うので、uses のあたりに、System.Types を追加しておく。
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Types, // これを追加 System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
...
private
{ Private 宣言 }
FOldMousePos: TPoint; // マウスの旧座標を記録する変数
public
{ Public 宣言 }
end;
procedure TForm1.FormCreate(Sender: TObject); begin WindowState := TWindowState.wsMaximized; Randomize; ShowCursor(False); FOldMousePos := Point(-1, -1); // マウス旧座標記録用変数を初期化 end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
var
dx, dy: Integer;
const
DIST: Integer = 32;
begin
if FOldMousePos.X <> -1 then
begin
dx := FOldMousePos.X - X;
dy := FOldMousePos.Y - Y;
if ((dx * dx) + (dy * dy)) > (DIST * DIST) then
Application.Terminate;
end;
FOldMousePos := Point(X, Y);
end;
((dx * dx) + (dy * dy)) > (DIST * DIST) のあたりは三平方の定理(ピタゴラスの定理) を使ってる。
_ピタゴラスの定理 - Wikipedia
三平方の定理を使うと、値がプラスかマイナスかを気にせずに掛け算だけで判定ができるから楽なのです…。
これで、マウスを動かしても終了できるようになった。スクリーンセーバのフルスクリーン表示モードは大体出来たかなと…。
◎ 設定画面用フォームを作成 :
次に、スクリーンセーバの設定画面を作る。
今回はスクリーンセーバ名を表示して、OKボタンをクリックしたら終了するだけの処理にする。
ファイル → 新規作成 → VCLフォーム。Unit2.pas が新規作成されて、フォームが一つ表示された。
TButton をダブルクリックすれば、ボタンをクリックした時に呼ばれるプロシージャが自動作成される。アプリを終了するための一行を追加。
しかし、この状態でF9キーを押して動作確認しようとしても、先ほど作成したフルスクリーン表示用のフォームまで表示されてしまう。今回作成したフォームだけを表示するようにしたい。
プロジェクト → ソースを表示。もしくは、プロジェクトのファイル群がツリー表示されてるウインドウで、プロジェクト名を右クリック → ソースの表示。.dpr ファイル (SSDelphiTest1.dpr) が表示される。
本来なら、このソースコード内でコマンドラインオプションの解析をして、フルスクリーン表示、設定画面、プレビュー画面の処理に振り分けないといかんのだけど…。とりあえず、まずは各フォームの表示確認だけをしておく。
Application.CreateForm() で TForm1 と TForm2 の2種類が生成されているので、TForm1 の行はコメントアウトしておく。該当行で Ctrl + / を叩けばコメントアウトができる。もう一度 Ctrl + / を叩けばコメント解除になる。
F9キーを叩けば、設定画面用フォームだけが表示される状態になる。OKボタンをクリックして終了するか確認。
これで設定画面用フォームも作成できた。
今回はスクリーンセーバ名を表示して、OKボタンをクリックしたら終了するだけの処理にする。
ファイル → 新規作成 → VCLフォーム。Unit2.pas が新規作成されて、フォームが一つ表示された。
- TLabel を貼り付けて、Caption にスクリーンセーバ名を入力。Font を設定して好みの見え方を指定。
- TButton も貼り付けて、Caption は「OK」にする。
TButton をダブルクリックすれば、ボタンをクリックした時に呼ばれるプロシージャが自動作成される。アプリを終了するための一行を追加。
procedure TForm2.Button1Click(Sender: TObject); begin Application.Terminate; end;
しかし、この状態でF9キーを押して動作確認しようとしても、先ほど作成したフルスクリーン表示用のフォームまで表示されてしまう。今回作成したフォームだけを表示するようにしたい。
プロジェクト → ソースを表示。もしくは、プロジェクトのファイル群がツリー表示されてるウインドウで、プロジェクト名を右クリック → ソースの表示。.dpr ファイル (SSDelphiTest1.dpr) が表示される。
本来なら、このソースコード内でコマンドラインオプションの解析をして、フルスクリーン表示、設定画面、プレビュー画面の処理に振り分けないといかんのだけど…。とりあえず、まずは各フォームの表示確認だけをしておく。
Application.CreateForm() で TForm1 と TForm2 の2種類が生成されているので、TForm1 の行はコメントアウトしておく。該当行で Ctrl + / を叩けばコメントアウトができる。もう一度 Ctrl + / を叩けばコメント解除になる。
begin Application.Initialize; Application.MainFormOnTaskbar := True; // Application.CreateForm(TForm1, Form1); Application.CreateForm(TForm2, Form2); Application.Run; end.
F9キーを叩けば、設定画面用フォームだけが表示される状態になる。OKボタンをクリックして終了するか確認。
これで設定画面用フォームも作成できた。
◎ プレビュー画面用フォームを作成 :
Windowsの「スクリーンセーバーの変更」ウインドウでは、スクリーンセーバのプレビュー画面が小さく表示される。本来ならスクリーンセーバの実処理をそこに表示しないといかんのだろうけど…。今回はスクリーンセーバ名を表示するだけにしておく。
設定画面用フォームの時と同様に、フォームをもう1つ追加する。ファイル → 新規作成 → VCLフォーム。Unit3.pas が追加されて、フォームも追加される。
このままだと、Delphi IDE上で動作確認した際、終了させる方法が無い。一応仮で、OnKeyDown イベントにプロシージャを割り当てて、ESCキーが押されていたら終了するようにしておく。本番では削除してしまっていいけれど…。フツーはESCキーを押す人も居ないだろうからそのまま放置でもいいのかも…。
プロジェクトのソースを表示して、プレビュー画面用フォームだけが生成されるようにしておく。
F9キーを押して実行。それらしく表示されるか確認。ESCキーを押せば終了する。
これでプレビュー画面用フォームも作成できた。
設定画面用フォームの時と同様に、フォームをもう1つ追加する。ファイル → 新規作成 → VCLフォーム。Unit3.pas が追加されて、フォームも追加される。
- BorderStyle を bsNone にして、タイトルバーを消去。
- Width と Height を、152 x 112 にする。「スクリーンセーバーの変更」ウインドウ内のプレビュー画面表示枠はそのサイズなので…。
- TLabel を配置して、Caption をスクリーンセーバ名にする。
- フォームの色(Color) や、TLabel の Font を変更して、背景色や文字色を指定する。
このままだと、Delphi IDE上で動作確認した際、終了させる方法が無い。一応仮で、OnKeyDown イベントにプロシージャを割り当てて、ESCキーが押されていたら終了するようにしておく。本番では削除してしまっていいけれど…。フツーはESCキーを押す人も居ないだろうからそのまま放置でもいいのかも…。
procedure TForm3.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_ESCAPE then
Application.Terminate;
end;
プロジェクトのソースを表示して、プレビュー画面用フォームだけが生成されるようにしておく。
begin Application.Initialize; Application.MainFormOnTaskbar := True; // Application.CreateForm(TForm1, Form1); // Application.CreateForm(TForm2, Form2); Application.CreateForm(TForm3, Form3); Application.Run; end.
F9キーを押して実行。それらしく表示されるか確認。ESCキーを押せば終了する。
これでプレビュー画面用フォームも作成できた。
◎ コマンドライン引数の解析 :
ここまでの作業で、フルスクリーン表示用、設定画面用、プレビュー画面用 ―― 必要なフォームは全部作成した。
後は、コマンドラインオプション(コマンドライン引数)に基づいて処理を振り分ければスクリーンセーバになるはず。
Windows用のスクリーンセーバは、以下のコマンドライン引数が渡される可能性がある。
コマンドライン引数を取得する方法だけど、Delphi の場合、ParamCount と ParamStr にコマンドライン引数の情報が入っている。
更に、Delphi の .dpr の中では、以下のような処理をしているので…。
そんなわけで、コマンドライン引数に応じて、Application.CreateForm() で発生させるフォームの種類を、フルスクリーン表示用、設定画面用、プレビュー画面用のどれかしらにしてやればスクリーンセーバになってくれるはず。
そんな感じで、.dpr はこうなった。
ビルドして出来上がった .exe の動作確認をするために、コマンドライン引数を指定した状態で実行したい。プロジェクトのオプション設定で指定できる。
パラメータに、「/s」「/c」「/c:123456」「/p」「/p 0」を指定してプロジェクトオプションを保存してから、F9キーを叩いて実行したら、コマンドライン引数に対応したフォームが表示された。
これで完成! ではない…。
フルスクリーン表示モードは、スクリーンセーバの起動設定時間が来るたびに何度も起動されてしまう可能性があるらしいので、既に起動していたらまた起動されないように ―― いわゆる多重起動禁止処理を入れないといけない。
Delphi で多重起動禁止をしたいなら、Windowsの Mutex (CreateMutex()) を使うのが簡単らしい。uses に Winapi.Windows を追加すれば使えるようになる。
そんな感じで、こうなった。フルスクリーン表示モードのところに多重起動禁止処理を入れている。
他のモードは多重起動禁止処理を入れなくていいのだろうか?
入れないほうがいいらしい。というのも、設定画面やプレビュー画面を表示している状態でも、時間が来たらスクリーンセーバを起動させないといけないので…。
後は、コマンドラインオプション(コマンドライン引数)に基づいて処理を振り分ければスクリーンセーバになるはず。
Windows用のスクリーンセーバは、以下のコマンドライン引数が渡される可能性がある。
- /s ... フルスクリーン表示。
- /c:HWND ... 設定画面表示。HWNDはウインドウハンドル。「スクリーンセーバーの変更」で「設定」を押された時はこの指定になる。らしい。
- /c ... 設定画面表示。
- /p HWND ... プレビュー画面表示。HWNDはウインドウハンドル。与えられたウインドウハンドルを親としたウインドウを作って、そこにプレビュー画面を表示しないといけない。
- コマンドラインオプション無し ... 設定画面表示。.scr を右クリックして「構成」を選ぶとこの指定になる。らしい。
コマンドライン引数を取得する方法だけど、Delphi の場合、ParamCount と ParamStr にコマンドライン引数の情報が入っている。
- ParamCount : コマンドライン引数の数。コマンドライン引数が何も指定されてなかったら 0 になる。
- 配列 ParamStr : コマンドライン引数の文字列。ParamStr(0) は実行ファイル名。ParamStr(1) 以降はコマンドライン引数の文字列。空白文字で区切られて配列に入ってる。
更に、Delphi の .dpr の中では、以下のような処理をしているので…。
- Application.CreateForm() で何かしらのフォームを作成して、
- Application.Run で各フォームのメインループが回り始めて、
- フォーム側の処理で Application.Terminate が呼ばれたら、メインループ相当の Application.Run を抜けて、次の処理に進んでいく。
そんなわけで、コマンドライン引数に応じて、Application.CreateForm() で発生させるフォームの種類を、フルスクリーン表示用、設定画面用、プレビュー画面用のどれかしらにしてやればスクリーンセーバになってくれるはず。
そんな感じで、.dpr はこうなった。
program SSDelphiTest1;
uses
Vcl.Forms,
Unit1 in 'Unit1.pas' {Form1} ,
Unit2 in 'Unit2.pas' {Form2} ,
Unit3 in 'Unit3.pas' {Form3} ,
System.SysUtils,
System.StrUtils;
{$R *.res}
var
arg: string;
hwnd: Int64;
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
// コマンドライン第1引数を '/s', '/c', '/p' の状態にする
if ParamCount > 0 then
arg := ParamStr(1).Substring(0, 2).ToLower
else
arg := '';
if arg = '/s' then
begin
// フルスクリーン表示
Application.CreateForm(TForm1, Form1);
Application.Run;
Exit; // Exit は他言語での Return みたいなもの
end;
if arg = '/c' then
begin
// 設定画面表示
Application.CreateForm(TForm2, Form2);
Application.Run;
Exit;
end;
if arg = '/p' then
begin
// プレビュー画面表示
if ParamCount >= 2 then
hwnd := StrToInt64Def(ParamStr(2), 0)
else
hwnd := 0;
Application.CreateForm(TForm3, Form3);
if hwnd <> 0 then
Form3.ParentWindow := hwnd;
Application.Run;
Exit;
end;
// 設定画面表示
Application.CreateForm(TForm2, Form2);
Application.Run;
end.
- 文字列の一部を切り出す Substring() や 小文字化する ToLower を使うために、uses に System.SysUtils, System.StrUtils を追加している。
- StrToInt64Def(ParamStr(2), 0) は、文字列型から Int64型(数値)への変換をする。Def がついていると、変換に失敗した時は第2引数の値を返してくれるらしい。
- フォームの親ウインドウを変更する時は、.ParentWindow に親ウインドウのウインドウハンドルを指定してやればいいらしい…? ちょっと自信が無い。
ビルドして出来上がった .exe の動作確認をするために、コマンドライン引数を指定した状態で実行したい。プロジェクトのオプション設定で指定できる。
- Ctrl + Shift + F11キーを押すか、もしくは、プロジェクト → オプション、でプロジェクトのオプション設定ウインドウが開く。
- デバッガ → パラメータ、でコマンドライン引数を指定する。
パラメータに、「/s」「/c」「/c:123456」「/p」「/p 0」を指定してプロジェクトオプションを保存してから、F9キーを叩いて実行したら、コマンドライン引数に対応したフォームが表示された。
これで完成! ではない…。
フルスクリーン表示モードは、スクリーンセーバの起動設定時間が来るたびに何度も起動されてしまう可能性があるらしいので、既に起動していたらまた起動されないように ―― いわゆる多重起動禁止処理を入れないといけない。
Delphi で多重起動禁止をしたいなら、Windowsの Mutex (CreateMutex()) を使うのが簡単らしい。uses に Winapi.Windows を追加すれば使えるようになる。
そんな感じで、こうなった。フルスクリーン表示モードのところに多重起動禁止処理を入れている。
program SSDelphiTest1;
uses
Vcl.Forms,
Unit1 in 'Unit1.pas' {Form1} ,
Unit2 in 'Unit2.pas' {Form2} ,
Unit3 in 'Unit3.pas' {Form3} ,
System.SysUtils,
System.StrUtils,
Winapi.Windows;
{$R *.res}
var
arg: string;
hwnd: Int64;
hMutex: THandle;
const
MUTEXNAME: string = 'SSDelphiTest1Mutex5963'; // 他と被らない文字列にする
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
// コマンドライン第1引数を '/s', '/c', '/p' の状態にする
if ParamCount > 0 then
arg := ParamStr(1).Substring(0, 2).ToLower
else
arg := '';
if arg = '/s' then
begin
// フルスクリーン表示
// 多重起動禁止処理
hMutex := CreateMutex(nil, False, PChar(MUTEXNAME));
if (hMutex = 0) or (GetLastError = ERROR_ALREADY_EXISTS) then
begin
// 既に起動している
if hMutex <> 0 then
CloseHandle(hMutex);
Exit; // Exit は他言語での Return みたいなもの
end;
try
begin
Application.CreateForm(TForm1, Form1);
Application.Run;
end;
finally
if hMutex <> 0 then
CloseHandle(hMutex);
end;
Exit;
end;
if arg = '/c' then
begin
// 設定画面表示
Application.CreateForm(TForm2, Form2);
Application.Run;
Exit;
end;
if arg = '/p' then
begin
// プレビュー画面表示
if ParamCount >= 2 then
hwnd := StrToInt64Def(ParamStr(2), 0)
else
hwnd := 0;
Application.CreateForm(TForm3, Form3);
if hwnd <> 0 then
Form3.ParentWindow := hwnd;
Application.Run;
Exit;
end;
// 設定画面表示
Application.CreateForm(TForm2, Form2);
Application.Run;
end.
他のモードは多重起動禁止処理を入れなくていいのだろうか?
入れないほうがいいらしい。というのも、設定画面やプレビュー画面を表示している状態でも、時間が来たらスクリーンセーバを起動させないといけないので…。
◎ .scrを作成 :
これで処理は実装できた。後は、生成された .exe を .scr にリネームコピーしてやれば Windowsのスクリーンセーバになるはず…。
でも、ビルドするたびに一々 .exe を .scr に手作業でリネームコピーするのは面倒臭い。
しかし Delphi なら、ビルドが終わった直後に任意のコマンドを実行できるので、自動でリネームコピーをするように指定できる。
_Delphiでexeを生成したらscrも作りたい - mieki256's diary
プロジェクト → オプション → ビルド → ビルドイベント → ビルド後イベント → コマンド。以下を入力して「保存」をクリック。
この設定をしてからビルド(Shift + F9 や F9)をすると、以下のメッセージが表示される。Delphi が把握してない謎のコマンドが実行されようとしているけれどコレ本当に大丈夫? 危なくないか? と尋ねてきているのだろう。
「このプロジェクトを常に信頼する」にチェックを入れて「はい」をクリック。これで毎回ビルド後に、自動で .scr を作ってくれるようになった。
でも、ビルドするたびに一々 .exe を .scr に手作業でリネームコピーするのは面倒臭い。
しかし Delphi なら、ビルドが終わった直後に任意のコマンドを実行できるので、自動でリネームコピーをするように指定できる。
_Delphiでexeを生成したらscrも作りたい - mieki256's diary
プロジェクト → オプション → ビルド → ビルドイベント → ビルド後イベント → コマンド。以下を入力して「保存」をクリック。
copy /Y "$(OUTPUTPATH)" "$(OUTPUTDIR)$(OUTPUTNAME).scr"
この設定をしてからビルド(Shift + F9 や F9)をすると、以下のメッセージが表示される。Delphi が把握してない謎のコマンドが実行されようとしているけれどコレ本当に大丈夫? 危なくないか? と尋ねてきているのだろう。
SSDelphiTest1 を信頼しますか? このプロジェクトは1つまたは複数のビルドイベントを含んでおり、 これらはシステム上で任意のコマンドを実行する可能性があります。 このプロジェクトを信頼し、ビルドを続行しますか?
「このプロジェクトを常に信頼する」にチェックを入れて「はい」をクリック。これで毎回ビルド後に、自動で .scr を作ってくれるようになった。
◎ Windowsにインストール :
できあがったスクリーンセーバ(.scr)は、以下の場所にコピーすることでインストールできる。
今回は、ビルド構成を Release、ターゲットプラットフォームを Windows 64ビットにしてビルドしたので、64bit版のプログラム(.exe, .scr) が生成された。
自分は Windows11 x64 25H2 を使っているので、C:\Windows\System32\ に SSDelphiTest1.scr をコピーした。
「スクリーンセーバーの変更」ウインドウを表示して、リスト一覧の中に表示されるか確認。ファイル名の最初に 'SS' が付いているとそこは省略された状態で表示されるので、SSDelphiTest1 は DelphiTest1 として表示された。
期待した通りに動いてくれた…。Delphi でWindows用のスクリーンセーバを作ることができた。
出来上がった .scr のファイルサイズは 3.7MB。C#で作成すると100MBを超えてしまうので、このファイルサイズの小ささは魅力的。
- OSが64bit。スクリーンセーバが64bit版プログラム → C:\Windows\System32\
- OSが64bit。スクリーンセーバが32bit版プログラム → C:\Windows\SysWOW64\
- OSが32bit。スクリーンセーバが32bit版プログラム → C:\Windows\System32\
今回は、ビルド構成を Release、ターゲットプラットフォームを Windows 64ビットにしてビルドしたので、64bit版のプログラム(.exe, .scr) が生成された。
自分は Windows11 x64 25H2 を使っているので、C:\Windows\System32\ に SSDelphiTest1.scr をコピーした。
「スクリーンセーバーの変更」ウインドウを表示して、リスト一覧の中に表示されるか確認。ファイル名の最初に 'SS' が付いているとそこは省略された状態で表示されるので、SSDelphiTest1 は DelphiTest1 として表示された。
期待した通りに動いてくれた…。Delphi でWindows用のスクリーンセーバを作ることができた。
出来上がった .scr のファイルサイズは 3.7MB。C#で作成すると100MBを超えてしまうので、このファイルサイズの小ささは魅力的。
◎ スクリーンセーバ名を指定 :
この状態だと、「スクリーンセーバーの変更」ウインドウにはファイル名が表示されてしまう。
プログラムにリソースファイルを含ませることで、「スクリーンセーバーの変更」ウインドウ内で表示されるスクリーンセーバ名を指定することができる。
_Delphiでリソースファイルの追加 - mieki256's diary
_スクリーンセーバーを作ったが、名前を設定できずにファイル名になってしまう
プロジェクトフォルダ内に myresource.rc というテキストファイル(リソーススクリプトファイル)を作成して以下を記述した。"Delphi SSaver Test 1" が、今回表示したいスクリーンセーバ名。
プロジェクト → プロジェクトに追加、を選択。ファイル種類を「リソースファイル (.rc)」にして、myresources.rc を開く。
これでビルド(Shift + F9) をすれば、.rc が .res に変換されて、プログラム内に含まれた状態になる。「スクリーンセーバーの変更」のリスト上でも反映された。
プログラムにリソースファイルを含ませることで、「スクリーンセーバーの変更」ウインドウ内で表示されるスクリーンセーバ名を指定することができる。
_Delphiでリソースファイルの追加 - mieki256's diary
_スクリーンセーバーを作ったが、名前を設定できずにファイル名になってしまう
プロジェクトフォルダ内に myresource.rc というテキストファイル(リソーススクリプトファイル)を作成して以下を記述した。"Delphi SSaver Test 1" が、今回表示したいスクリーンセーバ名。
STRINGTABLE PRELOAD DISCARDABLE BEGIN 1 "Delphi SSaver Test 1" END
プロジェクト → プロジェクトに追加、を選択。ファイル種類を「リソースファイル (.rc)」にして、myresources.rc を開く。
これでビルド(Shift + F9) をすれば、.rc が .res に変換されて、プログラム内に含まれた状態になる。「スクリーンセーバーの変更」のリスト上でも反映された。
◎ ソース :
ここまで作業したソースを一応置いておく。
_SSDelphiTest1.dpr
_Unit1.pas
_Unit1.dfm
_Unit2.pas
_Unit2.dfm
_Unit3.pas
_Unit3.dfm
_myresource.rc
_SSDelphiTest1.dpr
_Unit1.pas
_Unit1.dfm
_Unit2.pas
_Unit2.dfm
_Unit3.pas
_Unit3.dfm
_myresource.rc
◎ 課題 :
いくつか課題は残ってる。
この状態ではマルチディスプレイ環境に対応していない。どうすれば対応できるのか…?
また、デスクトップ上で動いているアプリの種類によっては、この作りではキーの押し下げやマウス操作で終了できないという話もあるので、対応させられるなら対応させたい…。
_Windows 令和のスクリーンセーバーの作り方 - Qiita
設定を、ファイルもしくはレジストリに保存する処理も入ってない。どうすれば設定を保存・反映できるのか…? 仮に、ファイルとして設定を保存する際、適切なファイルフォーマットはどれだろう? .ini? .json? Delphi的には一体何がオススメなのだろう?
この状態ではマルチディスプレイ環境に対応していない。どうすれば対応できるのか…?
また、デスクトップ上で動いているアプリの種類によっては、この作りではキーの押し下げやマウス操作で終了できないという話もあるので、対応させられるなら対応させたい…。
_Windows 令和のスクリーンセーバーの作り方 - Qiita
設定を、ファイルもしくはレジストリに保存する処理も入ってない。どうすれば設定を保存・反映できるのか…? 仮に、ファイルとして設定を保存する際、適切なファイルフォーマットはどれだろう? .ini? .json? Delphi的には一体何がオススメなのだろう?
[ ツッコむ ]
以上です。


