我会说得对:我通过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。可以选中或取消选中这些项目。
当用户点击我的某个项目时,我希望通过WM_SYSCOMMAND
收到GetMsgProc
消息,但它永远不会被调用。在使用其他消息进行初始化期间,GetMsgProc
会被调用几次。我确实在WM_SYSCOMMAND
中看到CallWndProcRetProc
消息,但它不包含正确的项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'消息。项目选择消息未送达。
我还尝试了对菜单所属的窗口进行子类化,并且我的WndProc从未得到项目选择消息,尽管其他消息如“WM_MENUSELECT”和“WM_UNINITMENUPOPUP”已经发送。
还有哪些指针需要检查?
更新2:
因此,当我对窗口进行子类化/取消子类化时,我会在CallWndProc
挂钩中执行此操作。当我收到WM_INITMENUPOPUP
消息时,我会进行子类化,当我收到关闭菜单的WM_MENUSELECT
消息时(lParam
等于NULL
且HIWORD(wParam)
等于{ {1}})。
我单击系统菜单(此时0xFFFF
被抬起),将鼠标光标移动到包含自定义项目的子菜单中,然后单击其中一个项目。在此过程中,我记录了我在新WndProc中收到的每条消息。以下是我在测试期间在WndProc中收到的消息列表:
WM_INITPOPUPMENU
我希望用户点击该项时看到的消息是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
。我对Windows消息或使用Windows API没有太多经验。这两个中的一个是正确的信息吗?这两个消息都没有,但它应该是,对吧?有什么我想念的吗?
答案 0 :(得分:0)
好的,我明白了。我不知道为什么,但我没有在任何钩子中看到正确的信息。但是,如果我将窗口子类化,我会在WndProc中看到它。在上面的第二个更新中,我说在子类化时我没有看到正确的消息 - 我过早地取消了类别。
所以,这就是我的子类:
if (oldWndProc == -1)
{
oldWndProc = SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)NewWndProc);
currentSubclassedHandle = hwnd;
}
以下是我未取消的方式:
if (oldWndProc != -1)
{
SetWindowLongPtr(currentSubclassedHandle, GWL_WNDPROC, (LONG)oldWndProc);
oldWndProc = -1;
currentSubclassedHandle = NULL;
}
我得到的消息是WM_SYSCOMMAND
,其中wParam
是自定义菜单项ID。在我在第二次更新中发布的最后一条消息之后,此消息会稍后传递到NewWndProc
。