0

我已经定义了一个所有者绘制的菜单类。该类考虑了本文MeasureItem定义的系统菜单的问题(即根据菜单是否为系统菜单调整自定义测量值)。但是我遇到的问题是在正确的时间对系统菜单进行子类化。

目前,我已经使用以下两个代码片段来尝试对其进行子类化(在 overriden 中CFrameWnd::LoadFrame):

  1. 尝试1:mSysMenu是的子类CMenu,它也被重写Attach为自动将菜单设置为所有者绘制(bool参数只是传递来通知它是否是系统菜单)

    mSysMenu.Attach(GetSystemMenu(FALSE)->GetSafeHmenu(), true);
    

    当我右键单击任务栏按钮时,这个失败,在wincore.cpp(in CWnd::OnMeasureItem) 中生成 3 个断言和一个丑陋的、变形的所有者绘制的菜单 - 与_AFX_THREAD_STATE类有关。但是,当我从右键单击任务栏之前调用TrackPopupMenu它时,一切都很好。OnLButtonDown

  2. 尝试 2:这只是创建了一个类似变形的菜单测量,但没有任何所有者绘制尝试的迹象

    CVSPMenu* sysMenu = (CVSPMenu*)CMenu::FromHandle(GetSystemMenu(FALSE)->GetSafeHmenu());
    sysMenu->SetOwnderDrawn(true);
    

我也尝试过做事,OnInitMenuPopup但传入的菜单指针似乎并没有使用(只是导致断言和失败)。

所以我的问题是,我如何以及在哪里子类化系统菜单?

4

1 回答 1

0

因为那个 MSDN 链接已经失效,所以我通过 web.archive 将它复活,并在下面粘贴了相关文本。

https://web.archive.org/web/20140413053140/http://blogs.msdn.com/b/oldnewthing/archive/2010/05/28/10016691.aspx

上次我提到在系统菜单的处理上有一个优化,它显着减少了系统中的菜单数量。

当一个窗口具有 WS_SYSMENU 窗口样式时,它就有一个系统菜单,但是直到有人对该窗口调用 GetSystemMenu 之前,没有人知道它的菜单句柄是什么。在那之前,窗口管理器实际上不必承诺为窗口创建菜单。它可以假装窗口有一个。(这种技术被称为延迟初始化。)

窗口管理器创建一个包含标准系统菜单项的全局默认系统菜单。如果有人按下 Alt+Space 或以其他方式调用从未调用过 GetSystemMenu 的窗口的系统菜单,则窗口管理器只使用全局默认系统菜单,因为它知道没有人自定义菜单。(您无法自定义您没有权限的菜单!)由于大多数人从不自定义他们的系统菜单,因此这种优化避免了桌面堆与相同菜单的相同副本的混乱。在 16 位时代,这是一个特别重要的优化,当时所有窗口管理器对象都必须适合单个 64KB 堆(称为系统资源)。

如果你真的很狡猾,你可以瞥见难以捉摸的全局默认系统菜单,因为它嗖嗖作响: 与任何其他弹出菜单一样,正在显示的菜单的句柄被传递给窗口的 WM_INITMENUPOPUP,如果你的程序从未调用 GetSystemMenu,您将看到的句柄是全局默认系统菜单。请注意,您对此菜单无能为力,因为窗口管理器会阻止任何修改它的尝试。(否则,您的程序的菜单修改会对其他程序的菜单产生意想不到的影响!)

因此,如果您的程序习惯于在其 WM_INITMENUPOPUP 处理程序中修改其系统菜单,则应在 WM_CREATE 处理程序中对 GetSystemMenu 进行虚拟调用,以强制系统菜单从假装系统菜单更改为真实菜单。

于 2019-03-30T22:32:16.050 回答