Обычно, когда элемент управления отображает всплывающее меню, WM_INITPOPUPMENU
генерируется сообщение, которое «позволяет приложению модифицировать меню перед его отображением, не изменяя все меню.»
К сожалению, стандартный Win32 Edit управления не генерирует это сообщение для его всплывающего меню по умолчанию, как это было подтверждено в статье MSDN Magazine за ноябрь 2000 года (ссылка на MSDN сам умер, но эта связь с Internet Archive):
MSDN Magazine, November 2000, C++ Q&A:
В: Почему не создается сообщение WM_INITMENUPOPUP при щелчке правой кнопкой мыши на элементе управления редактированием?
A: Я не могу сказать вам, почему его нет, но я могу подтвердить, что это правда ... редактировать элементы управления не отправлять WM_INITMENUPOPUP. Элемент управления редактирования должен вызывать TrackPopupMenu с помощью дескриптора HWND и/или TPM_NONOTIFY, который сообщает меню не отправлять уведомления. Возможно (и снова я только догадываюсь), что авторы пытались повысить производительность за счет сокращения трафика сообщений ... В любом случае, предположим, вы хотите добавить свои собственные пункты меню в контекстное меню управления редактированием. Как ты делаешь это? Увы, у вас нет выбора, кроме как изобретать колесо
Так что единственный вариант доступного подкласса управления редактированием и обработки WM_CONTEXTMENU
сообщения вместо создания и отображения вашего собственного меню настраиваемых всплывающего по мере необходимости. Это означает, что вам необходимо вручную дублировать функциональность любых стандартных элементов меню, которые вы хотите отобразить в своем пользовательском меню.
Обновление: есть способ доступа и изменения стандартного всплывающего меню элемента управления редактированием (я только что протестировал его, и он сработал). TecMan предоставил ссылку на сообщение VBForums discussion, в котором говорится об этом, однако он получает несколько деталей неправильно. Я получил правильные данные от PureBasic forum discussion.
Правильный подход заключается в следующем:
подкласса управление редактированием, чтобы перехватить WM_CONTEXTMENU
сообщений. Можно использовать либо SetWindowSubClass()
, либо SetWindowLongPtr(GWL_WNDPROC)
, хотя the first is preferred.
при получении WM_CONTENTMENU
сообщения, вызовите SetWindowsHookEx()
установить нити локального крюк (использование 0 для параметра и GetCurrentThreadId()
hMod
для параметра dwThreadId
). Можно использовать крючок WH_CBT
или WH_CALLWNDPROC
.Затем отправьте WM_CONTENTMENU
обработчику сообщений по умолчанию через DefSubclassProc()
или CallWindowProc()
, чтобы вызвать стандартное всплывающее меню.
внутри процедуры крюка, когда HCBT_CREATEWND
(WH_CBT
крючка) или WM_CREATE
(WH_CALLWNDPROC
крюка) уведомление получено, передать HWND
при условии, чтобы GetClassName()
. Если имя класса #32768
(стандартное окно имя класса для меню, а documented on MSDN) поста (очень важно!) Сообщение пользовательского окна с помощью PostMessage()
, указав окно меню, то HWND
в сообщении по WPARAM
или LPARAM
параметру, чтобы любыхHWND
, который вы контролируете, например, ваше главное окно, или даже сам элемент редактирования (поскольку он уже подклассифицирован). В следующем шаге вам понадобится HWND
. Вы можете опционально удалить девайс в это время или дождаться выхода DefSubclassProc()
/CallWindowProc()
(он выйдет после того, как меню будет уволено). Вам нужно использовать PostMessage()
, потому что окно меню еще не создало его HMENU
. PostMessage()
задерживает следующий шаг, пока не будет готова HMENU
.
при получении сообщения пользовательского окна, отправить на MN_GETMENU
сообщений в меню для HWND
, что вы получили от крючка с помощью SendMessage()
. Теперь у вас есть меню HMENU
и вы можете делать с ним все, что хотите.
отключить пункты меню Cut
, Copy
и Paste
, вызовите EnableMenuItem()
. Их идентификаторы элементов меню имеют те же значения, что и сообщения WM_CUT
, WM_COPY
и WM_PASTE
(это не документировано Microsoft, но является совместимым в версиях Windows).
Update: Я только что нашел более простое решение (которое также работал, когда я тестировал).
Подкласс управления редактированием для перехвата WM_CONTEXTMENU
, как описано выше.
при получении сообщения, вызовите SetWinEventHook()
установить внутрипотоковой крючок событий (установите параметр hmodWinEventProc
0, параметр idProcess
в GetCurrentProcessId()
, параметр idThread
к GetCurrentThreadId()
и dwFlags
параметр 0 - не WINEVENT_INCONTEXT
!). Задайте параметры eventMin
и eventMax
как EVENT_SYSTEM_MENUPOPUPSTART
, так что это единственное событие, которое вы получаете. Затем отправьте сообщение обработчику по умолчанию, чтобы вызвать всплывающее меню.
когда ваш обратный вызов события вызывается, меню уже полностью инициализировано, так что вы можете отправить MN_GETMENU
сообщений предоставленных HWND
, который будет окном в меню для (idObject
параметра функции обратного вызова будет OBJID_CLIENT
и параметр idChild
будет 0).
при необходимости использовать HMENU
.
отцепить событие крюк, когда он будет выполнен, как описано выше.
Как вы можете видеть здесь, это работает.
Перед изменением меню:
После отключения пунктов меню:
Даже удаление пунктов меню:
Зачем вам это нужно? –
@DavidHeffernan, это требование некоторых конечных пользователей;). В общем случае нам также необходимо добавить наши пользовательские элементы в контекстное меню ОС по умолчанию. – TecMan
Странные пользователи! Вероятно, проще всего заменить все меню своим. –