Учебное пособие: Сообщения и их обработка

BOOL SetMessageQueue(cMaxMsg);

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

Особенности посылки и передачи сообщений в Win32 API

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

Еще одно отличие связано с тем, что размер очереди больше не фиксирован — при необходимости очередь динамически увеличивается. Благодаря этому функция SetMessageQueue больше не применяется.

Возможность применения нескольких интерфейсных потоков в одном процессе приводит дополнительно к отказу от функции PostAppMessage — так как остается неясно, какому потоку надо послать сообщение, и к добавлению функции:

BOOL PostThreadMessage(dwThreadID, uMsg, wParam, lParam);

которая посылает сообщение конкретному потоку. При этом в очередь соответствующего потока помещается сообщение, имеющее нулевой хендл окна–получателя. Идентификатор потока dwThreadID можно получить посредством функций:

DWORD GetWindowThreadProcessId(hWnd, lpdwProcessID);

DWORD GetCurrentThreadId(void);

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

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

Однако в жизни ситуация существенно усложняется: часто поток, обрабатывающий переданное сообщение, передает пославшему потоку какое–либо «встречное» сообщение. Так, например, при начале DDE–разговора DDE–клиент передает с помощью функции SendMessage широковещательное сообщение WM_DDE_INITIATE, а DDE–сервер отвечает на это сообщение передачей сообщения WM_DDE_ACK, если он поддерживает требуемый DDE–разговор. То есть обработчик сообщения WM_DDE_INITIATE сервера передает с помощью той–же функции SendMessage сообщение WM_DDE_ACK окну клиента, которое в данный момент ждет конца обработки переданного им сообщения WM_DDE_INITIATE[3] . При этом было бы возможным зависание обеих потоков, так как поток клиента остановлен до конца обработки переданного им WM_DDE_INITIATE, а поток сервера будет ждать, пока остановленный поток клиента не ответит на встречное сообщение WM_DDE_ACK, которое он передает в качестве подтверждения.

Что бы избежать подобных неприятностей функция SendMessage может обрабатывать сообщения, переданные данному потоку другими потоками, находясь в ожидании ответа от обработчика переданного сообщения.

Осторожно! Зависание потоков при обмене сообщениями все–таки возможно. Такая ситуация легко может случиться при использовании MDI: в Win32 для каждого открытого дочернего MDI–окна автоматически создается свой собственный поток. В этом случае при использовании функции SetFocus для передачи фокуса от одного дочернего MDI–окна другому дочернему MDI–окну (естественно принадлежащему другому потоку) происходит обмен сообщениями WM_SETFOCUS и WM_KILLFOCUS, что приводит к остановке обеих потоков. Эта особенность платформы официально описана Microsoft.

Кроме того возможно зависание потока, передавшего сообщение, если поток–получатель завис сам или занят очень продолжительными операциями. Так как продолжительная остановка передающего сообщение потока может быть нежелательна, то в Win32 API описаны специальные функции для работы с межпотоковыми сообщениями: SendMessageTimeout, SendMessageCallback, SendNotifyMessage и ReplyMessage.

Внимание! Функции SendMessageTimeout, SendMessageCallback и SendNotifyMessage не реализованы для платформы Win32s а, кроме того, в документации (правда не всегда) встречается странное упоминание о том, что в случае Windows–95 параметр wParam является 16ти битовым (!), что может существенно ограничить применение этих функций для передачи многих сообщений.

LRESULT SendMessageTimeout(

hWnd, uMsg, wParam, lParam, fuFlags, uTimeout, lpdwResult);

Дополнительные параметры этой функции: lpdwResult — указывает переменную, в которую будет помещен результат обработки сообщения, uTimeout — максимальное время, в миллисекундах, в течении которого надо ожидать результата, а fuFlags — определяет, как функция будет ожидать ответа:

флаг SMTO_ABORTIFHUNG говорит о том, что если поток–получатель завис (то есть уже более 5 секунд не обрабатывает никаких сообщений), то не ждать результата, а сразу вернуть управление.

флаг SMTO_BLOCK исключает обработку «встречных» сообщений во время ожидания ответа. С этим флагом надо быть очень осторожным!

флагSMTO_NORMALиспользуетсятогда, когданиSMTO_BLOCKниSMTO_ABOPRTIFHUNGнеуказаны.

Возвращаемый функцией результат равен TRUE, если сообщение обработано или FALSE, если отведенное для ожидания время истекло или поток–получатель завис.

Если функция SendMessageTimeout применяется для передачи сообщения окну, созданному тем–же самым потоком, то она работает как обычная функция SendMessage — время ожидания не ограничено и проверка на зависание не выполняется.

Вторая функция SendMessageCallback передает сообщение указанному окну и возвращает управление немедленно после этого, не дожидаясь результата обработки сообщения. Но когда сообщение будет обработано, будет вызвана указанная вами функция, обрабатывающая полученный результат.

BOOL SendMessageCallback(hWnd, uMsg, wParam, lParam, lpfnResultCallback, dwData);

void CALLBACK ResultCallback(hWnd, uMsg, dwData, lResult) {...}

Параметр lpfnResultCallback является указателем на функцию–обработчик результата, а dwData — некоторые дополнительные данные, которые вы хотите передать в вашу функцию–обработчик. Прототип этой функции приведен в следующей строке.

К-во Просмотров: 282
Бесплатно скачать Учебное пособие: Сообщения и их обработка