gpt4 book ai didi

delphi - firemonkey 和全局热键?

转载 作者:行者123 更新时间:2023-12-03 15:27:20 26 4
gpt4 key购买 nike

我正在寻找一种在 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希望这是有道理的

最佳答案

FMX 框架不会将消息路由到您的表单。因此,您的 WMHotKey 永远不会被调用,因为 FMX 框架永远不会调用 Dispatch。通过检查 FMX.Platform.Win 单元的实现部分中声明的 WndProc 方法,您可以看到这种情况。

解决此问题的最简单方法是通过调用 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 销毁窗口。

关于delphi - firemonkey 和全局热键?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15359235/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com