Delphi:系统菜单是否打开?

在Delphi中,我需要一个确定系统菜单(即窗口菜单,点击图标时出现的菜单)是否被打开的函数。 原因是我写了一个反垃圾按键loggingfunction,它发送垃圾到当前活动的editcontrol(这也阻止了读WinAPI消息的键盘logging器读取内容)。 但是如果打开系统菜单,编辑控件STILL有焦点,所以垃圾会调用快捷方式。

如果我在我的TForm1中使用消息WM_INITMENUPOPUP ,我可以确定什么时候系统菜单打开,但我希望我不必更改TForm,因为我想写一个非可视化的组件,它不需要在TForm的任何修改 – 私有类本身。

//I do not want that solution since I have to modify TForm1 for that! procedure TForm1.WMInitMenuPopup(var Message: TWMInitMenuPopup); begin if message.MenuPopup=getsystemmenu(Handle, False) then begin SystemMenuIsOpened := true; end; end; 

TApplicaton.HookMainWindow()不会将WM_INITMENUPOPUP发送到我的挂钩函数。

 function TForm1.MessageHook(var Msg: TMessage): Boolean; begin Result := False; if (Msg.Msg = WM_INITMENUPOPUP) then begin // Msg.Msg IS NEVER WM_INITMENUPOPUP! if LongBool(msg.LParamHi) then begin SystemMenuIsOpened := true; end; end; end; procedure TForm1.FormCreate(Sender: TObject); begin Application.HookMainWindow(MessageHook); end; procedure TForm1.FormDestroy(Sender: TObject); begin Application.UnhookMainWindow(MessageHook); end; 

即使经过很长时间的研究,我也没有发现有关如何查询系统菜单是否打开的信息。 我没有find任何方式来确定该菜​​单的打开+closures。

请给我一个解决scheme吗?

问候
丹尼尔Marschall

Solutions Collecting From Web of "Delphi:系统菜单是否打开?"

如果你不想对TForm-derivate-class进行任何修改,为什么不尝试纯粹的Windows API方式来实现你当前的解决方案,即使用SetWindowLongPtr()截获WM_INITMENUPOPUP消息。 Delphi VCL风格截取消息实际上只是这个Windows API函数的一个包装。

为此,使用SetWindowLongPtr()窗口过程设置一个新的地址,并且一次性获得窗口过程的原始地址。 请记住将原始地址存储在LONG_PTR变量中。 在32位Delphi中, LONG_PTRLongint ; 假设未来将发布64位 Delphi, LONG_PTR应该是Int64 ; 你可以使用$IFDEF指令来区分它们,如下所示:

  Type {$IFDEF WIN32} PtrInt = Longint; {$ELSE} PtrInt = Int64; {$ENDIF} LONG_PTR = PtrInt; 

要用于此目的的nIndex参数的值是GWLP_WNDPROC 。 此外,将窗口过程的新地址传递给dwNewLong参数,例如LONG_PTR(NewWndProc)NewWndProc是一个处理消息的WindowProc回调函数 ,它是把你的拦截条件放在你要截取的消息的默认处理的地方。 回调函数可以是任何名称,但参数必须遵循WindowProc约定。

请注意,必须调用CallWindowProc()才能将未由新窗口过程处理的消息传递给原始窗口过程。

最后,应该在代码的某个地方再次调用SetWindowLongPtr() ,将修改/新窗口过程处理程序的地址设置回原始地址。 如上所述,原来的地址已被保存。

这里有一个Delphi代码示例 。 它使用了SetWindowLong() ,但现在微软建议使用SetWindowLongPtr() ,以使其兼容32位和64位版本的Windows。

在Delphi 2009之前,Delphi的Windows.pas中不存在SetWindowLongPtr() 。如果使用Delphi的旧版本,则必须自己声明,或者使用JEDI API Library的 JwaWinUser单元。

Application.HookMainWindow不会做你认为的事情。 它钩住隐藏的应用程序窗口,而不是主窗体。 要拦截WM_INITMENUPOPUP特定的表单,您只需要为它编写一个处理程序,就像您所看到的那样。

为了做到这一点对于任何一个组件的所有者形式,你可以指定窗体的WindowProc属性来放置钩子:

 unit FormHook; interface uses Windows, Classes, SysUtils, Messages, Controls, Forms; type TFormMessageEvent = procedure(var Message: TMessage; var Handled: Boolean) of object; TFormHook = class(TComponent) private FForm: TCustomForm; FFormWindowProc: TWndMethod; FOnFormMessage: TFormMessageEvent; protected procedure FormWindowProc(var Message: TMessage); virtual; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property OnFormMessage: TFormMessageEvent read FOnFormMessage write FOnFormMessage; end; procedure Register; implementation procedure Register; begin RegisterComponents('Test', [TFormHook]); end; procedure TFormHook.FormWindowProc(var Message: TMessage); var Handled: Boolean; begin if Assigned(FFormWindowProc) then begin Handled := False; if Assigned(FOnFormMessage) then FOnFormMessage(Message, Handled); if not Handled then FFormWindowProc(Message); end; end; constructor TFormHook.Create(AOwner: TComponent); begin inherited Create(AOwner); FFormWindowProc := nil; FForm := nil; while Assigned(AOwner) do begin if AOwner is TCustomForm then begin FForm := TCustomForm(AOwner); FFormWindowProc := FForm.WindowProc; FForm.WindowProc := FormWindowProc; Break; end; AOwner := AOwner.Owner; end; end; destructor TFormHook.Destroy; begin if Assigned(FForm) and Assigned(FFormWindowProc) then begin FForm.WindowProc := FFormWindowProc; FFormWindowProc := nil; FForm := nil; end; inherited Destroy; end; end. 

然后你可以在表单上使用这个组件:

 procedure TForm1.FormHook1FormMessage(var Message: TMessage; var Handled: Boolean); begin case Message.Msg of WM_INITMENUPOPUP: ... end; end; 

问题可能是,如果表单有任何其他组件执行相同的事情,那么你需要确保unhooking发生相反的顺序(最后挂钩,最后unhooked)。 上面的例子在构造函数中挂钩并在析构函数中解除挂钩; 这似乎工作,即使在同一表单上的多个实例。

没有尝试过这个自己,但给这个镜头:

使用GetMenuItemRect获取由GetSystemMenu返回的菜单项0的矩形。 我(假设!) GetMenuItemRect应该返回0如果系统菜单没有打开(因为系统不知道菜单项的矩形,除非它是打开的?)如果结果是非零,检查是否返回的协调是可能的给定的屏幕分辨率。

如果您有时间,可以查看AutoHotKey的源代码,以了解在系统菜单打开/关闭时如何监视 。