Смекни!
smekni.com

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

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

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

Подробнее о циклах обработки сообщений

Цикл обработки сообщений, в том простейшем виде, который применен в примере 1A, применяется редко. В реальных приложениях этот цикл усложняется. Обычно при этом преследуют следующие задачи:

включить дополнительные средства трансляции сообщений, обычно применяемые акселераторами и немодальными диалогами;

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

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

При рассмотрении двух последних задач следует иметь в виду, что аналогичного эффекта можно добиться применением многопотоковых приложений. Более того, во многих случаях многопотоковый вариант оказывается предпочтительным. При принятии решения необходимо учитывать специфику разрабатываемого приложения и его область применения. Если отказ от устаревшей 16ти разрядной платформы Windows 3.x не вызывает возражений, а также речь идет о разработке нового приложения, а не переносе старого на новую платформу, то стоит серьезно проработать вопрос с реализацией многопотокового приложения.

Дополнительная обработка сообщений

Первая задача — дополнительная обработка сообщений — обычно сводится к выполнению дополнительных действий после извлечения сообщения из очереди и трансляцией этого сообщения. Для применения акселераторов обычно используют функцию TranslateAccelerator или TranslateMDISysAccel, для немодальных диалогов — IsDialogMessage. Часто функции, выполнившие специфичную трансляцию сообщения, заодно передают их оконной процедуре, так что последующая трансляция и вызов DispatchMessage становятся не нужны. Например:

HWND hwndDlg = ...; // хендл окна немодального диалога

MSG msg;

while (GetMessage(&msg, NULL, NULL, NULL)) {

if (!IsWindow(hwndDlg) || !IsDialogMessage(hwndDlg, &msg)) {

TranslateMessage(&msg);

DispatchMessage(&msg);}}

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

Продолжительные операции

Вторая задача — дать возможность нормальной работы интерфейса при выполнении продолжительных операций. Эта задача обычно решается путем встраивания вспомогательных циклов обработки сообщений в процессе выполнения продолжительных операций. Единственное, что при этом требуется — что бы цикл завершался не по получению WM_QUIT, а по завершению обработки последнего сообщения в очереди, что бы приложение могло продолжить свою операцию. Для этого применяют функцию PeekMessage вместо GetMessage.

BOOL PeekMessage(lpMsg, hWnd, uFilterFirst, uFilterLast, fuRemove);

Эта функция осуществляет выборку сообщений из очереди, однако, если очередь пуста, функция PeekMessage просто возвращает FALSE, а не ждет поступления сообщения. Ее параметры аналогичны параметрам функции GetMessage, кроме дополнительного fuRemove. Этот параметр может быть комбинацией флага PM_NOYIELD и одного из флагов PM_REMOVE или PM_NOREMOVE.

Флаг PM_NOYIELD говорит функции о том, что при отсутствии сообщений в очереди нельзя передавать управление другим приложениям. Обычно PeekMessage при отсутствии сообщений в очереди передает управление другим приложениям и, только если их очереди тоже пусты, возвращает FALSE. Таким образом цикл, построенный на PeekMessage без флага PM_NOYIELD, не исключает возможность нормальной работы других приложений.

Флаг PM_NOREMOVE явно указывает, что нужно только проверить наличие сообщений в очереди. Если сообщение есть, возвращается информация об этом сообщении (в структуре MSG), а сообщение остается в очереди. Флаг PM_REMOVE указывает на необходимость извлечения сообщений из очереди.

MSG msg;

// продолжительные операции (вычисления, печать ...)

while (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {

TranslateMessage(&msg);

DispatchMessage(&msg);}

// дальнейшиепродолжительныеоперации

Такой цикл сначала обработает все сообщения, накопившиеся в очереди, а затем завершиться. При использовании таких циклов надо иметь в виду следующее:

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

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

MSG msg;

BOOL fStop = FALSE;

// продолжительные операции (вычисления, печать ...)

while (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {

if (msg.message == WM_QUIT) {

fStop = TRUE;

PostQuitMessage(msg.wParam); // такзавершитсяциклв WinMain

break; // а так — этот}

MyTranslation(&msg); // будем считать, что сюда мы убрали всю

// трансляцию и вызов DispatchMessage}

if (!fStop) {

// дальнейшие продолжительные операции}

Обработка состояний простоя

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

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

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

Для реализации циклов с обработкой состояний простоя обычно применяют одну из двух функций: WaitMessage или GetMessage. Так как с WaitMessage мы еще не встречались, то сначала рассмотрим вариант с ней:

void WaitMessage(void);

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

for (;;) {

while (!PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {

// ... выполнить операции во время простоя

WaitMessage(); // дождаться следующего сообщения}

if(msg.message == WM_QUIT) break; // прервать цикл по WM_QUIT

// нормальная обработка сообщения

TranslateMessage(&msg);

DispatchMessage(&msg);}

Такой цикл применяют обычно непосредственно в функции WinMain, вместо обычного. Надо отметить, что вызов одной функции GetMessage заменился на целый цикл из PeekMessage, необходимых операций и WaitMessage, а также проверку сообщения WM_QUIT. Причем вложенный цикл выполнятся однократно для каждого состояния простоя — после возврата из функции WaitMessage в очереди будет присутствовать сообщение, PeekMessage вернет TRUE и вложенный цикл завершиться.

В принципе, вместо WaitMessage можно использовать GetMessage, примерно так:

for (;;) {

if (!PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {

// ... выполнитьоперациивовремяпростоя

if (!GetMessage(&msg, NULL, NULL, NULL)) break; // WM_QUIT (!)}

if (msg.message == WM_QUIT) break; // прерватьциклпо WM_QUIT

...}

На чем остановиться?

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

Вариант с обработкой состояний простоя существенно более трудоемкий, однако имеет определенные достоинства, связанные с тем, что может быть организовано выполнение нескольких видов продолжительных операций одновременно. Например, можно в состоянии простоя организовать одновременную печать документа на принтере, выполнение сложного математического расчета и, скажем, оптимизацию использования памяти задачей. До определенной степени такая организация приложения позволяет имитировать многопотоковые задачи в рамках одного потока. Это может быть оправдано при разработке приложений, работающих в среде Windows 3.x, не поддерживающей многопотоковые задачи. Для приложений Win32 API обычно проще воспользоваться дополнительными потоками.