mieki256's diary



2026/02/13(金) [n年前の日記]

#1 [lazarus] LazarusでWebView2を使いたい。その4

Lazarus 4.4 + WebView4Delphi を使って Windows用のスクリーンセーバを作成しているところ。

キーの押し下げが取得できない :

WebView2部分をマウスクリックしてフォーカスを移すと、フォーム側の OnKeyDownイベントが取得できなくなることに気づいた。KeyPreveiwプロパティを True にしていてもダメか…。

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 だけを定義することにした。

以上です。

過去ログ表示

Prev - 2026/02 -
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project