Смекни!
smekni.com

Сообщения и их обработка (стр. 2 из 4)

Особенности посылки сообщений в Windows API

Помимо рассмотренных функций в Windows API существует еще две функции, посылающие сообщения. С одной из них — PostQuitMessage мы уже встречались в примере 1A. Эта функция посылает сообщение WM_QUIT, которое служит для завершения цикла обработки сообщений.

BOOL PostQuitMessage(wParam);

Сообщение WM_QUIT интересно еще и тем, что оно не посылается никакому окну — хендл окна–получателя равен NULL. В принципе у вас есть возможность самому посылать такие сообщения своему приложению, однако для этого обычная функция PostMessage не подходит. Вместо нее предусмотрена другая функция

BOOL PostAppMessage(hTask, uMsg, wParam, lParam);

Эта функция направляет сообщение приложению, указанному хендлом задачи hTask[2]. Для получения этого хендла можно воспользоваться функциями GetCurrentTask или GetWindowTask.

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

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 — некоторые дополнительные данные, которые вы хотите передать в вашу функцию–обработчик. Прототип этой функции приведен в следующей строке.

Уточнение: реально функция–обработчик результата будет вызвана не немедленно после обработки сообщения получателем, а результат обработки будет помещен в очередь обработанных сообщений, откуда он будет извлечен тогда, когда поток, передавший сообщение, обратиться к очереди сообщений для получения следующего (то есть при вызове GetMessage, PeekMessage, WaitMessage или одну из функций SendMessage...).

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

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

Отдельно стоит рассмотреть вопросы оптимизации обработки межпотоковых сообщений. Дело в том, что WindowsNT может работать на многопроцессорных системах, реально выполняя несколько потоков в одно и то же время. В этом случае может быть целесообразно сократить время, в течении которого один поток просто ожидает результат обработки другого потока. Для этого предназначены две дополнительные функции ReplyMessage и SendNotifyMessage.

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