4

私はfiremonkeyアプリでグローバルホットキーを取得する方法を探しています(少なくとも今のところ、Windowsのみ)。いくつかの欲求不満とグーグルの後、これはうまくいくはずです:winapi呼び出しでホットキーを登録してください

RegisterHotKey(FmxHandleToHWND(form1.Handle), 0 , MOD_CONTROL, $41); 

trueを返します。
次に、フォームの手順でホットキーをキャッチします

procedure WMHotKey(var Msg: TWMHotKey); message WM_HOTKEY;

しかし、これは決して呼び出されません。以前はvclアプリでこれを行っていたので、firemonkeyはメッセージをさまざまな方法で処理していると思います。だから問題は:firemonkeyアプリでグローバルホットキーをキャッチするにはどうすればよいですか?

編集:そのソリューションを適用するいくつかの例。クラスの少ないユニットを作成しました

unit fire_hotkey;

interface

uses windows, messages,allocatehwnd;

type
  TMsgHandler = procedure (var Msg: TMessage) of object;

  THotClass = class(TObject)
    fMsgHandlerHWND : HWND;
    proc:TMsgHandler;
    constructor Create;
    procedure init;
    destructor Destroy; override;
  end;

implementation

{ hotClass }

constructor THotClass.Create;
begin
  inherited;

end;

destructor THotClass.Destroy;
begin
  ThreadDeallocateHWnd(fMsgHandlerHWND);
  inherited;
end;

procedure THotClass.init;
begin
   fMsgHandlerHWND := ThreadAllocateHWnd(proc,true);
end;

end.

次に、私のメインフォームには、ホットキーイベントを処理するための手順があります。

procedure TformEditor.WMHotKey(var Msg: TMessage);
begin
  if Msg.Msg = WM_HOTKEY then
  begin
    //call lua function or sth
    //...
  end
  else
    Msg.Result := DefWindowProc(hotkeyGrabber.fMsgHandlerHWND, Msg.Msg, Msg.wParam, Msg.lParam);
end;

そして、グローバルなhotkeyGrabber:THotClassがあります。フォームの作成時に初期化されます。

  hotkeyGrabber:=THotClass.Create;
  hotkeyGrabber.proc:=WMHotKey;
  hotkeyGrabber.init;

その後、通常のvclアプリのようにホットキーを登録する必要があり、それらは http://www.swissdelphicenter.ch/torry/showcode.php?id=147 それが理にかなっていることを願って検索されます

4

1 に答える 1

5

FMXフレームワークは、メッセージをフォームにルーティングしません。したがって、WMHotKeyFMXフレームワークがを呼び出すことはないため、が呼び出されることはありませんDispatchWndProcユニットの実装セクションで宣言されたメソッドを調べると、これが当てはまることがわかりますFMX.Platform.Win

この問題を解決する最も簡単な方法は、を呼び出して独自のウィンドウを作成することCreateWindowです。次に、メッセージを処理するそのウィンドウのウィンドウプロシージャを実装しWM_HOTKEYます。

これらの低レベルAPI呼び出しを次のようにラップしました。

unit AllocateHWnd;

interface

uses
  System.SysUtils, System.Classes, System.SyncObjs, Winapi.Messages, Winapi.Windows;

function ThreadAllocateHWnd(AMethod: TWndMethod; MessageOnly: Boolean): HWND;
procedure ThreadDeallocateHWnd(Wnd: HWND);

implementation

const
  GWL_METHODCODE = SizeOf(Pointer)*0;
  GWL_METHODDATA = SizeOf(Pointer)*1;
  ThreadAllocateHWndClassName = 'MyCompanyName_ThreadAllocateHWnd';

var
  ThreadAllocateHWndLock: TCriticalSection;
  ThreadAllocateHWndClassRegistered: Boolean;

function ThreadAllocateHWndProc(Window: HWND; Message: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
  Proc: TMethod;
  Msg: TMessage;
begin
  Proc.Code := Pointer(GetWindowLongPtr(Window, GWL_METHODCODE));
  Proc.Data := Pointer(GetWindowLongPtr(Window, GWL_METHODDATA));
  if Assigned(TWndMethod(Proc)) then begin
    Msg.Msg := Message;
    Msg.wParam := wParam;
    Msg.lParam := lParam;
    Msg.Result := 0;
    TWndMethod(Proc)(Msg);
    Result := Msg.Result
  end else begin
    Result := DefWindowProc(Window, Message, wParam, lParam);
  end;
end;

function ThreadAllocateHWnd(AMethod: TWndMethod; MessageOnly: Boolean): HWND;

  procedure RegisterThreadAllocateHWndClass;
  var
    WndClass: TWndClass;
  begin
    if ThreadAllocateHWndClassRegistered then begin
      exit;
    end;
    ZeroMemory(@WndClass, SizeOf(WndClass));
    WndClass.lpszClassName := ThreadAllocateHWndClassName;
    WndClass.hInstance := HInstance;
    WndClass.lpfnWndProc := @ThreadAllocateHWndProc;
    WndClass.cbWndExtra := SizeOf(TMethod);
    Winapi.Windows.RegisterClass(WndClass);
    ThreadAllocateHWndClassRegistered := True;
  end;

begin
  ThreadAllocateHWndLock.Acquire;
  Try
    RegisterThreadAllocateHWndClass;
    if MessageOnly then begin
      Result := CreateWindow(ThreadAllocateHWndClassName, '', 0, 0, 0, 0, 0, HWND_MESSAGE, 0, HInstance, nil);
    end else begin
      Result := CreateWindowEx(WS_EX_TOOLWINDOW, ThreadAllocateHWndClassName, '', WS_POPUP, 0, 0, 0, 0, 0, 0, HInstance, nil);
    end;
    Win32Check(Result<>0);
    SetWindowLongPtr(Result, GWL_METHODDATA, NativeInt(TMethod(AMethod).Data));
    SetWindowLongPtr(Result, GWL_METHODCODE, NativeInt(TMethod(AMethod).Code));
  Finally
    ThreadAllocateHWndLock.Release;
  End;
end;

procedure ThreadDeallocateHWnd(Wnd: HWND);
begin
  Win32Check(DestroyWindow(Wnd));
end;

initialization
  ThreadAllocateHWndLock := TCriticalSection.Create;

finalization
  ThreadAllocateHWndLock.Free;

end.

これはVCLのスレッドセーフバージョンでありAllocateHWnd、メインスレッドの外部では使用できないことで有名です。

あなたがする必要があるのは、ウィンドウプロシージャ、すなわちを実装する何かでクラスを作成することですTWndMethod。インスタンスメソッドまたはクラスメソッドにすることができます。次に、を呼び出しThreadAllocateHWndてウィンドウを作成し、そのウィンドウをに渡しますRegisterHotKey。すべてをほどくときは、ホットキーの登録を解除し、を呼び出してウィンドウを破棄しますThreadDeallocateHWnd

于 2013-03-12T11:11:44.030 に答える