2026/02/13(金) [n年前の日記]
#1 [lazarus] LazarusでWebView2を使いたい。その4
Lazarus 4.4 + WebView4Delphi を使って Windows用のスクリーンセーバを作成しているところ。
◎ キーの押し下げが取得できない :
WebView2部分をマウスクリックしてフォーカスを移すと、フォーム側の OnKeyDownイベントが取得できなくなることに気づいた。KeyPreveiwプロパティを True にしていてもダメか…。
TTimer で一定時間毎にマウスの状態を監視して、ボタンが押されたか、マウスカーソルが動いたかを検出していたので、似たような方法でESCキーの押し下げだけは取得できたのだけど…。
ただ、何かしらのキーが押されたかどうかをこの方法でチェックしようとしたら、起動直後にアプリが終了してしまって…。
そこで、以下のページで紹介されている、SetWindowsHookEx() を使ってみることにした。
_[Windows] 令和のスクリーンセーバーの作り方 #初心者 - Qiita
AI(Google Gemini)によると、SetWindowsHookEx(WH_KEYBOARD, KeyHookProc, 0, GetCurrentThreadID); という書き方は、自身のスレッドを対象にしている時は動くけれど、WebView2 は Delphi/Lazarusのフォームと別スレッドで動いてしまうから効かないそうで…。以下のような書き方になった。
Delphi なら WH_KEYBOARD_LL が定義されているけれど、Lazarus では定義されていないのでコンパイルエラーが出てしまった。仕方なく、自前で定義することになった。
ちなみに、WH_KEYBOARD より WH_KEYBOARD_LL のほうが低レベルな扱いらしくて、システム全体に絡んでくるから気をつけろ、とAIが言っている。また、フォームを閉じるときに必ずフックを解除しないといけないから気をつけろ、ともAIが言っている。今回は OnDestroy イベントの中に入れておいた。
WH_KEYBOARD_LL は JwaWindows の中で定義されているので、それを uses に追加すれば未定義エラーは出なくなるけれど、試したところ GetCursorPos() でコンパイルエラーが出るようになってしまって…。AIに尋ねたら、uses に書く順番を気を付ければ、GetCursorPos() が後から別の何かで上書き定義されてエラーを解消できると言ってきた。試したら、たしかに記述する順番によってコンパイルエラーは出なくなった。
ただ、WH_KEYBOARD_LL という定義1つのために uses の記述順まで意識しなきゃいけないのはどうかと…。今回は JwaWindows をコメントアウトして、自前で WH_KEYBOARD_LL だけを定義することにした。
TTimer で一定時間毎にマウスの状態を監視して、ボタンが押されたか、マウスカーソルが動いたかを検出していたので、似たような方法でESCキーの押し下げだけは取得できたのだけど…。
uses
Windows,
//...
{ タイマーを使って一定時間毎に終了条件を満たしているか調べる }
procedure TFullScrnForm.MouseCheckTimerTimer(Sender: TObject);
begin
// ESCキーが押されてるかチェック
if (Windows.GetAsyncKeyState(VK_ESCAPE) and $8000) <> 0 then
begin
Application.Terminate;
Exit;
end;
ただ、何かしらのキーが押されたかどうかをこの方法でチェックしようとしたら、起動直後にアプリが終了してしまって…。
そこで、以下のページで紹介されている、SetWindowsHookEx() を使ってみることにした。
_[Windows] 令和のスクリーンセーバーの作り方 #初心者 - Qiita
AI(Google Gemini)によると、SetWindowsHookEx(WH_KEYBOARD, KeyHookProc, 0, GetCurrentThreadID); という書き方は、自身のスレッドを対象にしている時は動くけれど、WebView2 は Delphi/Lazarusのフォームと別スレッドで動いてしまうから効かないそうで…。以下のような書き方になった。
uses
Windows,
// ...
const
WH_KEYBOARD_LL = 13;
var
hhkLowLevelKybd: HHOOK;
keyDownFg: boolean;
// キーボードフック時に呼ばれるコールバック関数
function LowLevelKbdProc(nCode: integer; wParam: WPARAM; lParam: LPARAM): LRESULT;
stdcall;
begin
// nCodeが0以上の場合、有効なキーイベント
if nCode = HC_ACTION then
begin
if (wParam = WM_KEYDOWN) or (wParam = WM_SYSKEYDOWN) then
begin
// キーが押された
keyDownFg := True;
end;
end;
// 次のフックへ処理を渡す
Result := CallNextHookEx(hhkLowLevelKybd, nCode, wParam, lParam);
end;
{ フォームが表示される時の処理 }
procedure TFullScrnForm.FormShow(Sender: TObject);
begin
// ...
// キーボードフックを設定。
// WebView2にフォーカスが当たってると
// フォームのOnKeyDownイベントが取得できなくなるのでこうして処理する
hhkLowLevelKybd := SetWindowsHookEx(WH_KEYBOARD_LL, @LowLevelKbdProc, HInstance, 0);
keyDownFg := False;
if hhkLowLevelKybd = 0 then
begin
ShowMessage('Error: Initializing keyboard hook.');
end;
end;
{ タイマーを使って一定時間毎に終了条件を満たしているか調べる }
procedure TFullScrnForm.MouseCheckTimerTimer(Sender: TObject);
begin
MouseCheckTimer.Enabled := False;
// ESCキーが押されてるかチェック
if (Windows.GetAsyncKeyState(VK_ESCAPE) and $8000) <> 0 then
begin
Application.Terminate;
Exit;
end;
// ...
// 何かキーが押されてたら終了
if (FChkKeyDown) and (keyDownFg) then
begin
Application.Terminate;
Exit;
end;
MouseCheckTimer.Enabled := True;
end;
{ フォームを破棄する際の処理 }
procedure TFullScrnForm.FormDestroy(Sender: TObject);
begin
// ...
// キーボードフックを解除
if hhkLowLevelKybd <> 0 then
UnhookWindowsHookEx(hhkLowLevelKybd);
end;
Delphi なら WH_KEYBOARD_LL が定義されているけれど、Lazarus では定義されていないのでコンパイルエラーが出てしまった。仕方なく、自前で定義することになった。
ちなみに、WH_KEYBOARD より WH_KEYBOARD_LL のほうが低レベルな扱いらしくて、システム全体に絡んでくるから気をつけろ、とAIが言っている。また、フォームを閉じるときに必ずフックを解除しないといけないから気をつけろ、ともAIが言っている。今回は OnDestroy イベントの中に入れておいた。
WH_KEYBOARD_LL は JwaWindows の中で定義されているので、それを uses に追加すれば未定義エラーは出なくなるけれど、試したところ GetCursorPos() でコンパイルエラーが出るようになってしまって…。AIに尋ねたら、uses に書く順番を気を付ければ、GetCursorPos() が後から別の何かで上書き定義されてエラーを解消できると言ってきた。試したら、たしかに記述する順番によってコンパイルエラーは出なくなった。
uses JwaWindows, Windows, Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, LazFileUtils, LCLType, ComCtrls, uWVWindowParent, uWVBrowser, uWVLoader, uWVBrowserBase, uWVTypes, uWVTypeLibrary, ConfigData;
ただ、WH_KEYBOARD_LL という定義1つのために uses の記述順まで意識しなきゃいけないのはどうかと…。今回は JwaWindows をコメントアウトして、自前で WH_KEYBOARD_LL だけを定義することにした。
[ ツッコむ ]
#2 [anime] 「銀魂 THE FINAL」を視聴
BSテレ東で放送されていた版を録画していたので視聴。漫画原作をアニメ化した「銀魂」の劇場版、なのだろうか。
単独で見れる作りなのかなと思いながら眺め始めたのだけど、どうやらTVアニメ版か別の劇場版の続きだったようで何が何やら…。いや、一応冒頭でこれまでのあらすじと称して長々と説明してたのだけど、全編が DBZのパロディになっていたものだからさっぱり頭の中に入ってこない。というか本編で「こんなんじゃわかんねえよ!」的ツッコミ入れてて「ホントだよ…わかんねえよ…」と思った…。前作視聴済みを前提にして作ってあるのだろうな。というか、あらすじと言ってるけどコレ全部嘘話でしたというギャグなのかなあ? と思いながら眺めてたんで、ますます設定が分からない…。
本編も延々とバトルシーンが続いて…。銀魂にそういうのは求めてないんだけどなあ…。
もしかして銀魂もキン肉マンみたいな状態になってるんだろうか。キン肉マンも最初の頃はパロディギャグ漫画だった気がするけどいつの間にかプロレス漫画になってたし…。考えてみたらDBもそうか。西遊記のパロディ漫画っぽく始まったのに天下一武道会あたりから妙なことになった感が…。
でも、このあたりは銀八先生のコーナーで「先生この漫画ギャグ漫画だったのに最近バトルばっかりなんですが」「ジャンプ漫画だからこうなるのは当たり前です諦めてくださいキン肉マンもDBもそうだったでしょジャンプの宿命なんです仕方ないんです」とか平気で言ってそうでもある…。
とりあえず、一応視聴はした、とだけメモしておこう…。
もっとも自分のことだからどうせ見たことを忘れてしまってそのうち何かの拍子に目にして「アレ? なんかコレ見たような気がするぞ?」と首を捻ったりするんだろうなあ…。今期も再放送のTVアニメを数本見て「アレ? もしかしてこのアニメ見たことあるのでは…?」と困惑してたりするので…。
単独で見れる作りなのかなと思いながら眺め始めたのだけど、どうやらTVアニメ版か別の劇場版の続きだったようで何が何やら…。いや、一応冒頭でこれまでのあらすじと称して長々と説明してたのだけど、全編が DBZのパロディになっていたものだからさっぱり頭の中に入ってこない。というか本編で「こんなんじゃわかんねえよ!」的ツッコミ入れてて「ホントだよ…わかんねえよ…」と思った…。前作視聴済みを前提にして作ってあるのだろうな。というか、あらすじと言ってるけどコレ全部嘘話でしたというギャグなのかなあ? と思いながら眺めてたんで、ますます設定が分からない…。
本編も延々とバトルシーンが続いて…。銀魂にそういうのは求めてないんだけどなあ…。
もしかして銀魂もキン肉マンみたいな状態になってるんだろうか。キン肉マンも最初の頃はパロディギャグ漫画だった気がするけどいつの間にかプロレス漫画になってたし…。考えてみたらDBもそうか。西遊記のパロディ漫画っぽく始まったのに天下一武道会あたりから妙なことになった感が…。
でも、このあたりは銀八先生のコーナーで「先生この漫画ギャグ漫画だったのに最近バトルばっかりなんですが」「ジャンプ漫画だからこうなるのは当たり前です諦めてくださいキン肉マンもDBもそうだったでしょジャンプの宿命なんです仕方ないんです」とか平気で言ってそうでもある…。
とりあえず、一応視聴はした、とだけメモしておこう…。
もっとも自分のことだからどうせ見たことを忘れてしまってそのうち何かの拍子に目にして「アレ? なんかコレ見たような気がするぞ?」と首を捻ったりするんだろうなあ…。今期も再放送のTVアニメを数本見て「アレ? もしかしてこのアニメ見たことあるのでは…?」と困惑してたりするので…。
[ ツッコむ ]
以上、1 日分です。