· можно воспользоваться специальной функцией, передающей управление системе, и возвращающей его назад приложению после обработки других приложений. Таких функций в Windows 3.x две — Yield и DirectYield. Однако этот путь используется в очень специальных случаях, например при разработке отладчиков, из–за довольно жестких ограничений на применение этих функций.
При написании нормальных приложений для Windows 3.x разбиение программы на отдельные функции производится не механически, скажем через 100 строк, а функционально — каждая функция выполняет определенные действия. При этом система, вызывая соответствующую функцию, передает ей некоторые данные, которые указывают, что надо сделать.
Это очень важный момент.
До сих пор все программы состояли из алгоритма, управляющего данными. На практике это означало, что алгоритм, описывающий программу, предусматривал когда, где и в какой мере возможно получение данных и управляющих воздействий, и как и куда направлять вывод результатов.
Например, при необходимости ввода данных с клавиатуры, программа включала в себя вызов к операционной системе (или BIOS, на худой конец), который и возвращал требуемые данные.
Еще раз: обычная программа генерирует вызовы к операционной среде для получения и вывода данных: алгоритм управляет данными
В рассмотренном нами случае получается совершенно иная ситуация: поступающие от системы данные управляют поведением программы. Часто такими данными являются управляющие воздействия пользователя (например, изменение размеров окна, вызов меню и др.). Эти воздействия, вообще говоря, не синхронны с работой вашей программы, то есть получается, что данные управляют алгоритмом — один из основных принципов объектно–ориентированного программирования (ООП).
Введем новые понятия:
· данные, передаваемые от системы к соответствующей функции называются сообщением (message).
· процесс обращения к требуемой функции называется посылкой (post) или передачей (send) сообщения.
· функция, обрабатывающая сообщения, называется процедурой обработки сообщений (message handler).
Таким образом, когда вы создаете программу, работающую в псевдомногозадачной среде (здесь: Windows 3.x), вы должны написать требуемые процедуры обработки сообщений. Далее Windows будет передавать вашим процедурам сообщения для их обработки.
С точки зрения ООП все объекты должны обладать 3мя свойствами:
инкапсуляция — объединение в единое целое алгоритмов и необходимых данных;
наследование — возможность порождения новых объектов, основываясь на существующих, наследуя их свойства;
полиморфизм — разность реакций на одинаковые воздействия; наследники одного объекта могут отличаться своими свойствами друг от друга и от предка.
С точки зрения этих свойств объект, определенный процедурой обработки сообщений, удовлетворяет всем этим требованиям. Процедура обработки сообщений может пользоваться специфичными, сгруппированными в каких–либо структурах, данными (инкапсуляция). Мы можем создавать новый объект со своей процедурой обработки сообщений, которая может ссылаться на процедуру ранее описанного объекта (наследование), а также выполнять обработку дополнительных сообщений или иначе обрабатывать прежние сообщения (полиморфизм).
Обычно говорят, что процедура обработки сообщений определяет свойства объекта, так как задает реакцию этого объекта на воздействия (сообщения). Именно с такой трактовкой объекта возникли первые языки ООП.
В Windows объектом ООП является окно. Соответственно говорят, что сообщения направлены не процедуре, а окну. Процедура обработки сообщений определяет окно с конкретными свойствами, даже больше — одна процедура может обслуживать несколько разных окон, но тогда эти окна будут иметь одинаковую реакцию на одинаковые воздействия. То есть процедура обработки сообщений определяет не одно окно, а целый класс (class) окон.
Сообщения, которые Windows направляет окну, отражают то, что происходит с этим окном. Например, есть сообщения, информирующие об изменении размеров окна, или о перемещении мыши, или нажатии на клавишу и др. Передача сообщений является механизмом разделения многих ресурсов, не только процессора. Так, с помощью одних сообщений, реализовано разделение мыши или клавиатуры между задачами, другие сообщения, получаемые окном, помогают осуществить разделение дисплея и т.д.
Таким образом псевдомногозадачный метод разделения процессора оказался основой для построения объектно–ориентированной среды и попутно перевернул всю привычную нам философию написания программ — мы теперь создаем не управляющий алгоритм, а набор процедур, обеспечивающий реакцию нашего окна (то есть нашей программы) на внешние события.
Обработка сообщений является очень распространенным способом организации ООП–библиотек или ООП–языков. Существенное отличие (причем не в лучшую сторону) Windows 3.x заключается в том, что обработка сообщений является методом разделения процессора в псевдомногозадачной среде. Так как система не прерывает выполнение приложения в процессе обработки сообщения, то его обработка не должна занимать много времени
Это сильно затрудняет применение Windows 3.x для расчетных задач — либо мы должны их выполнить быстро, либо разбить на быстро выполняемые части, и понемногу обрабатывать по мере получения сообщений. Понятно, что обычно приходится разбивать на части, а это существенно замедляет вычисления. Вообще говоря, при обработке сообщения лучше укладываться в интервал менее 1 секунды, что бы задержка в реакции Windows на управляющие воздействия не была очень большой; критичной является задержка порядка 1–2 минуты — при этом Windows 3.x может просто дать сбой или зависнуть (что очень сильно зависит от наличия других работающих приложений).
В более сложном Win32 API применяется так называемая истинная многозадачность (вытесняющая, preemptive multitasking). В этом случае разделение процессора осуществляется по определенным временным интервалам (квантам времени). Обработка сообщений перестала быть методом разделения процессора, и в процессе обработки сообщения система может передавать управление другим приложениям. Сама же идея применения объектно–ориентированного подхода к окнам осталась неизменной.
Однако надо отметить, что реализация истинной многозадачности оказалась неполной. В рамках Win32 API могут работать как настоящие Win32 приложения, так и их 16ти разрядные собратья, написанные для Windows API. При запуске таких 16ти разрядных приложений под Win32 для них запускается специальная виртуальная 16ти разрядная Windows–машина, причем в Windows–95 для всех 16ти разрядных приложений используется одна общая виртуальная машина. Это значит, что истинная многозадачность реализована только между Win32 приложениями, в то время как 16ти разрядные приложения между собой используют обработку сообщений для разделения отведенного им процессорного времени. В случае Windows NT для каждого 16ти разрядного приложения запускается собственная Windows–машина, что позволяет им разделять процессор общим способом с приложениями Win32.
Истинная многозадачность в Win32 позволила реализовать так называемые многопотоковые приложения (multithread application). При этом выделяют два новых понятия — процесс (proccess) и поток (thread). Процессы в Win32 API примерно эквивалентны приложениям в Windows API. Для каждого процесса выделяются определенные системные ресурсы — адресное пространство, приоритеты и права доступа к разделяемым ресурсам и прочее, но не процессорное время. Процесс только лишь описывает запущенную задачу, как она есть, без непосредственных вычислений. Для разделения процессора используются не процессы, а потоки, которым и выделяется процессорное время. В рамках каждого процесса выделяется свой поток, называемый первичным (primary thread), создаваемый по умолчанию при создании процесса. При необходимости в пределах одного процесса может быть создано много потоков, конкурирующих между собой (и с потоками других процессов) за процессорное время, но не за адресное пространство.
Пока мы рассмотрели только основную идею использования сообщений для реализации объектно–ориентированной операционной среды. Сейчас надо перейти к особенностям организации приложения, работающего в такой среде.
Каждое приложение открывает по меньшей мере одно окно (в принципе могут существовать приложения вообще без окон, но как небольшие специализированные процедуры, не требующие никакого управления). Свойства окна определяются процедурой обработки сообщений этого окна. Таким образом, что бы определить свойства нужного окна, надо написать процедуру обработки сообщений, посылаемых этому окну (оконную процедуру или оконную функцию — window procedure, она же процедура обработки сообщений, message handler).
Одна процедура может обслуживать сообщения, посылаемые разным окнам с одинаковыми свойствами. Говорят, что окна, имеющие одну и ту же оконную функцию, принадлежат к одному классу окон. Вы должны эту процедуру зарегистрировать — это называется регистрацией класса окон.
Далее необходимо предусмотреть средства для создания и отображения окна зарегистрированного класса. С таким окном пользователь будет работать — передвигать его по экрану, изменять размеры, вводить текст и т.д. Вам необходимо обеспечить реакцию этого окна (то есть вашего приложения) на действия пользователя. Фактически вы должны запустить механизм, обеспечивающий доставку сообщений, адресованных вашему окну, до получателя — оконной процедуры. Этот механизм должен работать, пока работает ваше приложение. Такой механизм называется циклом обработки сообщений (message loop).
Таким образом вы должны выполнить несколько шагов для создания собственного приложения: