为什么使用Esc关闭弹出菜单的著名解决方法在私有句柄上不起作用?

3
我创建了一个组件,用于在我的应用程序中使用托盘图标。当图标显示弹出菜单时,无法使用Esc键关闭。然后我在这里找到了David Heffernan的解决方法。我将代码集成到我的组件中,现在菜单可以使用Esc关闭,但是在我弹出菜单之后,我的应用程序变得完全死亡,我无法访问主窗体上的任何内容,甚至系统按钮也不再起作用。
以下是重现问题的代码:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Menus, ShellApi;

const WM_ICONTRAY = WM_USER+1;

type
  TForm1 = class(TForm)
    PopupMenu1: TPopupMenu;
    Test1: TMenuItem;
    Test2: TMenuItem;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    IconData: TNotifyIconData;
  protected
    procedure PrivateWndProc(var Msg: TMessage); virtual;
  public
    PrivateHandle:HWND;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
 PrivateHandle:=AllocateHWnd(PrivateWndProc);

 // add an icon to tray
 IconData.cbSize:=SizeOf(IconData);
 IconData.Wnd:=PrivateHandle;
 IconData.uID:=1;
 IconData.uFlags:=NIF_MESSAGE + NIF_ICON;
 IconData.uCallbackMessage:=WM_ICONTRAY;
 IconData.hIcon:=Application.Icon.Handle;
 Shell_NotifyIcon(NIM_ADD, @IconData);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
 IconData.uFlags:=0;
 Shell_NotifyIcon(NIM_DELETE, @IconData);
 DeallocateHWnd(PrivateHandle);
end;

procedure TForm1.PrivateWndProc(var Msg: TMessage);
var p:TPoint;
begin
 if (Msg.Msg = WM_ICONTRAY) and (Msg.LParam=WM_RBUTTONUP) then
  begin
   GetCursorPos(p);
   SetForegroundWindow(PrivateHandle);
   PopupMenu1.Popup(p.x,p.y);
   PostMessage(PrivateHandle, WM_NULL, 0, 0);
  end;
end;

end.

1
请尽量简化代码,只保留必要的部分以便重现问题。 - David Heffernan
好的,我会尝试。 - Marus Gradinaru
完成了!我成功地编写了一个最小的代码来重现这个问题。 - Marus Gradinaru
我使用Form1的句柄而不是PrivateHandle,现在一切都正常了。 :) 但是在我的组件中,我必须使用私有句柄。那么,是否可以使用私有HWND使其正常工作? - Marus Gradinaru
1个回答

5

我猜你可能忘记调用DefWindowProc了。试试这个:

procedure TForm1.PrivateWndProc(var Msg: TMessage);
begin
  if (Msg.Msg = WM_ICONTRAY) and (Msg.lParam = WM_RBUTTONUP) then
  begin
    ...
  end
  else
    Msg.Result := DefWindowProc(PrivateHandle, Msg.Msg, Msg.wParam, Msg.lParam);
end;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接