Уважаемые дзенствующие господа, после долгих и, что самое печальное, бесплодных попыток понять механизм работы стандартного меню (конкретнее: снятие выделения с пункта меню при перемещении указателя мыши за его пределы), обращаюсь к Вам в надежде получить желаемую информацию. Под стандартным меню подразумевается меню, которое создается системой, если при вызове CreateWindowEx() параметр hMenu содержал правильный идентификатор ресурса. Такое меню не является дочерним окном, а отображается как часть неклиентской области, в отличие от ReBar, CoolBar и пр. Техника исследования была следующей: 1. запускался SoftIce, в нем устанавливалась точка останова на функцию SetCapture (а также на всякий случай на TrackMouseEvent и DragDetect), т.к. использование этой функции логично для определения момента выхода указателя за пределы заданной области, 2. запускался стандартный "Калькулятор" Windows (это приложение имеет стандартное меню), 3. запускался Microsoft Spy++, в нем отслеживались ВСЕ сообщения процесса "Калькулятор", 4. окно Spy++ располагалось таким образом, чтобы перекрывать часть пункта меню "Help", видимой оставалась только "Hel". Активным было окно Spy++, 5. указатель мыши перемещался к границе окна Spy++, затем смещался на один пиксель влево и оказывался над пунктом меню "Help" "Калькулятор"а, 6. процесс "Калькулятор"а получал сообщения WM_NCHITTEST, WM_SETCURSOR, WM_NCMOUSEMOVE, и пункт меню "Help" выделялся, при этом не срабатывала ни одна точка останова в SoftIce (т.е. функции из п.1 не вызывались), 7. указатель мыши смещался на один пиксель вправо, тем самым, оказываясь над окном Spy++, и, покидая видимые пределы окна "Калькулятор"а, 8. выделение пункта меню "Help" автоматически снималось, при этом процесс "Калькулятор"а не получал никаких сообщений. Вопрос: каким образом окно получает необходимое для снятия выделения уведомление о том, что указатель мыши покинул пределы меню. Т.к. процесс не получает системных сообщений Windows, логично предположить, что вызывается некая функция обратного вызова. В таком случае интересно, какая функция WinAPI используется для указания callback процедуры. Предположение 2: Неужели функция выделения пунктов стандартного меню реализована в системе как реакция на сообщения драйвера мыши о перемещениях указателя? Интерес к данному механизму вызван необходимостью реализовать "подсветку" различных областей (кнопок) нестандартного окна.
Для начала можно попробовать обработать эти сообщения (то есть не пропустить их в DefWindowProc), и посмотреть, действительно ли в них дело.
Помоему меню в Винде реализоваано какими-то хитрыми внутренними механизмами, как и перетаскивание окна.
Судя по всему, механизм выделения реализован так, что если какое-то окно получило WM_NCHITTEST (WM_MOUSEMOVE?), то при условии, что было выделено меню другого окна, это выделение снимается (в данном случае когда сообщение получал Spy++, то выделение "забиралось" у калькулятора). А за выделение меню точно отвечает WM_NCHITTEST , т.к. если его обработать таким образом: Код (Text): cmp message, WM_NCHITTEST je return_0 то меню не выделяется (и окно ни закрыть, не передвинуть).
UnNamed Не совсем понял вопрос, но может поможет. Вот пример, отличная штука, несмотря на простоту, обнаружил что автор огромную работу провёл. Это пример не стандартного меню Windows, а "собственного" меню построенного на базе CreateWindowEx. _141292152__xpmenu.zip
UnNamed Если речь идёт о пунктах меню верхнего уровня, то там перемещение мыши отслеживаеться через WM_NCHITTEST. Потом отправляеться сообщение в WM_DRAWITEM. Если речь идёт о пунктах меню верхнего уровня, то реально я думаю отследить передвижения. У меня почти получилось. Вот кусок кода: .elseif uMsg == WM_NCHITTEST ; сформируем сообщение ; Win95/98/me and Win NT 4.0 call IsShellType .if eax == WIN_98 || eax == WIN_ME || eax == WIN_95 || eax == WIN_95 || eax == WIN_NT4 invoke CallWindowProc,OldWndProc,hWin,uMsg,wParam,lParam .if eax == HTMENU && fAccel LOWORD lParam mov ptMnuItem.x, eax HIWORD lParam mov ptMnuItem.y, eax mov hMainMenu, $invoke( GetMenu, hWin ) invoke MenuItemFromPoint, hWin, hMainMenu, ptMnuItem.x, ptMnuItem.y mov nItemIndex, eax .if nItemIndex != -1 .if nPrevItemIndex != eax ; PrintDec eax, "WM_NCHITTEST" invoke DrawMenuBarItem, hWin, hMainMenu, nPrevItemIndex, 0 ; Clear the old Item .endif invoke DrawMenuBarItem, hWin, hMainMenu, nItemIndex, ODS_HOTLIGHT m2m nPrevItemIndex, nItemIndex .else invoke DrawMenuBarItem, hWin, hMainMenu, nPrevItemIndex, 0 m2m nPrevItemIndex, -1 .endif mov eax, HTMENU ret .endif .if nPrevItemIndex != -1 && fAccel mov hMainMenu, $invoke( GetMenu, hWin ) invoke DrawMenuBarItem, hWin, hMainMenu, nPrevItemIndex, 0 mov nPrevItemIndex, -1 mov eax, HTMENU ret .endif .endif Код функции DrawMenuBarItem, не привожу, ничего особенного нет. Формирует сообщение WM_DRAWITEM, с соответствующей структурой DRAWITEMSTRUCT. Я это кусок недоделал, потом вылезли глюки. Мне это нужно было, т.к. в Win_ME и ниже, для пунктов верхнего уровня при наведении, не отправляеться сообщение WM_DRAWITEM. В предыдущем примере есть проблемка, автор для пунктов меню верхнего уровня сделал окно. На мой взгляд этого не нужно делать, тогда не узанть когда мышь покидает окно переходя в клиентскую область главного окна. Такое меню даёт немерянно нестандартных возможностей. Я было начал делать подобное меню, но сейчас остановился, наверно не буду доделывать.
Господа! Спасибо за внимание к моему скромному вопросу. Прошу обратить внимание на постановку вопроса - Вопрос: каким образом окно получает необходимое для снятия выделения уведомление о том, что указатель мыши покинул пределы меню. Как отрисовать выделение при наведении ясно как божий день, а вот каким образом получить сигнал о том, что выделение надо снять? В обработчике WM_NCHITTEST это делать некорректно, т.к. WM_NCHITTEST не посылается окну, если указатель мыши из позиции над меню, одномоментно перемещается в точку за пределами окна (владеющего меню). Надеюсь на "вникновение" вами в суть вопроса.