Работа программ в WINDOWS. (Проще не бывает.)
Тот,
кто собирается писать программы для Windows рано или поздно вынужден разбираться
в механизме работы этой ОС (операционной системы). Давайте и мы погрузимся
(неглубоко) в этот механизм.
Что требуется от ОС для пользователя?
Пользователь имеет определенное количество работающих программ. Все они
безусловно должны выполняться.
И, как бы вы думали, операционная система их выполняет? Да,
конечно же, по очереди. И по тому, как иногда с
задержками появляются на экране элементы наших программ, очередь эта может быть
очень длинной и, отнюдь, не свободной. Но, с другой стороны, все это совершенно
логично.
А как же программы
взаимодействуют с ОС, выполняя свои функции по очереди? Оказывается,
при помощи сообщений, которыми они между собой
обмениваются.
Например, если что то нужно программе, то она посылает сообщение операционной
системе, которое помещается в очередь (программ то ведь много, а сообщений от
них еще больше). По мере возможности ОС их обрабатывает.
Или, допустим, произошло какое то
событие. Была нажата клавиша, перемещен указатель мыши, истек интервал
времени. Каждое из них воспринимается операционной системой, а она посылает
программам сообщение об этом произошедшем событии.
Очередь сообщений - это, конечно же, просто область памяти,
куда помещаются (пересылаются), подготовленные программой блоки данных
(структуры). Эти блоки заполняются по определенным правилам, в результате чего и
программы и ОС понимают, что от них требуется.
Итак, когда начинается выполнение какой либо программы, то
ОС, выделяя ей память определяет для себя место главной процедуры, которая будет
принимать сообщения. Проще можно сказать, что главная процедура программы в
определенном месте памяти ожидает появление подготовленных блоков данных
(сообщений) и передает их своей программе на обработку. Это, конечно очень
грубое и упрощенное описание процесса взаимодействия программ с операционной
системой. Возможны и другие способы взаимодействия, но это уже разговор
отдельный. Кому интересно, можно почитать разные книжки. Процесс познания
непрерывен и бесконечен.
Вот мы выяснили: у программы есть
главная процедура, которая получает сообщения от операционной системы.
Что же с этими сообщениями делать? Понятно что: на
них нужно реагировать - их нужно обрабатывать. Как
у нас операционка называется:
Окна.
Вот это Окно нашей программы, а точнее оконная
функция и должна на них реагировать. Эту функцию принято называть
функцией обратного вызова. Потому, что не сама
программа ее вызывает, а операционная система. В этом смысле Windows все
поставила с ног на голову. Если раньше (в DOS-е) во время выполнения программа
получала доступ к процессору и лишь изредка разрешала операционной системе себя
прервать для выполнения экстренных действий. Теперь же программа
ждет от ОС сообщений, которые являются реакцией на
определенные события, чтобы на них отреагировать.
Это называется событийной моделью управления. Но, даже объемными блоками кода,
ОС управляет по строго определенным правилам. Таково требование реальной
многозадачности.
Но, что то мы отвлеклись. Итак, мы имеем.
- Главную процедуру, которая создает
окно программы в том или другом виде и организует прием сообщений от
операционной системы. |
|
- Функцию окна, которая, реагируя на
сообщения, руководит действиями программы. |
Ну достаточно теории, перейдем к делу. Рассмотрим
программу на Delphi, на которой я собираюсь показать, только что описанную
модель работы операционной
системы.
Это работающая программа.
- Запустите Delphi.
- Из созданного проекта удалите все формы и модули.
- Выберите просмотр исходного кода проекта и введите этот текст программы
полностью.
- Затем просто выберите кнопку: воспроизвести.
Ой извините, это я, как будьто про плеер. Конечно же запустить программу. (Уж
больно похоже они выглядят). И программа сгенерирует фильм по любой книге.
Что, опять шутка? Ну, да!
Программа ничего вам не покажет. Она ничего не умеет, а
вернее умеет почти все.
- Она умеет перемещаться по экрану.
- Умеет сворачиваться и разворачиваться.
- Изменять размеры.
Вы скажете: это мало. Напишите такую программу в DOS и вы
поймете мало это или нет. Если бы это была реальная программа то механизм работы
Windows программ был бы завуалирован. Зато здесь можно показать все о чем я
говорил два абзаца назад.
Пока пропустим кое что, чтобы не перегружать мозги, но обо
всем я расскажу подробнее позже. Итак...
program WinMin;
uses Windows, Messages; const AppName = 'WinMin'; Var Window : HWnd; Message : TMsg; WindowClass : TWndClass; // Определяем функцию окна, которая будет обрабатывать сообщения от системы и // определенным образом реагировать на них function WindowProc (Window : HWnd; Message, WParam : Word; LParam : LongInt) : LongInt; stdcall; begin WindowProc := 0; // Оператор case выбирает способ реакции на сообщение от операционной системы. // Мы не оставили ему никакого выбора. Программа обрабатывает только сообщение // Windows Message: wm_Destroy с требованием разрушить то сооружение, которое мы // называли нашей программой. Конечно же операционка посылает такое сообщение // нашей программе, когда мы нажимаем крестик в правом верхнем углу или Alt+F4. case Message of wm_Destroy :begin PostQuitMessage (0); Exit; end; // Операционка приказывает программе заканчивать работу. Наша программа, //соглашаясь сообщает себе же о том, что нужно выполнить Quit, то есть закончить //работу. Но грязную работу по удалению из памяти нашего произведения сделает за нас //ОС. end; // это конец case, то есть выбора реакции на поступившие сообщения WindowProc := DefWindowProc (Window, Message, WParam, LParam); // А это еще что за процедура? По абревиатуре Def - defoult (по умолчанию), а также // по списку параметров ей передаваемых с первого же взгляда ясно, что именно это // и есть запасная дверь, в которую направляются все сообщения, с обработкой // которых не хочет связываться наша программа. // Там в дебрях операционной системы и происходит выполнение нудных, // повседневных запросов этого неугомонного экземпляра класса обезьян. // Ой нет, нет, конечно же класса окон. // Ведь при кажущемся разнообразии, все они так похожи, ну как все обезьяны // из одноименного класса. Все бы им прыгать с места на место, // сворачиваться, разворачиваться и проделывать у нас на виду // свои фокусы. end; // Ну это была оконная функция, а где же само окно, а вот оно: ниже. begin // Наше окно: WindowClass, хоть и похоже на других, но, все-таки особенное. Вот и зададим эти особенности. with WindowClass do begin // Ух ты, сколько их! А вы что думали: Microsoft зря хлеб ест? Конечно же у окна свой // индивидуальный стиль, ну, то есть некоторые особенности. Style := cs_HRedraw or cs_VRedraw; // Сразу же укажем на функцию окна, которая будет следить за посланными для нее // сообщениями. lpfnWndProc := @WindowProc; // Замнем, пока, для ясности... cbClsExtra := 0; cbWndExtra := 0; hInstance := 0; // Это наши икона и курсор hIcon := LoadIcon (0, idi_Application); hCursor := LoadCursor (0, idc_Arrow); // BackGround - задний фон, ну как же, White - белый конечно. hbrBackground := GetStockObject (White_Brush); // А что меню окна? Выходит ничего особенного, пусть пока будет так как есть. lpszMenuName := ''; // А как нашего дитятю назовем? Так смотри константу в самом верху: пусть немодно, // но со вкусом lpszClassName := AppName; end; // Фу! Умаялся прописывать все эти оконные настройки. Структура окна подготовлена. // Если ребенка сначала рожают, потом регистрируют, то с окном все наоборот. // Операционка должна знать, кого вы собрались рожать. Поэтому, как положено, // регистрируем наш класс. If RegisterClass (WindowClass) = 0 then Halt (255); // Если функция при регистрации вернула не ноль, значит все в порядке // Раз класс зарегистрирован, то давай рожай, то есть создавай наше окошко, // и указатель на него возвращай в этой самой переменной Window. // А уж мы используя этот указатель везде, где потребуется, через него, // будем на наше окошко ссылаться. Window := CreateWindow(AppName, // При создании опять придется с параметрами потрудиться, но, ведь, никто легких // путей и не обещал . 'Win_Min', ws_OverlappedWindow, cw_UseDefault, cw_UseDefault, cw_UseDefault, cw_UseDefault, 0, 0, HInstance, nil); // Все окно есть, покажем его. ShowWindow (Window, CmdShow); // И, на всякий случай, обновим. Если мы уже чего то в окошке нарисовали, то пусть // это нам покажут. UpdateWindow (Window); // Вот, где все и решается. Здесь и организуется эта самая очередь приема // сообщений от операционной системы. Программа, как бы, засыпает и ждет // сообщения. Вот так все программы в Windows спят себе и ничего не делают, ждут: // Когда же потребуется что то от них. Когда событие произойдет. Вот User мышкой // кликнул. Все обстоятельства этого клика анализирует операционная система, а, // затем, на основании этого вырабатывает сообщение и посылает его тому, в чьем // ведении находилась территория клика. while GetMessage (Message, 0, 0, 0) do begin // Ура! Свершилось! Функция получения сообщения объявила: есть сообщение. TranslateMessage (Message); // Нужно проверить, а не клавиши ли на клавиатуре нажимались. А если это так, то их // нужно в символы перевести (транслировать) и уж теперь прямо нашей программе // передать. Но не только символы, а вообще все сообщения для нашего окошка DispatchMessage (Message); end; //Помните, мы посылали нашей программе сообщение с Quit, вот только от него // значение GetMessage станет ложным и наступит Halt. Понятно любому: конец // программе. Halt; end. |
Подведем итог. Главная процедура программы создает окно
и организует цикл обработки сообщений от операционки. Функция, окна обрабатывая
эти сообщения, производит необходимые действия в программе.
Мы рассмотрели скелет программы на Delphi только из за того, что он наиболее
наглядно демонстрирует эти особенности работы программы в среде Windows.