49086 (597427), страница 3
Текст из файла (страница 3)
// дальнейшие продолжительные операции
Такой цикл сначала обработает все сообщения, накопившиеся в очереди, а затем завершиться. При использовании таких циклов надо иметь в виду следующее:
Вы можете ограничить работу интерфейса только каким–либо одним окном, может быть панелью диалога (и всеми дочерними окнами данного окна). Тогда необходимо указать вторым параметром функции 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 обычно проще воспользоваться дополнительными потоками.
Как правило трудоемкость реализации существенно зависит от первоначально поставленной перед разработчиком задачи. Если задача первоначально поставлена так, что выполнение продолжительных операций сразу ориентировано на обработку состояний простоя, то это практически не потребует дополнительных затрат времени и сил. Но переделка уже реализованных в виде одного непрерывного процесса продолжительных операций займет изрядное время и не всегда может быть сделана без кардинальной переделки задачи.
Тщательный анализ создаваемого приложения часто приводит к упрощению поставленной задачи. Так, например, возможность организовать печать документа в фоновом режиме (при обработке состояний простоя или в отдельном потоке), невольно порождает желание воспользоваться этой возможностью “на всю катушку” — разрешить одновременное редактирование документа, его печать, а также печать еще нескольких других документов разом. Дух захватывает от открывающихся возможностей.
Однако задумайтесь над такими вопросами:
если документ и печатается, и редактируется одновременно, то что надо печатать — то, что было в документе на момент начала печати, то что в нем уже есть с учетом сделанных исправлений, или то, что будет, когда исправления закончатся?
как вы себе представляете одновременную печать двух документов на одном принтере?
каковы будут требования к компьютеру, что бы он мог без существенных задержек в работе редактора выполнять печать нескольких документов одновременно?
Конечно, во всех случаях можно найти выходы из создавшегося положения, например: делать копию документа, которая будет отправлена в печать, что позволит продолжить редактирование, использовать разные принтеры для печати разных документов, либо система сама организует спулинг для печати нескольких документов на одном принтере и так далее.
Вопрос в другом — как часто эти возможности будут использоваться и оправдают ли себя существенно возросшие требования к ресурсам, если эти возможности будут использоваться крайне редко?
Вот хороший пример — редактор Notepad крайне прост, возможности его бедны, но он занимает мало ресурсов, быстро загружается сам и быстро считывает текстовые файлы. Редактор WinWord 97 обладает неизмеримо большими возможностями, но и неизмеримо большими требованиями к ресурсам системы, долго загружается сам и достаточно долго (по сравнению с Notepad) загружает редактируемый текст. Теперь вопрос — если вам надо подправить пару строк в autoexec.bat, вы воспользуетесь редактором Notepad или WinWord?
Разрабатывая новое приложение вы должны очень хорошо представлять себе его область применения и круг задач, которые оно должно будет решать. Сент–Экзюпери говорил, что “совершенство достигается не тогда, когда уже нечего добавить, а тогда, когда нечего отнять”. Перегружая задачу разными, может быть и удобными, но редко используемыми функциями, вы рискуете сделать его неудобным при выполнении часто выполняемых функций и, в конечном итоге, бесполезным — так как трудно представить себе более бесполезную вещь, чем та, которой никто не хочет пользоваться.
1 В некоторых руководствах в простейших примерах обходятся без трансляции вообще. Однако это является не совсем корректным, так как функция TranslateMessage распознает комбинацию клавиш Alt+Space как команду нажатия на кнопку системного меню. Конечно без нее приложение будет работать, но не в полной мере реализует стандартный клавиатурный интерфейс.
2 В случае Win32 процесс более передачи сообщений сложный и переданное сообщение может оказаться в очереди, однако обрабатываться такое сообщение не так, как обычные сообщения, находящиеся в той–же очереди. Подробнее — см. ниже.
3 Хендл задачи hTask является самостоятельным понятием, он не совпадает с хендлом копии приложения hInstance. Подробнее о хендле задачи смотри в разделе “Ошибка! Источник ссылки не найден.”.
4 В случае 16ти битовой платформы Windows таких проблем не возникает, так как использование функции SendMessage подобно простому вызову оконной процедуры.