2026/01/27(火) [n年前の日記]
#1 [delphi] Delphi 12 CEのブラシデザイナが表示できない
Windows11 x64 25H2 + Delphi 12.1 CE (Community Edition) 上で FMX (FireMonkey) の使い方を勉強していたけれど、ブラシデザイナ? なる機能が呼び出せなくて困ってしまった。一応、ソースコードにガリガリと処理を書いても同じことはできるらしいけど…。
Delphi でGUIアプリを作る場合、昔から存在してる VCLライブラリを使う方法と、OpenGLやDirectXで描画する FMX (FireMonkey)ライブラリを使う方法があるそうで、後者が気になったので試してたのけど…。
本来であれば、FMX のプロジェクトを新規作成して、TRectangle を置いて、Fill プロパティをダブルクリックするとブラシデザイナが起動するらしいのだけど…。
「0による浮動小数点数除算」というエラーメッセージが表示されて、それっきり。エラーダイアログが出るだけで、IDEは落ちずに済んでるけれど…。新規プロジェクトを作成して試しても同じエラーが出るので、プロジェクトの内容が悪さをしているわけではなさそう。
ググってみても対策が分からない…。そもそも、そういう不具合事例すら見かけない。Delphiユーザが少ないことに加えて FMX を使っている人は更に少ないということだろうか。それとも自分の環境がおかしいだけだろうか…。
仮にバグだとしても修正される見込みは無いだろうな…。現行版は Delphi 13 で、1つ前のバージョンを条件付きで無償利用できることにしているのが Community Edition らしいし。おそらく体験版のような位置づけだろうから、不具合が見つかっても放置だろう…。一応、12.2、12.3 の修正内容も眺めてみたけど、修正された的な記述も無く。バグ報告がどこに集まってるのかも分からない。たぶんログインしないと見れないような、比較的クローズドな場所に集まってるんだろう…。
Delphi は本来20万円以上する開発環境だけど、何十万も払ってこういうバグに遭遇したら萎えそうな予感…。
Lazarus に移行できるならしたほうがいいのかな。あっちはオープンソースだし…。
Delphi でGUIアプリを作る場合、昔から存在してる VCLライブラリを使う方法と、OpenGLやDirectXで描画する FMX (FireMonkey)ライブラリを使う方法があるそうで、後者が気になったので試してたのけど…。
本来であれば、FMX のプロジェクトを新規作成して、TRectangle を置いて、Fill プロパティをダブルクリックするとブラシデザイナが起動するらしいのだけど…。
「0による浮動小数点数除算」というエラーメッセージが表示されて、それっきり。エラーダイアログが出るだけで、IDEは落ちずに済んでるけれど…。新規プロジェクトを作成して試しても同じエラーが出るので、プロジェクトの内容が悪さをしているわけではなさそう。
ググってみても対策が分からない…。そもそも、そういう不具合事例すら見かけない。Delphiユーザが少ないことに加えて FMX を使っている人は更に少ないということだろうか。それとも自分の環境がおかしいだけだろうか…。
仮にバグだとしても修正される見込みは無いだろうな…。現行版は Delphi 13 で、1つ前のバージョンを条件付きで無償利用できることにしているのが Community Edition らしいし。おそらく体験版のような位置づけだろうから、不具合が見つかっても放置だろう…。一応、12.2、12.3 の修正内容も眺めてみたけど、修正された的な記述も無く。バグ報告がどこに集まってるのかも分からない。たぶんログインしないと見れないような、比較的クローズドな場所に集まってるんだろう…。
Delphi は本来20万円以上する開発環境だけど、何十万も払ってこういうバグに遭遇したら萎えそうな予感…。
Lazarus に移行できるならしたほうがいいのかな。あっちはオープンソースだし…。
[ ツッコむ ]
#2 [lazarus] Lazarusでビルド後にコマンドを実行
Windows11 x64 25H2 + Lazarus 4.4 で、ビルド後にコマンドを実行したい。.exe を .scr にリネームコピーしたい。
Delphi はビルド後に指定したコマンドを実行できるけど、Lazarus も使えるのだろうか? 調べてみたら、使えそう。
これでなんとかなりそう。
Delphi はビルド後に指定したコマンドを実行できるけど、Lazarus も使えるのだろうか? 調べてみたら、使えそう。
- プロジェクトオプション (Ctrl + Shift + F11) → コンパイラオプション → コンパイラコマンド、に「次の後に実行」という欄がある。
- 「コンパイル」「構築」にチェックを入れて、「実行」のチェックは外す。
- 「コマンド」のところにファイルコピーをするコマンドを入力。
これでなんとかなりそう。
◎ 使えるマクロ名が分からない :
コマンドを記述する際に使えるマクロ(?)名が分からない…。一応、以下のマクロは使えそうだと分かったけれど…。
しかし、拡張子を除いた生成ファイル名のマクロがあるんだか、ないんだか…。
AIに尋ねたら、「いっそファイル名を直接書いちまえよ。それなら間違いないぞ」と言ってきた。それはまあ、たしかに…。以下にしてみたら一応目的は果たせた。
_IDE Macros in paths and filenames - Free Pascal wiki
む? もしかして、$NameOnly() を使えば拡張子を除いたファイル名だけ取り出せないか…?
これで実現できたかも。
- $(TargetFile) : 生成される .exe のフルパス。
- $(ProjPath) : プロジェクトフォルダのフルパス。最後に区切り文字 "\" が含まれている。
しかし、拡張子を除いた生成ファイル名のマクロがあるんだか、ないんだか…。
AIに尋ねたら、「いっそファイル名を直接書いちまえよ。それなら間違いないぞ」と言ってきた。それはまあ、たしかに…。以下にしてみたら一応目的は果たせた。
cmd /C copy /Y "$(TargetFile)" "$(ProjPath)sslazarus1.scr"
_IDE Macros in paths and filenames - Free Pascal wiki
む? もしかして、$NameOnly() を使えば拡張子を除いたファイル名だけ取り出せないか…?
cmd /C copy /Y "$(TargetFile)" "$(ProjPath)$NameOnly($(TargetFile)).scr"
これで実現できたかも。
◎ 文字化けについて :
IDEの下のほうにメッセージが表示されているけれど、右クリックして「プロジェクト次の後にコマンドを実行:について」を選ぶと、実際にどんなコマンドが実行されたのか確認できる。もし、「(不明なマクロ)」という記述が見えた場合はマクロ名の指定で失敗してる。
文字化けしてる感じのメッセージも表示されているけれど…。たぶん copyコマンドが出力しているメッセージと、出力ウインドウの文字コードが違うのだろう…。
AIに尋ねてみたら「PowerShellを使えばメッセージは出ないで」と言ってきた。DOS窓で以下を打ってみたら、たしかに処理はされつつメッセージも出ないように見える。
Lazarus上で指定するなら以下になるのかな…。
これでも一応動作しているように見える。
文字化けしてる感じのメッセージも表示されているけれど…。たぶん copyコマンドが出力しているメッセージと、出力ウインドウの文字コードが違うのだろう…。
AIに尋ねてみたら「PowerShellを使えばメッセージは出ないで」と言ってきた。DOS窓で以下を打ってみたら、たしかに処理はされつつメッセージも出ないように見える。
powershell -command "Copy-Item 'sslazarus1.exe' 'sslazarus1.scr' -Force"
Lazarus上で指定するなら以下になるのかな…。
powershell -command "Copy-Item '$(TargetFile)' '$(ProjPath)$NameOnly($(TargetFile)).scr' -Force"
これでも一応動作しているように見える。
[ ツッコむ ]
#3 [lazarus] Lazarusでスクリーンセーバを作ろうとしてハマった
Windows11 x64 25H2 + Lazarus 4.4 でWindows用のスクリーンセーバを作ろうとしたけど、かなりハマった…。
Delphi と似たノリで作れるかなと試していたけれど、全画面表示モード、設定画面モードについては似た感じで作れたものの、プレビュー画面モードで躓いた。
Windowsから与えられたウインドウハンドルを、フォームの親ウインドウを指定するプロパティ、ParentWindow に代入すれば済むだろう、Delphi ならそれだけで上手く行ったし…。
しかし試してみたら、プレビュー画面モードで起動したプロセスがいつまで経っても終了してくれない。「スクリーンセーバーの変更」ウインドウでリストを切り替えるたびに、プロセスが次々に発生して、そのまま残り続ける…。大量のプロセスがずっと残ったままになる…。
AI君に対策を尋ねてみたけれど、どれもなかなか上手く行かなくて…。
A. 親ウインドウの存在をチェックする方法は、「スクリーンセーバの変更」ウインドウを閉じた時しか効かなかった。「スクリーンセーバーの変更」ウインドウが画面に表示されてる間は、大量のプロセスが残り続けてしまう。どうやら件のウインドウが表示されてる間、親ウインドウになるべきウインドウは、ずっと同じ状態で存在しているのだろう…。
B. WndProc()のオーバーライドも、「スクリーンセーバーの変更」ウインドウを閉じた時しか効かず…。
C. 持っている親ウインドウハンドルと、与えられたウインドウハンドルが違っているかチェックする方法は、フォームが表示された直後に終了してしまう。何故。
D. 自身が非表示になったら終了する方法を試したら、ようやくプロセスがその都度終了してくれた。この方法で大丈夫なのか分からんけど…。でも、プロセスが大量に残り続けるよりはいいだろう…。
Windowsの「スクリーンセーバーの変更」ウインドウは、一体何を子ウインドウに送ることで子ウインドウを消滅させているのだろうか…。
Delphi と似たノリで作れるかなと試していたけれど、全画面表示モード、設定画面モードについては似た感じで作れたものの、プレビュー画面モードで躓いた。
Windowsから与えられたウインドウハンドルを、フォームの親ウインドウを指定するプロパティ、ParentWindow に代入すれば済むだろう、Delphi ならそれだけで上手く行ったし…。
しかし試してみたら、プレビュー画面モードで起動したプロセスがいつまで経っても終了してくれない。「スクリーンセーバーの変更」ウインドウでリストを切り替えるたびに、プロセスが次々に発生して、そのまま残り続ける…。大量のプロセスがずっと残ったままになる…。
AI君に対策を尋ねてみたけれど、どれもなかなか上手く行かなくて…。
- A. TTimer を設置して一定時間毎に親ウインドウが存在するかどうかチェックして、存在しなかったら終了。
- B. WndProc(var Message: TMessage) を override して、Windowsから送られてくるメッセージをチェックしてみる。WM_CLOSE, WM_DESTROY, WM_NCDESTROY が届いたら終了。
- C. 自身のフォームが持っている親ウインドウのハンドルと、与えられたウインドウハンドルが違っていたら終了。
- D. 自分が非表示にされていたら終了。
A. 親ウインドウの存在をチェックする方法は、「スクリーンセーバの変更」ウインドウを閉じた時しか効かなかった。「スクリーンセーバーの変更」ウインドウが画面に表示されてる間は、大量のプロセスが残り続けてしまう。どうやら件のウインドウが表示されてる間、親ウインドウになるべきウインドウは、ずっと同じ状態で存在しているのだろう…。
B. WndProc()のオーバーライドも、「スクリーンセーバーの変更」ウインドウを閉じた時しか効かず…。
C. 持っている親ウインドウハンドルと、与えられたウインドウハンドルが違っているかチェックする方法は、フォームが表示された直後に終了してしまう。何故。
D. 自身が非表示になったら終了する方法を試したら、ようやくプロセスがその都度終了してくれた。この方法で大丈夫なのか分からんけど…。でも、プロセスが大量に残り続けるよりはいいだろう…。
Windowsの「スクリーンセーバーの変更」ウインドウは、一体何を子ウインドウに送ることで子ウインドウを消滅させているのだろうか…。
◎ ソース :
一応、プレビュー画面モード部分のソースを貼っておく。
_previewformunit.pas
_sslazarus1.lpr
_previewformunit.pas
unit PreviewFormUnit;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, LCLType,
StdCtrls, LCLIntf, ExtCtrls,
Windows;
type
{ TPreviewForm }
TPreviewForm = class(TForm)
Label1: TLabel;
Timer1: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
procedure Timer1Timer(Sender: TObject);
private
FParentHWND: HWND;
protected
procedure WndProc(var Message: TMessage); override;
public
procedure EmbedIntoParent(pHWND: HWND);
end;
var
PreviewForm: TPreviewForm;
implementation
{$R *.lfm}
{ TPreviewForm }
procedure TPreviewForm.FormCreate(Sender: TObject);
begin
//FParentHWND := 0;
BorderStyle := bsNone;
Left := 0;
Top := 0;
Width := 152;
Height := 112;
Label1.Left := (ClientWidth - Label1.Width) div 2;
Label1.Top := (ClientHeight - Label1.Height) div 2;
Label1.Font.Color := clBlue;
end;
// 親ウインドウを指定
procedure TPreviewForm.EmbedIntoParent(pHWND: HWND);
var
r: TRect;
begin
FParentHWND := pHWND;
if pHWND <> 0 then
begin
ParentWindow := pHWND;
Windows.SetParent(self.Handle, pHWND);
SetWindowLong(Self.Handle, GWL_STYLE, GetWindowLong(self.Handle, GWL_STYLE) or
WS_CHILD);
Windows.GetClientRect(pHWND, r);
MoveWindow(Self.Handle, 0, 0, r.Right - r.Left, r.Bottom - r.Top, True);
Visible := True;
end;
end;
procedure TPreviewForm.Timer1Timer(Sender: TObject);
begin
// 一定時間毎に自身が消えるべきかチェックする
//Windows.Beep(440, 100);
if (FParentHWND <> 0) and (not Windows.IsWindow(FParentHWND)) then
begin
// 親ウインドウが存在していないので終了
//Windows.Beep(1000, 100);
Application.Terminate;
end
else if not Windows.IsWindowVisible(self.Handle) then
begin
// 自分が非表示にされているなら終了
//Windows.Beep(1000, 1000);
Application.Terminate;
end;
end;
procedure TPreviewForm.WndProc(var Message: TMessage);
begin
//Windowsから閉じろとメッセージが来ているなら終了
case Message.Msg of
WM_CLOSE, WM_DESTROY, WM_NCDESTROY:
begin
//Windows.Beep(2000, 300);
Application.Terminate;
end;
end;
inherited WndProc(Message);
end;
procedure TPreviewForm.FormKeyDown(Sender: TObject; var Key: word; Shift: TShiftState);
begin
if FParentHWND = 0 then
begin
// 開発時用。ESCキーで終了
if Key = VK_ESCAPE then
Application.Terminate;
end;
end;
//initialization
// RegisterClass(TPreviewForm);
end.
_sslazarus1.lpr
program sslazarus1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}
cthreads,
{$ENDIF}
{$IFDEF HASAMIGA}
athreads,
{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms,
LCLIntf,
LCLType,
Windows,
SysUtils,
FullScrFormUnit,
ConfigFormUnit,
PreviewFormUnit { you can add units after this };
{$R *.res}
var
arg: string;
phwnd: HWND;
hMutex: THandle;
const
MUTEX_NAME: string = 'ScreenSaverLazarus1Mutex8686';
begin
RequireDerivedFormResource := True;
Application.Scaled := True;
{$PUSH}
{$WARN 5044 OFF}
//Application.MainFormOnTaskbar := True;
Application.MainFormOnTaskbar := False;
{$POP}
Application.Initialize;
if ParamCount >= 1 then
arg := LowerCase(Copy(ParamStr(1), 1, 2))
else
arg := '';
if arg = '/s' then
begin
// Fullscreen mode
hMutex := CreateMutex(nil, False, PChar(MUTEX_NAME));
if (hMutex = 0) or (GetLastError = ERROR_ALREADY_EXISTS) then
begin
if hMutex <> 0 then
CloseHandle(hMutex);
Exit;
end;
try
Application.CreateForm(TFullScreenForm, FullScreenForm);
Application.Run;
finally
if hMutex <> 0 then
CloseHandle(hMutex);
end;
end
else if arg = '/c' then
begin
// Config mode
Application.CreateForm(TConfigForm, ConfigForm);
Application.Run;
Exit;
end
else if arg = '/p' then
begin
// Preview mode
if ParamCount >= 2 then
phwnd := HWND(StrToInt64Def(ParamStr(2), 0))
else
phwnd := 0;
Application.CreateForm(TPreviewForm, PreviewForm);
if phwnd <> 0 then
PreviewForm.EmbedIntoParent(phwnd);
Application.Run;
end
else
begin
// Config mode
Application.CreateForm(TConfigForm, ConfigForm);
Application.Run;
end;
end.
◎ 余談。プロセスの確認 :
プロセスが残り続けているかどうかは、System Explorer 7.1.0.5359 を使ってチェックした。
_System Explorer Portable | PortableApps.com
右上のフィルタ入力欄(?)に文字列を打ち込めば、その文字列を含んだ名前のプロセスがリストアップされる。
_System Explorer Portable | PortableApps.com
右上のフィルタ入力欄(?)に文字列を打ち込めば、その文字列を含んだ名前のプロセスがリストアップされる。
[ ツッコむ ]
#4 [nitijyou] オノヤの方が来訪
ここ1週間ばかり、トイレの壁の後ろから出ているパイプから、水がポタポタと出続けている。下にバケツを置いて水を溜めていたけれど、1日でバケツから溢れるほどの量…。お袋さんがオノヤに電話で連絡したら、業者(?)の方が来訪して見てくれることになった。今日の夕方、15:45-16:20頃まで説明や調整をしてくれた。
*1
簡易水洗トイレの上のタンクから水が溢れそうになると後ろのパイプから出てくるということで、汚水の類ではないらしい。いや、手洗いをした水が上のタンクに入るから、一応汚水なのか…? 何にせよ、そのパイプから水が出てくる状況は全然アリらしい。にしても、ずっと出続けてるのはおかしい…。
業者の方が、上のタンクの中の、フロートの位置を数段階下げてくれた。これでタンク内に溜まる水の量が少なくなって漏れ出ることも少なくなる…のかな? そういうロジックでいいのか? 本当に? ちょっと自信が無い。何にしても、これで漏れ出る量が変化する可能性はあるだろう…。
簡易水洗トイレの上のタンクから水が溢れそうになると後ろのパイプから出てくるということで、汚水の類ではないらしい。いや、手洗いをした水が上のタンクに入るから、一応汚水なのか…? 何にせよ、そのパイプから水が出てくる状況は全然アリらしい。にしても、ずっと出続けてるのはおかしい…。
業者の方が、上のタンクの中の、フロートの位置を数段階下げてくれた。これでタンク内に溜まる水の量が少なくなって漏れ出ることも少なくなる…のかな? そういうロジックでいいのか? 本当に? ちょっと自信が無い。何にしても、これで漏れ出る量が変化する可能性はあるだろう…。
◎ タンクの設計がよろしくない :
フロートの調整作業がとにかく大変そうだった。手洗い用の水が出るところが邪魔になって蓋を外せず、斜めにして仮置きして作業するから重い蓋が落ちそうで危険極まりない。
水が出るところをシャワーヘッドのように回して外せたら蓋をスポンと抜いて作業できそうなのに…。INAXの設計はダメ過ぎないか…。いや、コストダウンでこうしたのかな…。あるいは水が流れる部分だから部品点数を少なめにしないと危険なのだろうか。だとしても、あの設計はどうかと思う。現場の苦労を無駄に増やす設計というか…。もうちょっとどうにかならなかったのか…。
水が出るところをシャワーヘッドのように回して外せたら蓋をスポンと抜いて作業できそうなのに…。INAXの設計はダメ過ぎないか…。いや、コストダウンでこうしたのかな…。あるいは水が流れる部分だから部品点数を少なめにしないと危険なのだろうか。だとしても、あの設計はどうかと思う。現場の苦労を無駄に増やす設計というか…。もうちょっとどうにかならなかったのか…。
*1: 本当は先週末に来てくれるという話だったのだけど…。「あそこは色々面倒臭いから行きたくねえ」みたいな扱いになってるのかな…。
[ ツッコむ ]
以上、1 日分です。