Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming_Windows_95_Part_I.pdf
Скачиваний:
96
Добавлен:
05.06.2014
Размер:
4.61 Mб
Скачать

37

Сложности программирования для Windows

Даже с учетом авторских пояснений структура и принципы работы программы Hellowin, вполне вероятно, так и останутся для вас немного загадочными. В короткой программе на С, написанной для обычной среды, вся программа целиком может поместиться в функции main. В HELLOWIN WinMain содержит только надстройку программы, необходимую для регистрации класса окна, создания окна и получения и передачи сообщений из/в очередь сообщений.

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

Не вызывай меня, я вызову тебя

Как уже упоминалось, программисты хорошо знакомы с понятием вызова операционной системы для выполнения каких-то действий. Например, программисты на С используют функцию fopen для открытия файла. Библиотечные функции, поставляемые с компилятором, содержат код, который фактически вызывает для открытия файла операционную систему. Здесь все просто.

Но операционная система Windows ведет себя иначе. Хотя в Windows имеется свыше тысячи доступных программисту функций, Windows также и сама посылает вызовы вашей программе, особенно оконной процедуре, которую мы назвали WndProc. Оконная процедура связана с классом окна, который программа регистрирует с помощью вызова функции RegisterClassEx. Окно, создаваемое на основе этого класса, использует оконную процедуру для обработки всех сообщений окна. Windows посылает сообщения окну, вызывая оконную процедуру.

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

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

Фактически, идея функции, находящейся в программе, но которая вызывается не из самой программы, не является абсолютно новой в традиционном программировании. Функция signal в С может перехватить <Ctrl>+<Break>. Вы можете иметь опыт с перехватом аппаратных прерываний с помощью языка ассемблера или одной из конструкций ON в Microsoft BASIC. Драйвер мыши фирмы Microsoft позволяет работать с этой мышью программам, сделанным не для Windows.

В Windows эта идея расширена и пронизывает всю систему. Любое событие, относящееся к окну, передается оконной процедуре в виде сообщения. Затем оконная процедура соответствующим образом реагирует на это сообщение или передает сообщение в DefWindowProc для обработки его по умолчанию.

Параметры wParam и lParam оконной процедуры не используются в HELLOWIN кроме как параметры для DefWindowProc. Эти параметры дают оконной процедуре дополнительную информацию о сообщении. Значение этих параметров зависит от самого сообщения.

Давайте рассмотрим пример. Когда меняется размер рабочей области окна, Windows вызывает оконную процедуру. Параметр hwnd оконной процедуры — это описатель окна, изменившего размер. Параметр iMsg равен WM_SIZE. Параметр wParam для сообщения WM_SIZE равен одной из величин SIZENORMAL, SIZEICONIC, SIZEFULLSCREEN, SIZEZOOMSHOW или SIZEZOOMHIDE (определяемых в заголовочных файлах Windows как числа от 0 до 4). Параметр wParam показывает, будет ли окно свернуто, развернуто или скрыто (в результате развертывания другого окна). Параметр lParam определяет новый размер окна. Новая ширина (16-разрядное значение) и новая высота (16-разрядное значение) объединяются вместе в 32-разрядный параметр lParam. В заголовочных файлах Windows имеется макрос, который позволяет выделить оба эти значения из lParam. Мы это проделаем в следующей главе.

Иногда, в результате обработки сообщения функцией DefWindowProc, генерируются другие сообщения. Например, предположим, что вы запускаете HELLOWIN и выбираете Close из системного меню программы, используя клавиатуру или мышь. DefWindowProc обрабатывает эту информацию. Когда она определяет, что вы выбрали опцию Close, то отправляет сообщение WM_SYSCOMMAND оконной процедуре. WndProc передает это сообщение DefWindowProc. DefWindowProc реагирует на него, отправляя сообщение WM_CLOSE оконной процедуре. WndProc снова передает это сообщение DefWindowProc. DefWindowProc реагирует на сообщение

38

WM_CLOSE, вызывая функцию DestroyWindow. DestroyWindow заставляет Windows отправить сообщение WM_DESTROY оконной процедуре. И наконец, WndProc реагирует на это сообщение, вызывая функцию PostQuitMessage путем постановки сообщения WM_QUIT в очередь сообщений. Это сообщение прерывает цикл обработки сообщений в WinMain и программа заканчивается.

Синхронные и асинхронные сообщения

Ранее было рассказано о передаче окну сообщений, что означает вызов операционной системой Windows оконной процедуры. Но в программах для Windows имеется цикл обработки сообщений, который берет сообщения из очереди сообщений, вызывая функцию GetMessage, и отправляет их оконной процедуре, вызывая функцию

DispatchMessage.

Так что же, буферизуются ли сообщения для Windows-программы (так же как в обычной программе буферизуется ввод с клавиатуры) и затем пересылаются дальше, или она (программа для Windows) получает сообщения непосредственно снаружи? И так, и этак.

Одни и те же сообщения могут быть и "синхронные" (queued), и "асинхронные" (nonqueued)1. Синхронными сообщениями называются сообщения, которые Windows помещает в очередь сообщений программы, и которые извлекаются и диспетчеризуются в цикле обработки сообщений. Асинхронные сообщения передаются непосредственно окну, когда Windows вызывает оконную процедуру. В результате оконная процедура получает все предназначенные для окна сообщения, как синхронные, так и асинхронные. Структура программ для Windows очень проста, поскольку у них имеется только одно центральное место обработки сообщений. Говорят, что синхронные сообщения помещаются в очередь сообщений (post), а асинхронные посылаются прямо в оконную процедуру (send).

Синхронными становятся сообщения, в основном, тогда, когда они являются результатом пользовательского ввода путем нажатия клавиш (например, WM_KEYDOWN и WM_KEYUP), это символы, введенные с клавиатуры

(WM_CHAR), результат движения мыши (WM_MOUSEMOVE) и щелчков кнопки мыши (WM_LBOTTONDOWN).

Кроме этого синхронные сообщения включают в себя сообщение от таймера (WM_TIMER), сообщение о необходимости плановой перерисовки (WM_PAINT) и сообщение о выходе из программы (WM_QUIT). Сообщения становятся асинхронными во всех остальных случаях. Часто асинхронные сообщения являются результатом синхронных. При передаче асинхронного сообщения в DefWindowProc из оконной процедуры, Windows часто обрабатывает сообщение, отправляя оконной процедуре другие асинхронные сообщения.

Очевидно, что процесс этот сложен, но к счастью большая часть сложностей ложится на Windows, а не на наши программы. С позиции оконной процедуры, эти сообщения проходят через нее упорядочено или синхронно. Оконная процедура может что-то сделать с этими сообщениями, а может и проигнорировать их. По этой причине оконную процедуру назвали "конечным пунктом обработки" (ultimate hook). Сообщения извещают оконную процедуру почти обо всем, что влияет на окно.

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

Например, когда WinMain вызывает функцию CreateWindow, Windows создает окно и для этого отправляет оконной процедуре асинхронное сообщение WM_CREATE. Когда WinMain вызывает ShowWindow, Windows отправляет оконной процедуре асинхронные сообщения WM_SIZE и WM_SHOWWINDOW. Когда WinMain вызывает UpdateWindow, Windows отправляет оконной процедуре асинхронное сообщение WM_PAINT.

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

1Необходимо уточнить дальнейшее использование атрибутов сообщения "синхронное" и "асинхронное". Ставится сообщение в очередь или не ставится, определяется способом его отправки. В оригинальном тексте книги для указания конкретного способа отправки сообщения используются слова send (функция SendMessage) и post (функция PostMessage). Если для отправки сообщения используется функция SendMessage, то оно не ставится в очередь, оконная процедура вызывается непосредственно, а функция возвращает управление только после обработки сообщения оконной процедурой. Если для отправки сообщения используется функция PostMessage, то оно ставится в очередь, а функция возвращает управление немедленно.

Таким образом, используя терминологию автора, можно сказать:

если сообщение отправляется с помощью функции SendMessage, то оно является асинхронным;

если сообщение отправляется с помощью функции PostMessage, то оно является синхронным.

(Прим. перев.).

39

Цикл обработки сообщений и оконная процедура работают не параллельно. Когда оконная процедура обрабатывает сообщение, то это результат вызова функции DispatchMessage в WinMain. DispatchMessage не завершается до тех пор, пока оконная процедура не обработала сообщение.

Но заметьте, что оконная процедура должна быть повторно-входимой (reentrant). Это означает, что Windows часто вызывает WndProc с новым сообщением, как результат вызова функции DefWindowProc в WndProc с предыдущим сообщением. В большинстве случаев повторная входимость оконной процедуры не создает проблем, но об этом следует знать.

Например, предположим, что вы вводите переменную в процесс обработки сообщения оконной процедурой и затем вызываете функцию Windows. Можете ли вы быть уверены в том, что после возврата функцией своего значения, ваша переменная осталась той же самой? Конечно, нет — в том случае, если конкретная функция Windows, которую вы вызвали, стала источником другого сообщения, и оконная процедура изменила вашу переменную при обработке этого второго сообщения. Это одна из причин того, что при компиляции программ для Windows необходимо отключать некоторые возможности оптимизации.

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

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

Думайте о ближнем

Windows 95 — это вытесняющая многозадачная среда. Это означает, что если программа работает слишком долго, то Windows может разрешить пользователю переключиться на другую программу. Это удобная вещь и одно из преимуществ Windows 95 по сравнению с предыдущими, основанными на DOS, версиями Windows.

Однако, поскольку Windows сконструирована определенным образом, эта вытесняющая многозадачность не всегда работает так, как бы вам хотелось. Например, предположим, что ваша программа тратит на обработку отдельного сообщения минуту или больше. Да, пользователь может переключиться на другую программу. Но пользователь ничего не в состоянии сделать с вашей программой. Пользователь не может переместить окно вашей программы, изменить его размер, закрыть его, т. е. вообще ничего. Так происходит потому, что, хотя ваша оконная процедура и должна выполнять все эти задачи, но она занята выполнением слишком долгой работы. Конечно, может показаться, что оконная процедура не выполняет операций по изменению размера и перемещению собственного окна, но, тем не менее, она это делает. Это часть работы функции DefWindowProc, которую, в свою очередь, необходимо считать частью оконной процедуры.

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

Кривая обучения

Да, как вы, несомненно, поняли из этой главы, программирование для Windows определенно отличается от программирования для общепринятой среды типа MS-DOS. Никто не станет утверждать, что программировать для

Windows легко.

При первом изучении программирования для Windows обычно делают то, что всегда делается при изучении новой операционной системы или нового языка программирования — пишется простая программа для вывода на экран содержимого файла. В общепринятой среде MS-DOS такая программа включает в себя обработку командной строки, простейший файловый Ввод/Вывод и форматирование вывода на экран. В отличии от этого, аналогичная программа для Windows превращается в монстра. Она требует изучения меню, окон диалога, полос прокрутки и т. д. Так как это ваша первая программа для Windows, то из-за необходимости сразу усвоить слишком большой объем материала, она обычно оказывается совершенно неправильной.

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

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

40

Если да, тогда еще один вопрос: хотите ли вы сами программировать все эти меню, окна диалога, полосы прокрутки и графику? Или было бы лучше воспользоваться уже подготовленными для этого программами Windows? Другими словами, что проще: узнать, как использовать более 1000 функций, или писать их самому? Что проще: направить свои силы на изучение управляемой сообщениями архитектуры Windows, или бороться с разнообразными вариантами организации пользовательского ввода в традиционной модели программирования?

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

Соседние файлы в предмете Операционные системы