03:28
Пост за сегодня
MFC -- фреймворк устаревший, написанный вразрез с парадигмой ООП. Тем не менее, на нём у нас крутятся проекты. Особенность этого фреймворка в том, что он очень близок к чистому WinAPI. И знание основ WinAPI может помочь определить, в чём проблема.
Евгений Евгеньевич, наш доктор наук, рассказывает, что интуиция -- это, в первую очередь -- опыт. Чем больше различных ситуаций ты видел, тем быстрее ты догадаешься о решении в сходной или даже новой ситуации.
Я так уклончиво начинаю, а что случилось-то? История крайне странная.
Создаю чекбокс. Мне надо, чтобы при клике на чекбокс производились определённые действия. Пишу действия -- это называется "обработчик". Обработчик не вызывается. В окне есть другой чекбокс, со своим обработчиком -- он прекрасно вызывается. Никакой разницы между чекбоксами нет. Переименовал чекбокс, создал новый обработчик -- не помогает. В чём же дело?
Тогда я полез проверять идентификаторы чекбоксов. Идентификатор -- это просто число, которое уникально для каждого элемента управления в окне. Как же происходит нажатие кнопки в WinAPI? (а кнопка и чекбокс с точки зрения WinAPI отличаются слабо).
Когда пользователь нажимает кнопку, генерируется сообщение, например, WM_COMMAND. Это сообщение ставится в очередь сообщений для того окна, где нажали кнопку. Цикл обработки сообщений на очередной итерации узнает, что произошёл щелчок. Но как он узнает, по какой кнопке щёлкнули? А вот у каждого сообщения есть "параметр" и этот параметр содержит идентификатор кнопки.
MFC и вижуал студия хорошо назначают идентификаторы только когда вы рисуете окно с нуля и ничего в нём не переделываете. Много ума для этого не надо. Когда же вы начинаете удалять элементы управления, студия за вами не "подчищает" мусор. Это надо делать самому. И для этого надо понимать, что и где чистить. Ситуация парадоксальная: ломать сложнее, чем строить.
ОКАЗАЛОСЬ
Много лет назад в диалоговом окне, где я рисовал чекбокс, уже была кнопка с идентификатором 1016. Потом её удалили. А идентификатор и обработчик нажатия остались. Когда я добавил новый чекбокс, студия посмотрела, какие идентификаторы не заняты? Ага, 1016 не занят ни одной кнопкой. Ставим его.
И тут в игру вступил притаившийся устаревший обработчик. Хотя у меня и было указано, что вызывать надо новый, но мои указания были в самом конце таблицы вызовов. А при поиске обработчика таблица просматривается с начала. При клике на чекбокс вызывался обработчик старой, давно удалённой кнопки! Ну и когда я его удалил -- всё заработало.
Евгений Евгеньевич, наш доктор наук, рассказывает, что интуиция -- это, в первую очередь -- опыт. Чем больше различных ситуаций ты видел, тем быстрее ты догадаешься о решении в сходной или даже новой ситуации.
Я так уклончиво начинаю, а что случилось-то? История крайне странная.
Создаю чекбокс. Мне надо, чтобы при клике на чекбокс производились определённые действия. Пишу действия -- это называется "обработчик". Обработчик не вызывается. В окне есть другой чекбокс, со своим обработчиком -- он прекрасно вызывается. Никакой разницы между чекбоксами нет. Переименовал чекбокс, создал новый обработчик -- не помогает. В чём же дело?
Тогда я полез проверять идентификаторы чекбоксов. Идентификатор -- это просто число, которое уникально для каждого элемента управления в окне. Как же происходит нажатие кнопки в WinAPI? (а кнопка и чекбокс с точки зрения WinAPI отличаются слабо).
Когда пользователь нажимает кнопку, генерируется сообщение, например, WM_COMMAND. Это сообщение ставится в очередь сообщений для того окна, где нажали кнопку. Цикл обработки сообщений на очередной итерации узнает, что произошёл щелчок. Но как он узнает, по какой кнопке щёлкнули? А вот у каждого сообщения есть "параметр" и этот параметр содержит идентификатор кнопки.
MFC и вижуал студия хорошо назначают идентификаторы только когда вы рисуете окно с нуля и ничего в нём не переделываете. Много ума для этого не надо. Когда же вы начинаете удалять элементы управления, студия за вами не "подчищает" мусор. Это надо делать самому. И для этого надо понимать, что и где чистить. Ситуация парадоксальная: ломать сложнее, чем строить.
ОКАЗАЛОСЬ
Много лет назад в диалоговом окне, где я рисовал чекбокс, уже была кнопка с идентификатором 1016. Потом её удалили. А идентификатор и обработчик нажатия остались. Когда я добавил новый чекбокс, студия посмотрела, какие идентификаторы не заняты? Ага, 1016 не занят ни одной кнопкой. Ставим его.
И тут в игру вступил притаившийся устаревший обработчик. Хотя у меня и было указано, что вызывать надо новый, но мои указания были в самом конце таблицы вызовов. А при поиске обработчика таблица просматривается с начала. При клике на чекбокс вызывался обработчик старой, давно удалённой кнопки! Ну и когда я его удалил -- всё заработало.
15.07.2021 в 13:39
в WinAPI у каждого контрола (кнопка, чекбокс, окно, и т.п.) есть handle, который возвращается при создании этого элемента, поэтому он уникален
его нельзя назначить руками
чтобы что-то сделать с контролом, нужно указать handle при вызове WinAPI-функции
а идентификаторы - это изобретение визуал студии, к WinAPI никакого отношения не имеющее
15.07.2021 в 13:52
15.07.2021 в 14:42
>>в WinAPI у каждого контрола (кнопка, чекбокс, окно, и т.п.) есть handle, который возвращается при создании этого элемента, >>поэтому он уникален
>>его нельзя назначить руками
>>чтобы что-то сделать с контролом, нужно указать handle при вызове WinAPI-функции
Тут всё правильно.
>>а идентификаторы - это изобретение визуал студии, к WinAPI никакого отношения не имеющее
А вот тут ноуп. Просто ноуп.
docs.microsoft.com/en-us/windows/win32/menurc/w...
Пример WM_COMMAND где wParam содержит идентификатор контрола.
docs.microsoft.com/ru-ru/windows/win32/api/winu...
Функция, позволяющая менять идентификатор контрола по его хендлу (GWLP_ID). И да, контролы считаются "дочерними окнами", если что.
stackoverflow.com/questions/20640330/winapi-bn-...
Вот чувак пишет на pure winapi и как раз хочет понять, как определить, какая кнопка кликнута. Ему объясняют про control id в CreateWindowEx.
Для упрощения работы ещё до MFC был изобретён rc-файл и компилятор ресурсов rc.exe (в рамках Windows SDK, который может быть использован без вижуал студии). rc-файл -- текстовый файл, содержащий описания строк, картинок, иконок и разметок диалоговых окон. Там-то и записаны идентификаторы контролов. Потом он компилируется в res-файл, а потом включается в экзешник компоновщиком. Посмотреть содержимое ресурсов можно такой известной программой как Resource Hacker.
Из-за того, что проект вижуал студии включает в себя рц-файл, может сложиться впечатление, что идентификатор контрола -- это действительно сущность, присутствующая только в вижуал студии. Но это не так, как видно из предыдущих примеров.
15.07.2021 в 14:43
>>Долбануться можно с этим вашим программированием!
Верно.
15.07.2021 в 16:17
это не идентификаторы контролов
это константы (из какого-то стандартного .h файла), обозначающие тип ответа пользователя в стандартных диалоговых окнах
idyes idno idcancel и подобные
когда диалоговое окно закрывается, в программу возвращается этот код закрытия
Функция, позволяющая менять идентификатор контрола по его хендлу (GWLP_ID).
эта функция позволяет менять свойства контрола
по индексам GWLP_ID и GWLP_USERDATA лежат пользовательские данные, игнорируемые виндой, юзер может туда занести что угодно
заметь, что тип этих данных - указатель (хотя там можно хранить и произвольное целое число, если ты хитрожопый... ноуп?)
напр, дельфи туда заносит указатель на строку паскаль-идентификатора контрола (напр, "Button1")
мфц, судя по твоему посту, заносит туда какой-то свой числовой идентификатор
кто во что горазд, WinAPI разрешает заносить туда что угодно, винда это всё игнорирует
ну должно же у контрола быть ячейка памяти, где пользователь может хранить указатель на свою связанную с этим контролом инфу (свои кастомные пользовательские свойства)? вот это вот она и есть
то есть, винда разрешает тебе хранить там твой идентификатор, но винда сама никогда твой идентификатор не пытается как-то учитывать, интерпретировать, сравнивать и т.п.
можешь смело хранить там день рождения тёщи )))
Вот чувак пишет на pure winapi и как раз хочет понять, как определить, какая кнопка кликнута. Ему объясняют про control id в CreateWindowEx.
как понять какая кнопка кликнута?
очень просто: в lParam передаётся handle этой кнопки
но мы лёгких путей не ищем - в hMenu суём свою константу (по аналогии как это принято делать для кодов закрытия диалогового окна), мамкины хакеры )))
но этот хак никак не узаконивает "идентификатор контрола"!
винда-то думает, что передаёт хэндл меню ))))
WinAPI по-прежнему ничего не знает про твой идентификатор, как бы тебе ни хотелось поверить в обратное
15.07.2021 в 17:46
Идентификаторы. В этом можно убедиться, изучив полностью пример, откуда взят тот отрывок кода:
github.com/microsoft/Windows-classic-samples/tr...
>>по индексам GWLP_ID и GWLP_USERDATA лежат пользовательские данные, игнорируемые виндой
GWLP_USERDATA -- верю. А вот что ID игнорируется виндой -- докажи.
>>напр, дельфи туда заносит указатель на строку паскаль-идентификатора контрола
Куда заносит, в юзер-дата? Или в айди? Где про это почитать?
>>очень просто: в lParam передаётся handle этой кнопки
Если ты создаёшь каждый контрол отдельно, через CreateWindowEx, то ты можешь иметь хендлы и сравнивать их. Однако если создание идёт из ресурсов, то как ты узнаешь хендлы?
>>но мы лёгких путей не ищем - в hMenu суём свою константу
Это не я придумал и не MFC, это написано в официальной документации:
docs.microsoft.com/en-us/windows/win32/api/winu...
hMenu
Type: HMENU
A handle to a menu, or specifies a child-window identifier, depending on the window style. For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; it can be NULL if the class menu is to be used. For a child window, hMenu specifies the child-window identifier, an integer value used by a dialog box control to notify its parent about events. The application determines the child-window identifier; it must be unique for all child windows with the same parent window.
15.07.2021 в 21:54
Идентификаторы. В этом можно убедиться, изучив полностью пример
да щас! полезу я бесплатно изучать чужой говнокод )))
начнём с определения понятий: в чём по твоему мнению отличие идентификаторов от констант?
А вот что ID игнорируется виндой -- докажи.
пасиба, поржал с твоей логики )))
если создание идёт из ресурсов, то как ты узнаешь хендлы?
ах, ты думаешь там магия, они создаются сами собою ))))
нет, мфц-шная либа разбирает руками ресурсы и создаёт контролы (и вписывает в свойства контролов свои идентификаторы, чтобы позже задействовать их в своей логике, про которую WinAPI ничего не знает)
> an integer value used by a dialog box control
ну вот, ты сам нашёл нужный кусок теста, который всё объясняет
специально для стандартных диалоговых окон сделано исключение - так как в их контролах hMenu всегда NULL, то там договорились хранить код возврата диалогового окна (стандартный набор кодов возврата: да/нет/отмена, константы прописаны в .h-файле)
для недиалоговых окон такой договорённости нет
а поскольку hMenu иногда всё-таки не NULL, то никакого универсального "ид контрола" там быть и не может
иногда там всё-таки должен лежать хендл меню
всё ещё не веришь?
тогда подумай, зачем в цитату из документации, выделенную жирным, вставили фразу "a dialog box control", если по твоему мнению эта хрень сделана для любых контролов вообще, а диалоговое окно - это просто случайный пример использования?
16.07.2021 в 04:53
- Say it!
- ...your kung-fu is the strogest.