Windows 7 - 自定义窗口菜单项:未调用 GetMsgProc 挂钩
我将直奔主题:我通过 Windows 全局挂钩添加到每个窗口的系统菜单中的自定义菜单项不会导致用户单击它时调用 GetMsgProc。
以下是我添加菜单项的方法:
void HookCore::AppendTasksMenu(HWND windowHandle)
{
// make the popup menu and set up the appearance flags for the sub-items
m_mnuMoveToTasks = CreatePopupMenu();
UINT tasksAppearanceFlags = MF_STRING | MF_ENABLED;
//TODO: need to make proper iterator for MenuItemList
list<MenuItemInfo*>::iterator iter;
for (iter = m_menuItems->Begin(); iter != m_menuItems->End(); iter++)
{
// check if we are adding a separator
if ((*iter)->GetSpecial() == MenuItemInfo::SEPARATOR)
{
AppendMenu(m_mnuMoveToTasks, MF_SEPARATOR, (*iter)->GetItemId(), NULL);
}
else
{
AppendMenu(m_mnuMoveToTasks,
((*iter)->IsChecked() ? tasksAppearanceFlags | MF_CHECKED : tasksAppearanceFlags),
(*iter)->GetItemId(), (*iter)->GetItemName().c_str());
}
}
// get the system menu and set up the appearance flag for our new system sub-menu
HMENU mnuSystem = GetSystemMenu(windowHandle, FALSE);
UINT itemAppearanceFlags = MF_STRING | MF_ENABLED | MF_POPUP;
AppendMenu(mnuSystem, MF_SEPARATOR, ID_MOVE_TO_TASK_SEP, NULL);
// append the sub-menu we just created
AppendMenu(mnuSystem, itemAppearanceFlags, (UINT_PTR)m_mnuMoveToTasks, MOVE_TO_TASK);
}
这会在系统菜单中创建一个新的子菜单,并且该子菜单包含我的附加项目。项目 ID 以 1001
开头,每个新项目都会加 1。这些项目可以被选中或取消选中。
当用户单击我的其中一项时,我希望通过 GetMsgProc
收到一条 WM_SYSCOMMAND
消息,但它永远不会被调用。然而,在使用其他消息进行初始化期间,GetMsgProc
确实会被调用几次。我确实在 CallWndProcRetProc 中看到了 WM_SYSCOMMAND 消息,但它不包含正确的项目 ID。我期望从 wParam
的低位字中获取项目 ID(如此处指定:[http://www.codeproject.com/KB/dialog/AOTop.aspx]),但是相反,它只包含 SC_MOUSEMENU
。
以下是我分配 GetMsgProc 挂钩的方法:
myhookdata[GET_MSG_HOOK].nType = WH_GETMESSAGE;
myhookdata[GET_MSG_HOOK].hkprc = GetMsgProc;
myhookdata[GET_MSG_HOOK].hhook = SetWindowsHookEx(
myhookdata[GET_MSG_HOOK].nType,
myhookdata[GET_MSG_HOOK].hkprc,
AppHandle, 0);
有什么想法吗?我的商品 ID 是否有误?获取单击项目的 ID 的正确方法是什么?
谢谢!
更新:
根据下面的建议,我尝试在 CallWndProc、CallWndProcRetProc、GetMsgProc 和 SysMsgProc 中捕获“WM_SYSCOMMAND”和“WM_COMMAND”消息。项目选择消息未发送。
我还尝试对菜单所属的窗口进行子类化,尽管传递了“WM_MENUSELECT”和“WM_UNINITMENUPOPUP”等其他消息,但我的 WndProc 从未收到项目选择消息。
还有其他地方可以检查的指示吗?
更新 2:
因此,当我子类化/取消子类化窗口时,我在 CallWndProc
挂钩中执行此操作。当我收到 WM_INITMENUPOPUP
消息时,我会进行子类化;当我收到用于菜单关闭的 WM_MENUSELECT
消息时,我会取消子类化(当 lParam
等于 NULL 时)
和 HIWORD(wParam)
等于 0xFFFF
)。
我单击系统菜单(此时将引发 WM_INITPOPUPMENU
),将鼠标光标移动到包含自定义项目的子菜单中,然后单击其中一项。在此过程中,我将收到的每条消息记录在新的 WndProc 中。以下是我在此测试期间在 WndProc 中收到的消息列表:
WM_INITMENUPOPUP
147 (0x0093) - what is this message?
148 (0x0094) [9 times] - what is this message?
WM_NCMOUSELEAVE
WM_ENTERIDLE [2 times]
WM_NOTIFY [2 times]
WM_ENTERIDLE [2 times]
WM_NOTIFY
WM_ENTERIDLE [11 times]
WM_MENUSELECT
WM_ENTERIDLE [5 times]
WM_MENUSELECT
WM_ENTERIDLE [6 times]
WM_MENUSELECT
WM_ENTERIDLE [7 times]
WM_MENUSELECT
WM_ENTERIDLE [8 times]
WM_NOTIFY
WM_ENTERIDLE [5 times]
WM_NOTIFY
WM_ENTERIDLE
WM_NOTIFY
WM_ENTERIDLE
WM_UNINITMENUPOPUP
WM_CAPTURECHANGED
当用户单击该项目时,我期望看到的消息是 WM_COMMAND
或 WM_SYSCOMMAND
。我对 Windows 消息或使用 Windows API 没有太多经验。这两条消息之一是需要寻找的正确消息吗?两条消息都不存在,但应该存在,对吗?我有什么遗漏的吗?
I'll get right to the point: a custom menu item that I added to the system menu of every window through Windows global hooks doesn't cause GetMsgProc to be invoked when the user clicks it.
Here is how I add the menu items:
void HookCore::AppendTasksMenu(HWND windowHandle)
{
// make the popup menu and set up the appearance flags for the sub-items
m_mnuMoveToTasks = CreatePopupMenu();
UINT tasksAppearanceFlags = MF_STRING | MF_ENABLED;
//TODO: need to make proper iterator for MenuItemList
list<MenuItemInfo*>::iterator iter;
for (iter = m_menuItems->Begin(); iter != m_menuItems->End(); iter++)
{
// check if we are adding a separator
if ((*iter)->GetSpecial() == MenuItemInfo::SEPARATOR)
{
AppendMenu(m_mnuMoveToTasks, MF_SEPARATOR, (*iter)->GetItemId(), NULL);
}
else
{
AppendMenu(m_mnuMoveToTasks,
((*iter)->IsChecked() ? tasksAppearanceFlags | MF_CHECKED : tasksAppearanceFlags),
(*iter)->GetItemId(), (*iter)->GetItemName().c_str());
}
}
// get the system menu and set up the appearance flag for our new system sub-menu
HMENU mnuSystem = GetSystemMenu(windowHandle, FALSE);
UINT itemAppearanceFlags = MF_STRING | MF_ENABLED | MF_POPUP;
AppendMenu(mnuSystem, MF_SEPARATOR, ID_MOVE_TO_TASK_SEP, NULL);
// append the sub-menu we just created
AppendMenu(mnuSystem, itemAppearanceFlags, (UINT_PTR)m_mnuMoveToTasks, MOVE_TO_TASK);
}
This creates a new sub-menu in the system menu, and the sub-menu contains my additional items. The item IDs start with 1001
, and increment by 1 for each new item. The items can be checked or unchecked.
When a user clicks one of my items I'm expecting to get a WM_SYSCOMMAND
message through GetMsgProc
, but it never gets called. GetMsgProc
does get called however a few times during the initialization with other messages. I do see the WM_SYSCOMMAND
message in CallWndProcRetProc
though, but it doesn't contain the correct item ID. I was expecting to get the item ID from the low-order word of wParam
(as specified here: [http://www.codeproject.com/KB/dialog/AOTop.aspx]), but instead it just contains SC_MOUSEMENU
.
Here is how I assign the GetMsgProc hook:
myhookdata[GET_MSG_HOOK].nType = WH_GETMESSAGE;
myhookdata[GET_MSG_HOOK].hkprc = GetMsgProc;
myhookdata[GET_MSG_HOOK].hhook = SetWindowsHookEx(
myhookdata[GET_MSG_HOOK].nType,
myhookdata[GET_MSG_HOOK].hkprc,
AppHandle, 0);
Any ideas? Are my item IDs wrong? What is the right way to get the ID of the clicked item?
Thanks!
Update:
Based on the suggestions below, I tried catching the 'WM_SYSCOMMAND' and 'WM_COMMAND' messages in CallWndProc, CallWndProcRetProc, GetMsgProc, and SysMsgProc. The item selection message wasn't delivered.
I also tried subclassing the window to which the menu belongs, and my WndProc never got the item selection message, although other messages like 'WM_MENUSELECT' and 'WM_UNINITMENUPOPUP' were delivered.
Any pointers where else to check?
Update 2:
So when I subclass/unsubclass the window, I do it in my CallWndProc
hook. I subclass when I get the WM_INITMENUPOPUP
message, and I unsubclass when I get the WM_MENUSELECT
message for menu closing (when lParam
equals NULL
and HIWORD(wParam)
equals 0xFFFF
).
I click on the system menu (at which point the WM_INITPOPUPMENU
gets raised), move the mouse cursor into my sub-menu that contains the custom items, and then click on one of the items. I log every message I get in my new WndProc during this process. Here is the list of messages I get in my WndProc during this test:
WM_INITMENUPOPUP
147 (0x0093) - what is this message?
148 (0x0094) [9 times] - what is this message?
WM_NCMOUSELEAVE
WM_ENTERIDLE [2 times]
WM_NOTIFY [2 times]
WM_ENTERIDLE [2 times]
WM_NOTIFY
WM_ENTERIDLE [11 times]
WM_MENUSELECT
WM_ENTERIDLE [5 times]
WM_MENUSELECT
WM_ENTERIDLE [6 times]
WM_MENUSELECT
WM_ENTERIDLE [7 times]
WM_MENUSELECT
WM_ENTERIDLE [8 times]
WM_NOTIFY
WM_ENTERIDLE [5 times]
WM_NOTIFY
WM_ENTERIDLE
WM_NOTIFY
WM_ENTERIDLE
WM_UNINITMENUPOPUP
WM_CAPTURECHANGED
The message I'm expecting to see when the user clicks the item is either WM_COMMAND
or WM_SYSCOMMAND
. I don't have much experience with windows messages or working with the Windows API. Is one of those two the right message to look for? Neither message is there, yet it should be, right? Is there something I'm missing?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,我明白了。我不知道为什么,但我在任何钩子中都没有看到正确的消息。但是,如果我对窗口进行子类化,我确实会在 WndProc 中看到它。在上面的第二次更新中,我说我在子类化时没有看到正确的消息 - 我太快取消子类化了。
因此,这就是我子类化的方式:
下面是我取消子类化的方式:
我收到的消息是 WM_SYSCOMMAND,其中
wParam
是自定义菜单项 ID。这条消息在我在第二次更新中发布的最后一条消息之后不久被传递到 NewWndProc。Ok, I figured it out. I'm not sure why, but I don't see the correct message in any of the hooks. I do, however, see it in WndProc if I subclass the window. In the 2nd Update above I said that I don't see the correct message when subclassing - I was unsubclassing too soon.
So, this is how I subclass:
And here is how I unsubclass:
The message I get is
WM_SYSCOMMAND
, where thewParam
is the custom menu item ID. This message gets delivered to theNewWndProc
a little bit after the last message that I posted in the 2nd Update.