2013-03-12 4 views
4

Я ищу способ захватить глобальную горячую клавишу в приложении firemonkey (только для окон, по крайней мере пока). После некоторого разочарования и погуглить это должно работать: регистр горячих клавиш с WinAPI называютfiremonkey и глобальная горячая клавиша?

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

он возвращает истину.
, а затем поймать горячую клавишу в процедуре, сформировавшийся

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, и они будут Cought http://www.swissdelphicenter.ch/torry/showcode.php?id=147 надеется, что это имеет смысл

+1

Кто-то помог мне на самом деле использовать это решение, поэтому я добавил его на вопрос – laggyluk

ответ

5

Рамка FMX будет не направлять сообщения ваша форма. Итак, ваш WMHotKey никогда не будет вызван, потому что фреймворк FMX никогда не вызывает Dispatch. Вы можете видеть, что это происходит, проверяя метод WndProc, объявленный в разделе реализации блока 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.

+0

работ: D большое спасибо – laggyluk

Смежные вопросы