| | | | |
Message методы, или обработка сообщений классами | Полный текст материала
Другие публикации автора: Сергей Галездинов
Цитата или краткий комментарий: «... Данная статья предназначения для более глубокого понимания того, как реализована обработка сообщений Windows в VCL и как это можно и нужно использовать в своих целях и использовать правильно. ...» |
Важно:- Страница предназначена для обсуждения материала, его содержания, полезности, соответствия действительности и так далее. Смысл не в разборке, а в приближении к истине :о) и пользе для всех.
- Любые другие сообщения или вопросы, а так же личные эмоции в адрес авторов и полемика, не относящаяся к теме обсуждаемого материала, будут удаляться без предупреждения авторов, дабы не мешать жителям нормально общаться.
- При голосовании учитывайте уровень, на который расчитан материал. "Интересность и полезность" имеет смысл оценивать относительно того, кому именно предназначался материал.
- Размер одного сообщений не должен превышать 5К. Если Вам нужно сказать больше, сделайте это за два раза. Или, что в данной ситуации правильнее, напишите свою статью.
Всегда легче осудить сделанное, нежели сделать самому. Поэтому, пожалуйста, соблюдайте правила Королевства и уважайте друг друга.
Добавить свое мнение.
| | Содержит полезные и(или) интересные сведения | [1] | 5 | 71.4% | | | | Ничего особенно нового и интересного | [2] | 1 | 14.3% | | | | Написано неверно (обязательно укажите почему) | [3] | 1 | 14.3% | | Всего проголосовали: 7 | | | Все понятно, материал читается легко | [1] | 6 | 85.7% | | | | Есть неясности в изложении | [2] | 1 | 14.3% | | | | Непонятно написано, трудно читается | [3] | 0 | 0% | | Всего проголосовали: 7 |
[Структура VCL-приложения] [Отправка и получение сообщений]
Отслеживать это обсуждение
Всего сообщений: 2402-07-2009 04:00В моем случае реализовано в основном потоке, все работает. Можете пояснить, что Вы имели в виду?
Ага, точно. Это вы из другого потока шлете, неверно понял. А имел в виду вот что: если вы создаете окно в другом потоке и в нем же хотите ловить и обрабатывать сообщения, то в коде потока необходимо самому создать цикл извлечения сообщений на GetMessage. Иначе сообщения в очередь складываться будут, а вот извлекаться и доставляться окну - нет. Но это не ваш случай, в главном потоке у вас цикл выборки сообщений уже есть, так что ничего предпринимать по этому поводу не нужно. |
|
02-07-2009 03:42 В моем случае реализовано в основном потоке, все работает. Можете пояснить, что Вы имели в виду? |
|
02-07-2009 02:28Только в этом случае нужно цикл выборки сообщений на Get(Peek)Message в функции потока реализовать самому.
Хотя, это нужно в любом случае. И в первом - тоже, если окно создано неосновным потоком. |
|
01-07-2009 07:01
01-07-2009 06:59 Здорово, когда профессионалы еще и просто хорошие люди! :-) |
|
01-07-2009 06:49Неужели создавать его ручками??
Обычно так и делают: AllocateHWND создает невидимое message-only окошко (вызовите в конструкторе, хэндл сохраните), а в методе, который вы передадите параметром, вызывайте Dispatch. Тогда такому окошку можно и Send и Post слать. PS: Только не забудьте две вещи: вызывать DefWindowProc и по окончанию работы - DeallocateHWND. PPS: можно не создавать окно, а слать сообщения прямо потоку через PostThreadMessage. Только в этом случае нужно цикл выборки сообщений на Get(Peek)Message в функции потока реализовать самому. |
|
01-07-2009 06:47 Да, пришлось пойти по этому пути. Хоть и некрасиво, мне кажется... Смотрю в исходниках, как сделан TForm. Спасибо за ответы! |
|
01-07-2009 06:45сообщение от автора материала что мешает реализовать свое окно? там есть хендл, очередь сообщений будет работать. В чем проблема?
Если интересно - взгляните как реализован в RTL TPopupList к примеру. Общеизвестный прием |
|
01-07-2009 06:33'Ну как бы данный механизм не был предназначен для подобных извратов:)'
Как же в таком случае реализовать сбор и обработку данных? Вроде бы, такая схема часто предлагается... А для SendMessage/PostMessage отсутствует Handle. Неужели создавать его ручками?? |
|
01-07-2009 06:29сообщение от автора материала Ну как бы данный механизм не был предназначен для подобных извратов:) Реализовать очередь можно как угодно. Как фантазия разыграется. Но я бы все-таки порекоммендовал бы юзать SendMessage/PostMessage как более уместные здесь, ибо врядли вы сделаете настолько же thread-safe реализацию |
|
01-07-2009 06:25 Понадобилось для того, чтобы генерировать сообщения из потока, в котором реализовано чтение данных из СОМ-порта. По приходу сообщений нужно инициировать расчеты. А как сделать аналог системной очереди сообщений? Неужели опять ручками, посредством TThread? Где можно посмотреть что-нибудь подобное? Спасибо. |
|
01-07-2009 06:19сообщение от автора материала реализуйте свой аналог системной очереди сообщений, только вместо хендла закладывайтесь на объекты
Только честно говоря непонятно для чего вам это понадобилось |
|
01-07-2009 06:15 Если Dispatch будет работать в контексте вызывающего потока, то он нам не нужен. Как бы соорудить аналог PostMessage для объекта без Хэндла? |
|
07-04-2009 23:182 DRON
Причина такого поведения кроется, скорее всего, в желании повысить производительность.
Дело скорее не в желании, а просто в наличии такой возможности :) Текущий класс на этапе компиляции точно известен, предок - тоже, ничто не мешает вычислить адрес статически, в отличии от вызова виртуального или динамического метода "извне". |
|
16-02-2009 07:44
16-02-2009 06:41Собственная выгода :)
Кстати да :) message-методы - это на самом деле гибкий и элегантный механизм, аналогов которого в других языках я не видел. Он имеет смысл и вне обработки Windows-сообщений, а пишут на эту тему увы очень мало. :( Если отвлечься от того, что это методы для обработки сообщений Windows, и посмотреть на них с другой точки зрения, то возможности открываются очень широкие. А вот собственно эта другая точка зрения:
- Существует возможность ассоциировать определенный метод с целочисленным индексом
- Существует возможность вызова такого метода по индексу, не известному на этапе компиляции
- Метод может быть перекрыт в потомках и его вызов по индексу полиморфен.
Т.е. тут как бы виртуальность в квадрате. Если в случае обычных виртуальных/динамических методов мы знаем на этапе компиляции какой метод мы вызываем, не знаем только какая именно его реализация будет выполнена, то здесь мы на этапе компиляции не то что о реализации, мы не знаем какой из вообще методов будет вызван - это определяется в рантайм.
А с практической точки зрения - это элегантный способ избавления от длинных case-ов, причем с описанием case-а (и возможностью вызова) в базовом предке, а обработкой - в потомках. Такой вот объектно-ориентированный case.
Достаточно того, чтобы структура имела первое поле типа DWORD, чтобы можно было осуществить переход по адресу, равному числовому значению этого поля.
Если копнуть глубже, то даже это условие не обязательно, учитывая что Dispatch - виртуальный ;-) Эх, жаль только что компилятор позволяет ассоциировать message-методы с весьма ограниченным набором значений. |
|
05-02-2009 05:52сообщение от автора материала Теперь автор и копирайт указаны:) даже большими буквами и в начале, так что без паники:) |
|
04-02-2009 11:47
03-02-2009 09:31Было интересно почитать, т.к. сам не пользовался подобным механизмом. |
|
31-01-2009 13:47этот метод располагается в vmt по адресу, кратному числовому значению сообщения, стоящего после ключевого слова message.
Нет, в VMT находится только указатель на DMT, а сама DMT состоит из двух массивов: идентификаторов и указателей на соответствующие методы. Вообще в HelloWorld можно было ничего не писать про внутреннюю организацию, кому надо тот посмотрит исходник, но раз уж написал...
Код примера:
procedure WMNull(var Message: TMessage); message WM_NULL;
Приведёт к ошибке компиляции, причём причины этого описаны в самой статье ;-)
Пусть в обработчике будет произведен вызов inherited. Метод динамический, поэтому будет производиться поиск среди message методов родителя (то есть TControl).
Это не так: для динамических методов компилятор производит связывание inherited ещё на этапе компиляции. То есть, грубо говоря, поиск по DMT производится только внутри Dispatch, а все методы предков (или DefaultHandler в случае их отсутствия) вызываются как обычные статические методы. Причина такого поведения кроется, скорее всего, в желании повысить производительность (сообщений приходит много, а поиск по DMT не быстрый).
Достаточно того, чтобы структура имела первое поле типа DWORD, чтобы можно было осуществить переход по адресу, равному числовому значению этого поля.
Не DWORD, а WORD. Это кстати приводит к "забавным" глюкам: например сообщение посланное таким образом "SendMessage(Handle, WM_USER or $10000, 0, 0);" пройдёт мимо "case Message of ... WM_USER:...", но попадёт в обработчик "...;message WM_USER". Это можно использовать для "пробивания" сообщения через WndProc в ситуациях аналогичных той, что описана в "Подводные камни".
Почему 49151?
Первопричина не в наличии RegisterWindowMessage, а в том что компилятору нужно некоторое количество свободных слотов для обычных (не message) динамических методов. Если не ограничить диапазон для message методов, то Dispatch мог бы приводить к вызову обычных dynamic методов, что не есть хорошо. |
|
31-01-2009 02:56сообщение от автора материала Про MainWndProc - каюсь, mea culpa:)
Про проблемы с сабклассингом - даже если прямой вызов Dispatch и противоречит правильной работе VCL, тем не менее проблема от этого не перестала существовать и нужно быть к этому готовым:)
Правильнее было бы сказать, что Dispatch всегд обрабатывает сообщение в той нити, которая его вызвала, а не в той, которая создала окно.
Ну, это было написано применительно к использованию message-методов для своей выгоды. Это не обязательно должна быть обработка сообщений для окон. Если класс, его данные, является разделяемым ресурсом при использовании в нескольких потоках, то Dispatch будет работать в контексте вызывающего потока, а не в главной нити. То есть ни о какой синхронизации потоков не может быть и речи. Если многие привыкли при использовании окон и SendMessage, что поток будет остановлен и дожидаться, когда сообщение будет обработано в другом потоке, создавшем окно, то тут нужно быть аккуратным и проводить синхронизацию самому. |
|
30-01-2009 23:34Сергей, я ведь вам обещал, что если некоторые принципиальные моменты вы не исправите, мне придётся писать отрицательный отзыв? Обещал. Так что теперь не обижайтесь :)
которая, как известно получается из приведения WndProc к виду, которому требует Windows через MakeObjectInstance
WndProc не "приводится к требуемому виду" с помощью MakeObjectInstance. MakeObjectInstance строит оконную процедуру на основе MainWndProc, а не WndProc. В одном месте вы это исправили, а здесь вот осталось.
В чём противоречия сабклассинга и message-методов, я так и не понял. В нормальных условиях перекрытая оконная процедура срабатывает существенно раньше, чем Dispatch, и если сообщение не доходит до message-метода, значит, так было задумано при перекрытии. Обход же перекрытой оконной процедуры в случае приямого вызова Dispatch проблемой не является, т.к. прямой вызов Dispatch извне для визуального компонента - это уже проблема сама по себе, безотносительно сабклассинга. При этом обходится WndProc, которая содержит важный для правильной работы VCL код.
Dispatch — это не SendMessage, при передаче сообщения классу в потоке с другим контекстом остановки потока не будет!
Не совсем верная фраза. Dispatch не работает асинхронно, он вернёт управление только после того, как сообщение будет полностью обработано. Правильнее было бы сказать, что Dispatch всегд обрабатывает сообщение в той нити, которая его вызвала, а не в той, которая создала окно. |
|
30-01-2009 18:57сообщение от автора материала Собственно, изначально статья так и планировалась :) Дать затравку. Но по ходу написания и исправления статьи приходилось уточнять и уточнять, и в итоге на статью для чего-то большего, чем Hello World это недотянуло, а для Hello World слишком много информации:) А описывать всю работу VCL в этой статье не планировалось - так что извините, первый ком блином:) |
|
30-01-2009 17:04Больше всего мне нравятся статьи из цикла Hello, world!
т.к. обычно туда пишут то, что все знают, все используют, но не все до конца понимают.
а вот чтобы в доступной форме раскрыть подлинную красоту, кот-ую закладывали разработчики, и пишутся такие статьи.
вот тут такого нет. тема - интересная, написано - неплохо. но вот для кого - непонятно. для новичка... - ну только если ему постоянно на пути встречаются фразы типа
private
procedure MyCoolHandler(var Message: TMessage); message WM_USER;, а таких новичков, наверное, немного.
а для того, кто уже задумался - ничего особо нового.
резюме:
в HelloWorld надо выкладывать только основной смысл - что, зачем и (очень приблизительно!) как делается. все остальное человек сам найдет, если тема интересна.
а в следующий уровень - недотягивает.
ЗЫ: афтор, пеши исчо! мы любим читать. и твои статьи нам тоже нравятся. |
|
|
|