WM_MENUSELECT は、ポップアップ メニューのメニュー項目に対しても実際に処理されますが、(ポップアップ) メニューを含むフォームの Windows プロシージャではなく、Menus.PopupList によって作成された非表示のヘルパー ウィンドウによって処理されます。幸いなことに、(少なくとも Delphi 5 では) Menus.PopupList.Window を介してこの HWND にアクセスできます。
たとえば、このCodeGear の記事で説明されているように、ウィンドウをサブクラス化する昔ながらの方法を使用して、ポップアップ メニューの WM_MENUSELECT も処理できるようになりました。HWND は、最初の TPopupMenu が作成された後から、最後の TPopupMenu オブジェクトが破棄される前まで有効です。
質問のリンクされた記事のデモ アプリを使用した簡単なテストにより、これが機能するかどうかが明らかになるはずです。
編集:実際に機能します。リンクされた例を変更して、ポップアップ メニューのヒントも表示するようにしました。手順は次のとおりです。
OnDestroy のハンドラー、古いウィンドウ プロシージャのメンバー変数、および新しいウィンドウ プロシージャのメソッドをフォームに追加します。
TForm1 = class(TForm)
...
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ApplicationEvents1Hint(Sender: TObject);
private
miHint : TMenuItemHint;
fOldWndProc: TFarProc;
procedure WMMenuSelect(var Msg: TWMMenuSelect); message WM_MENUSELECT;
procedure PopupListWndProc(var AMsg: TMessage);
end;
非表示の PopupList ウィンドウをサブクラス化するようにフォームの OnCreate ハンドラーを変更し、OnDestroy ハンドラーでウィンドウ プロシージャの適切な復元を実装します。
procedure TForm1.FormCreate(Sender: TObject);
var
NewWndProc: TFarProc;
begin
miHint := TMenuItemHint.Create(self);
NewWndProc := MakeObjectInstance(PopupListWndProc);
fOldWndProc := TFarProc(SetWindowLong(Menus.PopupList.Window, GWL_WNDPROC,
integer(NewWndProc)));
end;
procedure TForm1.FormDestroy(Sender: TObject);
var
NewWndProc: TFarProc;
begin
NewWndProc := TFarProc(SetWindowLong(Menus.PopupList.Window, GWL_WNDPROC,
integer(fOldWndProc)));
FreeObjectInstance(NewWndProc);
end;
サブクラス化されたウィンドウ プロシージャを実装します。
procedure TForm1.PopupListWndProc(var AMsg: TMessage);
function FindItemForCommand(APopupMenu: TPopupMenu;
const AMenuMsg: TWMMenuSelect): TMenuItem;
var
SubMenu: HMENU;
begin
Assert(APopupMenu <> nil);
// menuitem
Result := APopupMenu.FindItem(AMenuMsg.IDItem, fkCommand);
if Result = nil then begin
// submenu
SubMenu := GetSubMenu(AMenuMsg.Menu, AMenuMsg.IDItem);
if SubMenu <> 0 then
Result := APopupMenu.FindItem(SubMenu, fkHandle);
end;
end;
var
Msg: TWMMenuSelect;
menuItem: TMenuItem;
MenuIndex: integer;
begin
AMsg.Result := CallWindowProc(fOldWndProc, Menus.PopupList.Window,
AMsg.Msg, AMsg.WParam, AMsg.LParam);
if AMsg.Msg = WM_MENUSELECT then begin
menuItem := nil;
Msg := TWMMenuSelect(AMsg);
if (Msg.MenuFlag <> $FFFF) or (Msg.IDItem <> 0) then begin
for MenuIndex := 0 to PopupList.Count - 1 do begin
menuItem := FindItemForCommand(PopupList.Items[MenuIndex], Msg);
if menuItem <> nil then
break;
end;
end;
miHint.DoActivateHint(menuItem);
end;
end;
これは、一致する最初の項目またはサブメニューが見つかるまで、ループ内のすべてのポップアップ メニューに対して行われます。