Сообщение WM_NCHITTEST

Тема в разделе "WASM.BEGINNERS", создана пользователем Mikl___, 25 окт 2025.

  1. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229
    Перечитываю братьев Фроловых "Библиотека системного программиста", книга 11 "Операционная система Microsoft Windows 3.1 для программиста", глава 6 "Мышь"
    Полный список сообщений, поступающих от мыши
    СообщениеhexОписание
    WM_MOUSEACTIVATE
    21​
    Нажата клавиша мыши над неактивным окном
    WM_NCHITTEST
    84​
    Перемещение мыши в любом месте экрана
    WM_NCMOUSEMOVE
    A0​
    Перемещение курсора мыши во внешней области окна
    WM_NCLBUTTONDOWN
    A1​
    Нажата левая клавиша мыши во внешней области окна
    WM_NCLBUTTONUP
    A2​
    Отпущена левая клавиша мыши во внешней области окна
    WM_NCLBUTTONDBLCLK
    A3​
    Двойной щелчок левой клавишей мыши во внешней (non-client) области окна
    WM_NCRBUTTONDOWN
    A4​
    Нажата правая клавиша мыши во внешней области окна
    WM_NCRBUTTONUP
    A5​
    Отпущена правая клавиша мыши во внешней области окна
    WM_NCRBUTTONDBLCLK
    A6​
    Двойной щелчок правой клавишей мыши во внешней области окна
    WM_NCMBUTTONDOWN
    A7​
    Нажата средняя клавиша мыши во внешней области окна
    WM_NCMBUTTONUP
    A8​
    Отпущена средняя клавиша мыши во внешней области окна
    WM_NCMBUTTONDBLCLK
    A9​
    Двойной щелчок средней клавишей мыши во внешней области окна
    WM_NCXBUTTONDOWN
    AB​
    WM_NCXBUTTONUP
    AC​
    WM_NCXBUTTONDBLCLK
    AD​
    WM_MOUSEMOVE
    WM_MOUSEFIRST
    200​
    Перемещение курсора мыши во внутренней области окна
    WM_LBUTTONDOWN
    201​
    Нажата левая клавиша мыши во внутренней области окна
    WM_LBUTTONUP
    202​
    Отпущена левая клавиша мыши во внутренней области окна
    WM_LBUTTONDBLCLK
    203​
    Двойной щелчок левой клавишей мыши во внутренней (client) области окна
    WM_RBUTTONDOWN
    204​
    Нажата правая клавиша мыши во внутренней области окна
    WM_RBUTTONUP
    205​
    Отпущена правая клавиша мыши во внутренней области окна
    WM_RBUTTONDBLCLK
    206​
    Двойной щелчок правой клавишей мыши во внутренней области окна
    WM_MBUTTONDOWN
    207​
    Нажата средняя клавиша мыши во внутренней области окна
    WM_MBUTTOMUP
    208​
    Отпущена средняя клавиша мыши во внутренней области окна
    WM_MBUTTONDBLCLK
    WM_MOUSELAST
    209​
    Двойной щелчок средней клавишей мыши во внутренней области окна
    WM_MOUSEWHEEL
    20A​
    WM_XBUTTONDOWN
    20B​
    WM_XBUTTONUP
    20C​
    WM_XBUTTONDBLCLK
    20D​
    WM_NCMOUSEHOVER
    2A0​
    WM_MOUSEHOVER
    2A1​
    WM_NCMOUSELEAVE
    2A2​
    WM_MOUSELEAVE
    2A3​
    Из приведенных 22 сообщений ― 21 сообщение образуется из сообщения WM_NCHITTEST. Это сообщение генерируется драйвером мыши при любых перемещениях мыши. Сообщение WM_NCHITTEST не использует параметр wParam. (Выделенное красным это цитата Фроловых, далее пытаюсь проверить так ли это экспериментальным путем). В младшем слове параметра lParam передается X координата курсора мыши, в старшем ― Y координата. Координаты вычисляются относительно верхнего левого угла экрана. Объединяем клиентские и неклиентские сообщения
    binСообщение
    Client Area=2
    /non-client=0
    Client Area=0
    /non-client=0xA
    0010/0000​
    0000/1010​
    0000MOUSEMOVE
    0010/0000​
    0000/1010​
    0001LBUTTONDOWN
    0010/0000​
    0000/1010​
    0010LBUTTONUP
    0010/0000​
    0000/1010​
    0011LBUTTONDBLCLK
    0010/0000​
    0000/1010​
    0100RBUTTONDOWN
    0010/0000​
    0000/1010​
    0101RBUTTONUP
    0010/0000​
    0000/1010​
    0110RBUTTONDBLCLK
    0010/0000​
    0000/1010​
    0111MBUTTONDOWN
    0010/0000​
    0000/1010​
    1000MBUTTOMUP
    0010/0000​
    0000/1010​
    1001MBUTTONDBLCLK
    0010/0000
    0000/0000
    1010MOUSEWHEEL
    0010/0000​
    0000/1010​
    1011XBUTTONDOWN
    0010/0000​
    0000/1010​
    1100XBUTTONUP
    0010/0000​
    0000/1010​
    1101XBUTTONDBLCLK
    0010/0000
    1010​
    0000/
    0001
    MOUSEHOVER
    0010/0000
    1010​
    0010/
    0011
    MOUSELEAVE
    Сообщение WM_NCHITTEST передается функции DefWindowProc. Функция DefWindowProc определяет положение курсора мыши относительно расположенных на экране объектов и возвращает одно из значений (раздел dec ― Фроловы, раздел hex ― то что подтверждает эксперимент)
    ЗначениеdechexРасположение курсора мыши
    HTERROR
    -2​
    Над поверхностью экрана или на линии, разделяющей различные окна. Дополнительно функция DefWindowProc выдает звуковой сигнал
    HTTRANSPARENT
    -1​
    В окне, которое перекрыто другим окном
    HTNOWHERE
    0​
    Над поверхностью экрана или на линии, разделяющей различные окна
    HTCLIENT
    1​
    1​
    Во внутренней области окна (client area)
    HTCAPTION
    2​
    На заголовке окна (title-bar)
    HTSYSMENU
    3​
    В области системного меню
    HTGROWBOX,
    HTSIZE
    4​
    В области изменения размера окна (size box)
    HTMENU
    5​
    В области меню
    HTHSCROLL
    6​
    На горизонтальной полосе просмотра
    HTVSCROLL
    7​
    На вертикальной полосе просмотра
    HTMINBUTTON,
    HTREDUCE
    8​
    На кнопке минимизации
    HTMAXBUTTON,
    HTZOOM
    9​
    На кнопке максимизации
    HTLEFT,
    HTSIZEFIRST
    10​
    A​
    За левой вертикальной линией рамки окна
    HTRIGHT
    11​
    B​
    За правой вертикальной линией рамки окна
    HTTOP
    12​
    C​
    За верхней горизонтальной линией рамки окна
    HTTOPLEFT
    13​
    В верхнем левом углу рамки окна
    HTTOPRIGHT
    14​
    В правом верхнем углу рамки окна
    HTBOTTOM
    15​
    F​
    За нижней горизонтальной линией рамки окна
    HTBOTTOMLEFT
    16​
    10​
    За левым нижним углом рамки
    HTBOTTOMRIGHT,
    HTSIZELAST
    17​
    11​
    За правым нижним углом рамки
    HTBORDER
    18​
    На рамке окна, которое создано без толстой рамки, предназначенной для изменения размера окна
    HTOBJECT
    19​
    HTCLOSE
    20​
    На кнопке ×
    HTHELP
    21​
    На кнопке Help
    Как генерируется WM_MOUSEMOVE понятно. DefWindowProc возвращает HTCLIENT, в lParam координаты курсора. Вопрос в том, КАК генерируются L/M/X/RBUTTONDOWN/UP/DBLCLK? По каким признакам DefWindowProc определяет ЧТО в клиентской области щелкали клавишами мыши? Ведь WM_NCHITTEST не использует wParam. При приходе WM_NCHITTEST wParam=0. А ведь при других сообщениях в параметре wParam содержится значение, с помощью которого можно определить, какие клавиши на мыши и клавиатуре были нажаты в тот момент, когда произошло событие, связанное с сообщением.
    Параметр wParam состоит из отдельных битовых флагов.
    ЗначениеhexbinОписание
    X2X1MCtrlShiftRightLeft
    MK_LBUTTON
    1​
    000
    0​
    0​
    0​
    1​
    Нажата левая клавиша мыши
    MK_RBUTTON
    2​
    000
    0​
    0​
    1​
    0​
    Нажата правая клавиша мыши
    MK_SHIFT
    4​
    000
    0​
    1​
    0​
    0​
    На клавиатуре нажат Shift
    MK_CONTROL
    8​
    000
    1​
    0​
    0​
    0​
    На клавиатуре нажат Ctrl
    MK_MBUTTON
    10​
    001
    0​
    0​
    0​
    0​
    Нажата средняя клавиша мыши
    MK_XBUTTON1
    20​
    010
    0​
    0​
    0​
    0​
    Нажата клавиша XBUTTON1
    MK_XBUTTON2
    40​
    100
    0​
    0​
    0​
    0​
    Нажата клавиша XBUTTON2
     
    Последнее редактирование: 3 ноя 2025
  2. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    223
    А разве WM_LBUTTONDOWN/WM_RBUTTONDOWN/ WM_RBUTTONDOWN происходят из WM_NCHITTEST? Для происхождения этих сообщений есть соответствующие non client сообщения. WM_NCLBUTTONDOWN/WM_NCRBUTTONDOWN/ WM_NCRBUTTONDOWN

    Т.е. WM_NCHITTEST только подсказывает куда перенаправить сообщение. А само сообщение уже идет следом и превращается либо в WM_LBUTTONDOWN либо остается WM_NCLBUTTONDOWN.
     
  3. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229
    MaKsIm,
    я цитирую Фроловых
    А Фроловы вытащили/перевели эту фразу из недр документации о Windows. Часть сообщений я могу сымитировать используя WM_NCHITTEST и реакцию DefWindowProc. Часть не понимаю как, к ним относятся реакции на L/R/M/XBUTTONDOWN/UP/DBLCLK Хотелось бы узнать о предположениях форумчан...
     
  4. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    223
    Вот хотелось бы видеть эти недра доки Windows. Тут разве что информация дополняется из глобальных системных переменных из сегмента fs после получения информации от WM_NCHITTEST. Иначе информации привязанной к непосредственному состоянию системы в конкретный момент времени взяться неоткуда.

    Я же полагаю, что сообщение L/R/M/XBUTTONDOWN/UP/DBLCLK уже есть, но по родительскому handle (desktop), которое порождает WM_NCHITTEST для окна текущего приложения и после превращается в одно из событий.
     
  5. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229
    MaKsIm,
    очень возможно, что вы правы. Я написал крохотную программу. Сообщение WM_NCHITTEST появляется при движении мыши и предваряет появление WM_MOUSEMOVE. Щелчки к появлению WM_NCHITTEST перед сообщениями L/R/M/XBUTTONDOWN/UP/DBLCLK не приводят.
     
  6. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    223
    Ну пока все сходится. Сначала, при движении мыши, DWM опрашивает окна на предмет попадания в их области. После же при событии клика просто использует последнее полученное значение из WM_NCHITTEST для перенаправления события клика.
    --- Сообщение объединено, 26 окт 2025 ---
    Вообще совершить одновременно передвижение курсора мыши и клик для человека попросту невозможно. Но, предположу, что ответ кешируется вместе с координатами из MOUSEMOVE и попросту подавляется повторное сообщение NCHITTEST. Для мыши это подавляет лишний опрос окна, а вот для тачскрина приходится слать NCHITTEST перед каждым событием.
     
    Mikl___ нравится это.
  7. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    605
    MaKsIm

    > информация дополняется из глобальных системных переменных из сегмента fs

    Это врядле. Там только механизм пакетной обработки есть, ничего туда кернел не пишет(лишь несколько переменных к гую не относящихся). Это проверено визорами(гуи апп на каждой итерации секвенации(на каждой инструкции и выборке в память, после сисколов втч) сравнивает дамп сегмента).

    Там можно найти двойной линк на известные таблицы apfndispatch.

    Но это вроде в другом билде можно смотреть, оптимизация - любой шадов сервисный вызов рисует текст, окна и тп. Что бы ядро лишний раз не дергать.
     
    Последнее редактирование: 29 окт 2025
    Mikl___ нравится это.
  8. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229
    Ahimov,
    если братья Фроловы правы и Из приведенных 22 сообщений ― 21 сообщение образуется из сообщения WM_NCHITTEST, то как выдернуть сообщения типа WM_NCLBUTTON/WM_LBUTTON из WM_NCHITTEST и DefWindowProc? Хотя бы намекните как?
     
    Ahimov нравится это.
  9. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    605
    Такой четкий вопрос, респект,

    Текли сурки не только эти. Где это на гх с мобилы искать я не знаю.

    Поднимите тему по утекшим когда то исходам, думаю вам помогут.

    Физически два харда заныканы за 200км, даже если я туда поеду, меня с этим возьмут будет беда.

    В какой сборке были lsass msgina etc я не знаю, но это было давно очень и паблик.

    Я это пишу с телефона что бы вы понимали.
    --- Сообщение объединено, 1 ноя 2025 ---
    Откуда эти сообщения берутся можно глянуть и юзер отладчиком, не нужен ядерный. Окна э́то юзер, зачем знать как это реализовано в кернел, если вы не ищите уязвимости, те анализ работы этого механизма.
    --- Сообщение объединено, 1 ноя 2025 ---
    Пролистал сурки, есть только case WM_NCHITTEST. Загрузку константы я не нашел, мб это:

    Код (Text):
    1. if (HTCLIENT == (int)SendMessage(_hwnd, WM_NCHITTEST, 0, lParam))
    2.     {
    - это уже не системная обработка, те это не ядро/тень/user/gui а сторонние либы или апп.
     
    Последнее редактирование: 1 ноя 2025
  10. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229
    Чарльз Петцольд «Программирование для Windows 95» в двух томах. Том I. Глава 6. Мышь
    Сообщение теста попадания
    Если вы вели подсчет, то знаете, что мы рассмотрели 20 из 21 сообщения мыши. Последним сообщением является WM_NCHITTEST, означающее "тест попадания в нерабочую область" (nonclient hit-test). Это сообщение предшествует всем остальным сообщениям мыши рабочей и нерабочей области. Параметр lParam содержит значения х и у экранных координат положения мыши. Параметр wParam не используется.
    В приложениях для Windows это сообщение обычно передается в DefWindowProc. В этом случае Windows использует сообщение WM_NCHITTEST для выработки всех остальных сообщений мыши на основе положения мыши. Для сообщений мыши нерабочей области возвращаемое значение функции DefWindowProc при обработке сообщения WM_NCHITTEST передается как параметр wParam в сообщении мыши. Это значение может быть любым из множества значений wParam, которое бывает у этого параметра для сообщений мыши нерабочей области, плюс следующие:
    HTCLIENTРабочая область
    HTNOWHEREНет ни на одном из окон
    HTTRANSPARENTОкно перекрыто другим окном
    HTERRORЗаставляет DefWindowProc генерировать гудок
    Если функция DefWindowProc после обработки сообщения WM_NCHITTEST возвращает значение HTCLIENT, то Windows преобразует экранные координаты в координаты рабочей области вырабатывает сообщение мыши рабочей области.
    Если вы вспомните, как мы запретили все системные функции клавиатуры при обработке сообщения WM_SYSKEYDOWN, то вы, наверное, удивитесь, если сможете сделать что-нибудь подобное, используя сообщения мыши. Действительно, если вы вставите строки:
    Код (C):
    1. case WM_NCHITTEST:
    2. return(LRESULT) HTNOWHERE;
    в вашу оконную процедуру, то вы полностью запретите все сообщения мыши рабочей и нерабочей области вашего окна. Кнопки мыши просто не будут работать до тех пор, пока мышь будет находится где-либо внутри вашего окна, включая значок системного меню, кнопки минимизации, максимизации и закрытия окна.
    Сообщения порождают сообщения
    Windows использует сообщение WM_NCHITTEST для выработки всех остальных сообщений мыши. Идея сообщений, порождающих другие сообщения, характерна для Windows. Давайте рассмотрим пример. Если вы дважды щелкните мышью на значке системного меню Windows-программы, то программа завершится. Двойной щелчок генерирует серию сообщений WM_NCHITTEST. Поскольку мышь установлена над значком системного меню, то возвращаемым значением функции DefWindowProc является HTSYSMENU, и Windows ставит в очередь сообщение WM_NCLBUTTONDBLCLK с параметром wParam, равным HTSYSMENU.
    Оконная процедура обычно передает это сообщение DefWindowProc. Когда функция DefWindowProc получает сообщение WM_NCLBUTTONDBLCLK с параметром wParam, равным HTSYSMENU, то она ставит в очередь сообщение WM_SYSCOMMAND с параметром wParam, равным SC_CLOSE. (Это сообщение WM_SYSCOMMAND также генерируется, когда пользователь выбирает в системном меню пункт Close.) Оконная процедура вновь передает это сообщение в DefWindowProc. DefWindowProc обрабатывает сообщение, отправляя оконной процедуре синхронное сообщение WM_CLOSE.
     
    Последнее редактирование: 3 ноя 2025
    Research и MaKsIm нравится это.
  11. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    605
    Пример. Оконная процедура:
    Код (Text):
    1. WM_LBUTTONDOWN:
    2.    GetWindowRect()
    3.    if !PtInRect()
    4.       SendMessage( WM_NCLBUTTONDOWN)
    xxxButtonEvent(RIT) rit это поток сырого ввода. Вот как возникают мышиные сообщения:
    Код (C):
    1. switch (ButtonNumber) {
    2.     case MOUSE_BUTTON_RIGHT:
    3.         if (fBreak) {
    4.             message = WM_RBUTTONUP;
    5.         } else {
    6.             if (ISTS() && fDblClk)
    7.                 message = WM_RBUTTONDBLCLK;
    8.             else
    9.                 message = WM_RBUTTONDOWN;
    10.         }
    11.         break;
    Из SendInput() или непосредственно от RawInputThread.

    Так что wm_ncX noclient msg приложение шлет само себе.
     
    Mikl___ нравится это.