- Как работает Windows?
- Чем занимается операционная система?
- Компоненты Windows, или из чего Windows состоит
- Как работает Windows: программная оболочка = Shell
- Как работает Windows : Ядро
- Как работает Windows: Данные конфигурации
- Как Windows управляет программами?
- Создание ОС Windows. Структура ОС Windows
- Структура ОС Windows
- Общее описание структуры системы
- Операционная система Microsoft Windows 3.1 для программиста
- 1 . Основы Microsoft Windows
- 1.1. Компоненты и подсистемы Windows
- Файловая система
- Управление программами
- Управление оперативной памятью
- Драйверы устройств ввода/вывода
- Библиотеки динамической загрузки DLL
- Интерфейс графических устройств GDI
- Очереди сообщений
- Управление шрифтами
- Ресурсы
- Динамический обмен данными DDE
- Вставка и привязка объектов OLE
- Другие компоненты и подсистемы
- 1.2. Простейшее приложение Windows
- Функция WinMain
- Программа «Hello, world!» для Windows
- 1.3. Окно и функция окна
- Окна Windows
- Окна Windows как объекты
- Функция окна
- 1.4. Обработка сообщений
- 1.5. Приложение с обработкой сообщений
- Алгоритм работы приложения
- Листинги файлов приложения
- Определения типов, констант и функций
- Инициализация приложения
- Регистрация класса окна
- Создание главного окна приложения
- Отображение окна на экране
- Цикл обработки сообщений
- Завершение работы приложения
- Функция окна
- Файл определения модуля
- 1.6. О сложности программирования для MS-DOS и Windows
Как работает Windows?
Когда встаёт вопрос как работает Windows, мы подразумеваем тот факт, что речь идёт о ОС семейства Microsoft различных поколений, в принципе работы которых, конечно, немало общего. Не будет ошибкой, если мы вычленим основные четыре функции любой современной операционной системы, и посмотрим, как Windows с ними справляется. В этой статье приводится формальное описание работы операционной системы семейств Windows, приводится список основных компонентов системы. Я покажу, как Windows управляет программами и работает с оборудованием и расскажу о понятиях процесс , поток и др. Статья не для профессионалов (разве можно описать как работает Windows в ЕДИНСТВЕННОЙ статье?) и поможет начинающему получить общее представление о принципе работы Windows.
ВНИМАНИЕ! СТАТЬЯ НЕ ЗАКОНЧЕНА. МАТЕРИАЛЫ ДОБАВЛЯЮТСЯ ПО МЕРЕ ФОРМИРОВАНИЯ ОКОНЧАТЕЛЬНОГО ИНТЕРФЕЙСА БЛОГА.СПАСИБО ЗА ПОНИМАНИЕ.
Чем занимается операционная система?
Любой работающий компьютер есть связка четырёх типов программного обеспечения: операционная система, программы и приложения, драйверы и BIOS Вот основные функции операционной системы, которые определяют её место среди прочих программных оболочек.
- управление оборудованием (включает в себя приём микрокода от BIOS чипа материнской платы, управление оперативной памятью, диагностику неисправностей и сочленение установленного оборудования и программ к нему)
- управление программами (включает в себя установку и удаление программ, запуск программ и участие работе “железных” компонентов от имени программ)
- управление файлами (всё, что можно создавать, перемещать и удалять на твердотельных накопителях)
- создание пользовательского интерфейса (графического или командного/текстового) для обеспечения или контроля всех или многих указанных выше функций
Компоненты Windows, или из чего Windows состоит
Как работает Windows блочно можно отобразить так:
Из схемы видно, что основными компонентами любой операционной системы являются три блока:
Как работает Windows: программная оболочка = Shell
Это часть операционной системы, которая завязана и с пользователем и с приложениями. Именно шелл позволяет нам запускать флешки, устанавливать программы, запускать word-вый документ и печатать в нём, сохраняя в любое дозволенное системой место, которое заметно проводнику. Shell – это и File Explorer (проводник Windows), и контрольная панель Пуск, и окна Свойства системы и Мой компьютер. Человек или пользователь знает шелл как различные инструменты пользовательского интерфейса, такие как диалоговые окна, ярлыки и иконки, контекстные меню дополнительных команд.
А для программ шелл это команды и процедуры, которые эти программы могут вызывать: печать информацию через принтер, читать данные из баз, отображать что-то на экране.
Сам шелл состоит из нескольких подсистем, которые управляются в пользовательском режиме. Такой режим означает, что эти подсистемы имеют очень ограниченный доступ к системной информации, и доступ к “железу” может осуществляться ТОЛЬКО через системные службы (службы самой Windows). Одна из таких подсистем – подсистема безопасности Win32 – обеспечивает вход в систему (регистрацию пользователя), сохранение и распространение его привилегий на файлы и папки, а также другие функции безопасности. Абсолютно все программы и приложения “здороваются с Windows” только через подсистему Win32.
Как работает Windows : Ядро
Как и в других ОС, отвечает за работу с оборудованием, которое внутри компьютера находится или во время работы к компьютеру “пристёгивается”. Режим ядра полномочий имеет гораздо больше. Никакая программа, установленная в Windows, не имеет доступа к “железу” напрямую – только через шелл, который, в свою очередь, посылает все запросы к ядру. Такая система взаимодействия обеспечивает стабильную работу ОС. Это чем-то напоминает ресторан: перед тем как заказать блюдо, вам нужно позвать официанта (к шеф-повару и на саму кухню вас не пропустят).
Ядро состоит из двух компонентов:
- слой аппаратных абстракций HAL – hardware abstraction layer
– (ближайший к железу уровень) - уровень исполнительных служб (обеспечивает взаимодействие подсистем в пользовательском режиме со слоем аппаратных абстракций)
Что здесь нужно запомнить? Логика работы ясна уже из описания: исполнительные службы Windows работают с оборудованием при помощи самого HAL и драйверов для конкретного устройства. Такой принцип работы устанавливается незаметно для глаз пользователя в момент установки Windows на компьютер. В тот самый момент, когда мы видим строки состояния установки компонентов и служб/программ, и строится структура слоя HAL на основе процессора, который в материнской плате вашей машины установлен. То есть привычные процессы во время установки Windows предстают теперь в таком свете:
загрузка шелл пользовательского интерфейса
приглашение к компиляции HAL
То, что появится после установки ОС, перенести на другой компьютер один в один не получится: для нормальной работы в новой конфигурации до– или переустановка отдельных драйверов или целого их пакета неизбежна.
Как работает Windows: Данные конфигурации
Ну, а всем операционным системам нужно место, где данные о программах и оборудовании, а также настройках и предпочтении пользователя можно хранить. Нужно хранить информацию и о том, как пользователь хочет, чтобы Windows и программы выглядели, где хранить скачиваемое и создаваемое и т.д. Для всего этого (или для почти всего) Windows использует … правильно, реестр. Некая информация хранится в файлах инициализации (характерные файлы с расширением .ini и .inf).
И когда установленная программа запускается, перед появлением какого-либо окна она сначала посмотрит реестр и прочитает эти файлы, и только потом уже запуск приложения можно считать успешным.
Как Windows управляет программами?
Если программа установлена, мы подразумеваем, что её остов где-то хранится. Чаще всего это жёсткий диск или флешка. Когда пользователь запускает программу, некоторые из её файлов копируются в оперативную память. Так появляется процесс. практически в том виде, каком мы видим его в Диспетчере задач или Process Explorer. Процессом можно назвать программу, запущенную вместе с соответствующими системными службами и другими ресурсами. Ресурсы нередко включают в себя и другие программы, включая захват дополнительных ячеек оперативной памяти для временного хранения данных. Когда процесс делает запрос к системе по типу “Мне нужны ресурсы!”, подразумевается, что программа обращается именно к подсистеме Win32. Так появляется поток данных. Поток – это единичная, выделенная задача (запустить или создать документ, вставить рисунок из буфера обмена, распечатать документ на принтере и т.д.). Схематично это выглядит так:
Как видите, потоков может быть несколько. Это называется многопоточность/мультипоточность: например, окна браузеров могут запускаться в нескольких вкладках или нескольких отдельных окнах. Выражаясь технически, в обоих случаях речь идёт о нескольких разных процессах браузер.exe.
Создание ОС Windows. Структура ОС Windows
Структура ОС Windows
Общее описание структуры системы
Архитектура ОС Windows (в данном разделе она излагается, следуя главным образом [ Кастер ] и [ Руссинович ] ), претерпела ряд изменений в процессе эволюции. Первые версии системы имели микроядерный дизайн, основанный на микроядре Mach , которое было разработано в университете Карнеги-Меллона. Архитектура более поздних версий системы микроядерной уже не является.
Причина заключается в постепенном преодолении основного недостатка микроядерных архитектур — дополнительных накладных расходов, связанных с передачей сообщений. По мнению специалистов Microsoft, чисто микроядерный дизайн коммерчески невыгоден, поскольку неэффективен. Поэтому большой объем системного кода, в первую очередь управление системными вызовами и экранная графика, был перемещен из адресного пространства пользователя в пространство ядра и работает в привилегированном режиме. В результате в ядре ОС Windows переплетены элементы микроядерной архитектуры и элементы монолитного ядра (комбинированная система). Сегодня микроядро ОС Windows слишком велико (более 1 Мб), чтобы носить приставку «микро». Основные компоненты ядра Windows NT располагаются в вытесняемой памяти и взаимодействуют друг с другом путем передачи сообщений, как и положено в микроядерных операционных системах. В тоже время все компоненты ядра работают в одном адресном пространстве и активно используют общие структуры данных, что свойственно операционным системам с монолитным ядром.
Высокая модульность и гибкость первых версий Windows NT позволила успешно перенести систему на такие отличные от Intel платформы, как Alpha (корпорация DEC), Power PC (IBM) и MIPS ( Silicon Graphic). Более поздние версии ограничиваются поддержкой архитектуры Intel x86.
Упрощенная схема архитектуры, ориентированная на выполнение Win32-приложений, показана на рис. 1.4.
ОС Windows состоит из компонентов, работающих в режиме ядра, и компонентов, работающих в режиме пользователя. Несмотря на миграцию системы в сторону монолитного ядра она сохранила некоторую структуру. В схеме, представленной на рис. 1.4, отчетливо просматриваются несколько функциональных уровней, каждый из которых пользуется сервисами более низкого уровня.
Задача уровня абстрагирования от оборудования (hardware abstraction layer, HAL) — скрыть аппаратные различия аппаратных архитектур для потенциального переноса системы с одной платформы на другую. HAL предоставляет выше лежащим уровням аппаратные устройства в абстрактном виде, свободном от индивидуальных особенностей. Это позволяет изолировать ядро, драйверы и исполнительную систему ОС Windows от специфики оборудования (например, от различий между материнскими платами).
Ядром обычно называют все компоненты ОС, работающие в привилегированном режиме работы процессора или в режиме ядра. Корпорация Microsoft называет ядром (kernel) компонент, находящийся в невыгружаемой памяти и содержащий низкоуровневые функции операционной системы, такие, как диспетчеризация прерываний и исключений, планирование потоков и др. Оно также предоставляет набор процедур и базовых объектов, применяемых компонентами высших уровней.
Ядро и HAL являются аппаратно-зависимыми и написаны на языках Си и ассемблера. Верхние уровни написаны на языке Си и являются машинно-независимыми .
Исполнительная система (executive) обеспечивает управление памятью, процессами и потоками, защиту, ввод-вывод и взаимодействие между процессами. Драйверы устройств содержат аппаратно-зависимый код и обеспечивают трансляцию пользовательских вызовов в запросы, специфичные для конкретных устройств. Подсистема поддержки окон и графики реализует функции графического пользовательского интерфейса (GUI), более известные как Win-32-функции модулей USER и GDI
В пространстве пользователя работают разнообразные сервисы (аналоги демонов в Unix), управляемые диспетчером сервисов и решающие системные задачи. Некоторые системные процессы (например, обработка входа в систему) диспетчером сервисов не управляются и называются фиксированными процессами поддержки системы. Пользовательские приложения ( user applications ) бывают пяти типов: Win32, Windows 3.1, MS-DOS, POSIX и OS/2 1.2. Среду для выполнения пользовательских процессов предоставляют три подсистемы окружения: Win32, POSIX и OS/2. Таким образом, пользовательские приложения не могут вызывать системные вызовы ОС Windows напрямую, а вынуждены обращаться к DLL подсистем (краткое определение dll имеется в приложении).
Основные компоненты ОС Windows реализованы в следующих системных файлах, находящихся в каталоге system32:
Операционная система Microsoft Windows 3.1 для программиста
1 . Основы Microsoft Windows
В этой главе мы рассмотрим структуру операционной системы Microsoft Windows и ее основные особенности. Этот чисто теоретический материал необходим для понимания принципов, положенных в основу всех программ, разрабатываемых для Windows. Мы также научимся создавать простейшие приложения для этой операционной системы.
Операционная система Microsoft Windows представляет собой однопользовательскую многозадачную операционную систему с графическим интерфейсом, в отличие от MS-DOS, которая является однопользовательской однозадачной операционной системой с текстовым интерфейсом. Многозадачность и графический интерфейс оказывают значительное влияние на структуру и принципы работы программ, созданных специально для Windows. И если программа, работающая под управлением MS-DOS, могла «единолично» использовать все аппаратные ресурсы компьютера (видеомонитор, клавиатуру, мышь, диски и т. д.), то программа для Windows должна разделять эти и другие ресурсы с остальными, работающими параллельно, программами.
Вы, наверное, знаете, что операционная система Microsoft Windows версии 3.1 может работать в двух режимах — стандартном и расширенном. Если в компьютере установлен процессор 80386 и имеется по крайней мере 2 Мбайт оперативной памяти, по умолчанию Windows запускается в расширенном режиме. Это основной режим работы, который обеспечивает использование всех возможностей Windows.
Стандартный режим работы Windows используется при работе на процессоре 80286. Вы можете запустить Windows в стандартном режиме и на процессоре 80386, если для запуска введете команду «win /s».
1.1. Компоненты и подсистемы Windows
Как и любая другая операционная система, Microsoft Windows содержит в себе ядро, подсистему управления оперативной памятью, подсистему управления программами, файловую систему, драйверы для работы с устройствами ввода/вывода и другие системы.
Рассмотрим кратко основные особенности отдельных подсистем Microsoft Windows.
Файловая система
Версия 3.1 операционной системы Microsoft Windows использует файловую систему MS-DOS. И это удобно с точки зрения совместимости с MS-DOS. Вы, наверное, знаете, что Windows запускается из MS-DOS как обычная программа с именем win.com. После запуска Windows не выгружает из памяти MS-DOS, но использует некоторые ее компоненты (например, файловую систему) в несколько измененном виде.
В следующих версиях Windows наряду с файловой системой MS-DOS планируется использовать высокопроизводительную файловую систему NTFS, которая произошла от файловой системы HPFS (High Performance File System), применяемой в операционной системе OS/2. Windows NT уже использует файловую систему NTFS. Новая файловая система имеет меньше ограничений, в частности в ней нет таблицы размещения файлов FAT и сняты практически все ограничения на длину имени файла (максимальная длина имени в OS/2 составляет 255 символов).
Управление программами
Подсистема управления программами в Windows обеспечивает запуск и одновременную работу нескольких программ. Программы, созданные специально для Windows, называются приложениями Windows (Windows application). В среде операционной системы Windows версии 3.1 одновременно может быть запущено несколько приложений Windows и несколько программ, созданных для MS-DOS.
Если Windows работает в расширенном режиме, для каждой программы MS-DOS при запуске создается отдельная виртуальная машина. При выполнении программы MS-DOS процессор работает в режиме виртуального процессора 8086, поэтому все работающие параллельно на разных виртуальных машинах программы MS-DOS изолированы друг от друга.
Если Windows работает в стандартном режиме, запуск программы MS-DOS приводит к приостановке выполнения приложений Windows и выгрузке содержимого оперативной памяти на диск. Процессор переходит в реальный режим работы. Соответствующей настройкой системы запуска программ MS-DOS можно добиться этого и в расширенном режиме работы Windows.
Для всех приложений Windows в расширенном режиме работы создается одна виртуальная машина, причем процессор работает в защищенном режиме. Все приложения Windows используют для адресации памяти одну локальную таблицу дескрипторов LDT, что может привести к взаимному влиянию приложений друг на друга. В этом смысле программы MS-DOS, работающие в среде Windows, лучше защищены друг от друга, чем приложения Windows, так как адресные пространства виртуальных машин MS-DOS изолированы друг от друга (за исключением начала адресного пространства, занятого резидентными программами и драйверами, загруженными до запуска Windows).
Подсистема управления программами не обеспечивает квантования времени между приложениями Windows, но делает это для запущенных одновременно программ MS-DOS. Приложения Windows сделаны таким образом, что они сами «добровольно» отдают друг другу процессорное время, обеспечивая так называемую невытесняющую мультизадачность (nonpreemptive multitasking).
Операционная система Windows NT может выполнять обычные программы MS-DOS, приложения Windows версии 3.1 и новые, 32-разрядные приложения, созданные специально для Windows NT. Для каждого 32-разрядного приложения Windows NT создает отдельную виртуальную машину. Благодаря этому приложения Windows NT изолированы друг от друга. Использование отдельных виртуальных машин позволяет реализовать для 32-разрядных приложений Windows NT вытесняющую мультизадачность (preemptive multitasking) с выделением каждому приложению квантов времени.
Управление оперативной памятью
Если вы помните, подсистема управления оперативной памятью в MS-DOS базируется на использовании блоков управления памятью MCB (см. первый том «Библиотеки системного программиста»). Такое «управление» памятью полностью основано на джентльменском соглашении между программами о сохранении целостности операционной системы, так как любая программа может выполнить запись данных по любому адресу. Программа может легко разрушить системные области MS-DOS или векторную таблицу прерываний.
Приложение Windows выполняется в защищенном режиме, поэтому оно не может адресоваться к любым областям памяти. Это сильно повышает надежность операционной системы в целом — мультизадачная система не должна завершаться аварийно при аварийном завершении одного из приложений.
Пусть вас не смущает, что вы не сможете изменить векторную таблицу прерываний. Во-первых, в защищенном режиме используется не векторная таблица прерываний, а дескрипторная таблица прерываний. Во-вторых, при создании обычных приложений у вас никогда не возникнет необходимость в изменении этой таблицы. В-третьих, если вам и в самом деле нужно изменить дескрипторную таблицу прерываний, мы расскажем, как это можно сделать, не прибегая к непосредственной записи новых значений селекторов в оперативную память, отведенную для таблицы.
Использование защищенного режима работы процессора обеспечивает приложениям Windows непосредственный доступ к расширенной памяти компьютера. С помощью системы управления памятью приложение может заказать для себя буфер очень большого размера. Физически этот буфер может находиться либо в расширенной памяти, либо в виртуальной. Можно также заказать небольшой буфер в стандартной памяти (ниже границы 1 Мбайт).
Виртуальная память располагается на жестком диске компьютера. При первоначальной установке Windows вы должны определить расположение и размер файла, который будет использоваться для виртуальной памяти (только при работе Windows в расширенном режиме). При необходимости Windows выполняет чтение в оперативную память отдельных страниц виртуальной памяти или запись страниц из оперативной памяти на диск. Все это создает иллюзию наличия оперативной памяти очень большого размера. В архитектуру процессора 80386 и 80486 заложена поддержка виртуальной памяти, так что процесс виртуализации выполняется достаточно эффективно.
Другая особенность системы управления памятью в операционной системе Windows связана с управлением сегментами памяти, выделенными приложению.
Как вы знаете, программа MS-DOS в зависимости от используемой модели памяти может состоять из одного или нескольких сегментов кода, а также из одного или нескольких сегментов данных. При загрузке программы MS-DOS все нужные сегменты загружаются в первый мегабайт оперативной памяти, после чего управление передается в точку входа, расположенную в сегменте кода.
Приложение Windows устроено сложнее и загружается по-другому. Как и программы MS-DOS, приложения Windows состоят из сегментов кода и сегментов данных. В зависимости от модели памяти приложение может иметь один или несколько сегментов кода и один или несколько сегментов данных.
Сегменты приложения Windows получают дополнительный атрибут — тип сегмента. Существуют сегменты с фиксированным расположением в оперативной памяти (fixed), перемещаемые (moveable) и удаляемые (discardable). В операционной системе MS-DOS нет аналога перемещаемым и сбрасываемым сегментам, так как при загрузке все сегменты располагаются по фиксированным (на время работы программы) адресам. Перемещаемые сегменты могут менять свое расположение в адресном пространстве. Управляет этим, незаметным для приложений, процессом операционная система Windows.
Для чего понадобились перемещаемые сегменты?
Вспомним, что Windows — многозадачная операционная система. Поэтому такой ресурс, как оперативная память, используется совместно всеми работающими параллельно приложениями или различными копиями одного и того же приложения, запущенного несколько раз. В процессе работы вы запускаете и завершаете различные приложения, что приводит к фрагментации непрерывного адресного пространства. Используя механизм перемещения сегментов, основанный на использовании схемы адресации процессора 80286, операционная система Windows по мере необходимости «уплотняет» оперативную память, высвобождая непрерывное адресное пространство для запуска новых приложений.
Удаляемые (discardable) сегменты обычно используются для хранения выполняемых сегментов или сегментов констант. Если операционной системе Windows требуется получить в свое распоряжение область памяти, она может уничтожить удаляемый сегмент и забрать распределенную для него память. Если впоследствии потребуется восстановить содержимое удаляемого сегмента, Windows выполняет чтение данных сегмента из соответствующего файла.
Помимо описанных выше атрибутов сегменты могут иметь еще два. Можно создать сегменты, загружаемые при запуске приложения (preload) и загружаемые при обращении к ним (loadoncall). Сегменты типа loadoncall не загромождают оперативную память, так как после запуска приложения они остаются на диске и загружаются в память только при необходимости. Причем при составлении программы вам достаточно описать сегмент как loadoncall, после чего Windows будет сама его загружать при обращении к сегменту со стороны приложения.
Механизм управления сегментами достаточно сложен, и пока еще не настало время для его детального рассмотрения. Однако уже сейчас видно, что требование обеспечения разделения адресных пространств приложений и обеспечения мультизадачности привело к значительному усложнению системы управления памятью по сравнению с используемой в MS-DOS.
Драйверы устройств ввода/вывода
Для работы с устройствами ввода/вывода Windows содержит комплект драйверов. Основное требование к этим драйверам заключается в способности работать в мультизадачном режиме, обеспечивая совместное использование устройств ввода/вывода всеми одновременно работающими приложениями.
Необходимо отметить, что задача создания собственного драйвера для Windows значительно сложнее задачи разработки драйвера для MS-DOS. Драйверы Windows — тема для отдельной книги (возможно, не одной). Однако для стандартных устройств, таких, как принтер, мышь, порт последовательной передачи данных и т. п. драйверы уже имеются и поставляются в составе Windows либо в комплекте с устройствами ввода/вывода.
Если же вам необходимо создать драйвер собственного нестандартного устройства, придется приобрести такой программный продукт, как Driver Development Kit (DDK), поставляемый фирмой Microsoft. В состав DDK входит вся необходимая документация, средства разработки и готовые примеры драйверов.
Библиотеки динамической загрузки DLL
Для того чтобы сформировать файл программы MS-DOS (типа exe или com), после компиляции исходных текстов отдельных модулей вы должны запустить редактор связей. Редактор связей соберет файл программы, включив в него все используемые в проекте модули. Фактически файл программы MS-DOS содержит весь код, который может потребоваться для ее выполнения. Единственное исключение — код обработчиков программных прерываний MS-DOS и BIOS, который не включается в файл программы, так как он всегда загружен в оперативную память.
Подобный подход непригоден для мультизадачной среды, так как он приводит к неэкономному расходованию дорогостоящего ресурса — оперативной памяти. В самом деле, если несколько приложений используют один и тот же модуль, нет никакого смысла загружать этот модуль несколько раз для каждого приложения. Вместо этого лучше было бы загрузить такой модуль в оперативную память один раз и организовать к нему коллективный доступ со стороны всех «заинтересованных» приложений.
Именно так и поступили разработчики Windows. Практически все модули Windows реализованы в виде так называемых библиотек динамической загрузки DLL (Dynamic Link Libraries). Когда приложения желают вызвать Windows для получения обслуживания, происходит обращение к единственной копии нужного модуля, находящейся в оперативной памяти (или загружаемой в оперативную память при обращении). Библиотеки динамической загрузки (или, иными словами, dll-библиотеки) находятся на диске в виде файлов с расширением имени dll, хотя может быть использовано и любое другое расширение.
Обратите внимание на приложения Calculator и Clock. Загрузочные файлы для этих приложений имеют размеры 43072 и 16416 байт, что совсем немного (и даже подозрительно мало!), особенно если учесть сложность выполняемых этими приложениями функций. Это возможно только благодаря тому, что большинство кода, выполняемого этими приложениями, находится вне загрузочных файлов. И в самом деле, все, что относится к формированию изображения калькулятора или часов, к перемещению и изменению размеров окон, выполняется модулями, расположенными в библиотеках динамической загрузки Windows.
Вы тоже можете создать свои собственные библиотеки динамической загрузки для использования вашими или другими приложениями.
Использование библиотек динамической загрузки для предоставления сервиса приложениям со стороны операционной системы имеет преимущество, связанное с возможностью обновления версии библиотеки без повторной сборки загрузочного файла приложения. Если ваше приложение использует функции из dll-библиотеки, вы можете обновить версию dll-библиотеки простой заменой старого файла библиотеки на новый. Если новая dll-библиотека совместима со старой (а обычно так и бывает), для ее использования вам не потребуется вносить никаких изменений в загрузочный файл приложения.
Из сказанного выше становится понятно, почему в среде Windows приложения не используют программные прерывания для получения обслуживания от операционной системы: механизм dll-библиотек обеспечивает большую гибкость и удобство в использовании. Так что при создании обычных приложений Windows вы можете забыть о программных прерываниях вообще и о переназначении аппаратных и программных прерываний в частности.
Интерфейс графических устройств GDI
Так как Windows является операционной системой с графическим интерфейсом, одно из важнейших мест в Windows занимает система графического ввода/вывода.
В Windows реализована концепция графического интерфейса, независимого от аппаратной реализации используемого устройства ввода/вывода. Этот интерфейс называется GDI (Graphics Device Interface). В рамках этого интерфейса определены все функции для работы с графикой.
Независимость от аппаратной реализации позволяет вам использовать одни и те же функции для рисования графических объектов (таких, как линии, окружности, прямоугольники и т. д.) как на экране видеомонитора, так и на бумаге, вставленной в матричный или лазерный принтер, в плоттер или другое устройство графического вывода, которое имеет драйвер для Windows.
Очереди сообщений
Иногда говорят, что работа операционной системы Windows основана на передаче сообщений (message). Что это за сообщения, кто, кому и зачем их передает?
Передача сообщений — это способ, при помощи которого в Windows организован обмен информацией между отдельными подсистемами, приложениями или между отдельными модулями одного и того же приложения.
Само по себе сообщение представляет собой структуру данных:
Эта структура содержит уникальный для Windows код сообщения message и другие параметры, отражающие адресат (идентификатор получателя) сообщения hwnd, содержимое сообщения wParam и lParam, время отправления time и информацию о координатах pt.
Windows содержит в себе системную очередь сообщений, куда последние могут поступать от драйверов устройств ввода/вывода (при завершении операции ввода/вывода) или от приложений, а также несколько очередей сообщений для каждого приложения.
Когда вы нажимаете клавиши, перемещаете мышь по поверхности стола или нажимаете кнопки на корпусе мыши, соответствующий драйвер (клавиатуры или мыши) вырабатывает сообщения, отражающие выполняемые вами действия. Эти сообщения попадают вначале в общую системную очередь и затем распределяются в очереди отдельных приложений. Подробности распределения и обработки сообщений мы рассмотрим немного позже.
Если вы обратите внимание на внешний вид приложений Windows, то обнаружите, что окно приложения содержит множество органов управления, таких, как кнопки, переключатели, полосы просмотра и т. д. Работа с этими органами управления подробно описана в руководстве пользователя Windows. Действие, выполненное вами над любым органом управления, приводит к генерации соответствующего сообщения и помещения этого сообщения в очередь приложения.
Приложение Windows постоянно анализирует содержимое своей очереди сообщений. Когда в очереди появляется сообщение от какого-либо органа управления, приложение выполняет соответствующее действие.
Такой механизм значительно упрощает программирование, так как приложение не занимается, например, отслеживанием текущих координат курсора мыши для того, чтобы определить, на какую кнопку вы нажали или какую выбрали строку в меню. Приложение получает готовое сообщение о том, что нажата кнопка с определенным идентификатором или выбрана строка из меню с определенным идентификатором, а вся остальная работа, связанная с отслеживанием текущих координат курсора и определением использованной кнопки, выполняется самой операционной системой Windows. Идентификаторы кнопок и других органов управления определяются программистом. Это просто числа, однозначно соответствующие органам управления.
Обычно приложение имеет главное окно, в котором располагаются такие органы управления, как кнопки, меню, полосы просмотра, переключатели и т. д. Работая с приложением, вы выбираете строки меню, нажимаете кнопки или используете другие органы управления. Каждый орган управления (кнопка или строка меню) имеет свой идентификатор. Когда вы нажимаете на кнопку или выбираете строку меню, в очередь сообщений приложения Windows заносит сообщение, содержащее идентификатор использованного органа управления.
Приложение анализирует очередь сообщений и выполняет обработку сообщений. Например, если вы нажали кнопку с надписью «Exit», приложение может завершить свою работу.
Следует отметить, что в Windows используется многоуровневая система сообщений.
Сообщения низкого уровня вырабатываются, когда вы перемещаете мышь или нажимаете клавиши на корпусе мыши или на клавиатуре. В эти сообщения входит информация о текущих координатах курсора мыши или кодах нажатых клавиш. Обычно приложения редко анализируют сообщения низкого уровня. Все эти сообщения передаются операционной системе Windows, которая на их основе формирует сообщения более высокого уровня.
Когда вы нажимаете кнопку на диалоговой панели или выбираете строку из меню приложения Windows, ваше приложение получает сообщение о том, что нажата та или иная клавиша или выбрана та или иная строка в меню. Вам не надо постоянно анализировать координаты курсора мыши или коды нажимаемых клавиш — Windows сама вырабатывает для вас соответствующее сообщение высокого уровня. Таким образом, вы можете возложить на Windows всю работу, связанную с «привязкой» мыши и клавиатуры к органам управления.
Но тем не менее ваше приложение может вмешаться и в обработку сообщений низкого уровня. Такая схема очень удобна и позволяет, с одной стороны, упростить приложение, возложив на Windows всю работу с сообщениями нижнего уровня, с другой стороны, позволяет при необходимости вмешаться в процесс обработки сообщений низкого уровня.
Программы MS-DOS в «классическом» исполнении работают по другому. Как правило, такие программы выполняются линейно, ожидая от пользователя ввода той или иной команды и блокируя нежелательные на данный момент действия.
Логика работы приложений Windows называется логикой, управляемой событиями. Под событием понимается обнаружение в очереди сообщений приложения того или иного сообщения. В любой момент времени вам разрешается использовать любые органы управления любого запущенного приложения (хотя приложение Windows при необходимости может выполнить блокировку отдельных органов управления).
Операционная система Windows направляет сообщение от использованного органа управления в очередь того приложения, к которому принадлежит данный орган управления. Поэтому приложение не должно беспокоиться о том, что в любой момент времени вы можете приступить к работе с другим приложением. Просто каждое приложение занимается обработкой своей очереди сообщений, а Windows заботится о том, чтобы все сообщения попадали в нужную очередь.
Так как все приложения используют совместно общий экран видеомонитора, работая каждое в своем окне, Windows обеспечивает обработку ситуаций, при которых одно окно перекрывает другое. Приложения не следят за тем, чтобы их окна не перекрывались окнами других приложений. Но они все должны быть способны обработать специальное сообщение, по которому необходимо перерисовать содержимое всего своего окна или части окна.
Управление шрифтами
Операционная система MS-DOS не содержит никакой серьезной поддержки для работы со шрифтами. В то же время Windows версии 3.1 использует передовую технологию масштабируемых шрифтов TrueType. Из руководства пользователя Windows вы знаете, что шрифты TrueType сохраняют свой внешний вид при изменении высоты букв. Поэтому они и называются масштабируемыми.
Любое приложение Windows может использовать шрифты, зарегистрированные (или, иными словами, установленные) в операционной системе Windows. Для работы со шрифтами Windows предоставляет приложениям богатый набор функций, позволяющий выполнять все необходимые операции.
Система управления шрифтами, встроенная в Windows, позволяет реализовать на практике режим редактирования документов, который носит название WYSIWYG — What You See Is What You Get (что вы видите, то и получите). Например, если вы используете для редактирования документов такой текстовый процессор, как Microsoft Word for Windows, вы можете выбрать для оформления любой из масштабируемых шрифтов. Система управления шрифтами обеспечит соответствие изображения текста на экране распечатке, полученной на принтере (особенно хорошие результаты получатся при использовании видеомонитора с высоким разрешением, а также струйного или лазерного принтера).
Ресурсы
Как мы говорили, приложения Windows используют новый формат файла загрузочного модуля. Одно из новшеств заключается в том, что файл загрузочного модуля кроме кода и обычных данных содержит так называемые ресурсы. В качестве ресурсов используются текстовые строки, пиктограммы, графические изображения, меню, диалоговые панели, шрифты или любые произвольные данные.
Для создания ресурсов используются специальные программы, которые называются редакторами ресурсов. Такие программы поставляются в комплекте с компилятором. Они позволяют редактировать ресурсы без изменения кода, и в этом смысле можно говорить об относительной независимости ресурсов от программного кода приложения.
Приложение Windows при необходимости может загрузить ресурс в оперативную память и использовать его, например, для вывода на экран.
В качестве ресурса удобно оформить, например, все текстовые сообщения или меню. В этом случае вы сможете редактировать текст сообщений или строк меню без повторной сборки всего приложения. Это удобно, если вам нужно перевести все сообщения или меню на другой язык (например, с русского на английский или наоборот).
Включение в ресурсы пиктограмм и других графических изображений позволит вам редактировать их отдельно, без повторной сборки приложения.
Ресурсы могут загружаться в оперативную память сразу после запуска приложения или при необходимости. В последнем случае после запуска приложения ресурсы хранятся на диске. Если ресурсы не используются, то они не занимают места в оперативной памяти.
Динамический обмен данными DDE
Любая мультизадачная операционная система содержит систему, обеспечивающую взаимодействие работающих параллельно процессов. Операционная система Windows имеет механизм динамического обмена данными DDE, с помощью которого приложения Windows могут обмениваться различной информацией, такой, как текстовые строки, числа или блоки оперативной памяти.
Сетевая операционная система Microsoft Windows for Workgroups использует DDE для организации взаимодействия и передачи данных между приложениями, работающими в сети на разных рабочих станциях.
Вставка и привязка объектов OLE
Операционная система Windows содержит сложный механизм вставки и привязки объектов OLE (Object Linking and Embedding), обеспечивающий интеграцию приложений на уровне объектов, таких, как документы, графические изображения или электронные таблицы.
При использовании OLE документ, подготовленный, например, текстовым процессором Microsoft Word for Windows, может содержать в себе как объект изображение, созданное графическим редактором Paint Brush. Для редактирования такого объекта из среды текстового процессора Microsoft Word for Windows вызывается приложение Paint Brush, причем результат редактирования записывается обратно в тело документа.
Другие компоненты и подсистемы
Кроме перечисленных выше, операционная система Windows содержит другие компоненты и подсистемы. Некоторые из них входят в комплект Windows версии 3.1, некоторые нужно приобретать отдельно.
В последнее время получают все более широкое распространение системы мультимедиа, обеспечивающие звуковой интерфейс между человеком и компьютером. Самые лучшие из таких систем способны (при наличии соответствующей аппаратуры) работать не только со звуком, но и с видеосигналом, поступающим с телевизионной камеры или видеомагнитофона.
С точки зрения программиста системы мультимедиа реализованы в виде dll-библиотек, содержащих наборы функций для работы с устройствами ввода/вывода звука и изображения.
Другая подсистема, устанавливаемая дополнительно, имеет отношение к управлению памятью и называется Win32s. Это подмножество 32-разрядного программного интерфейса операционной системы Windows NT, использующее сплошную несегментированную модель памяти. В этой модели памяти приложения обычно никогда не изменяют содержимого сегментных регистров процессора, так как они могут непосредственно адресовать огромные объемы виртуальной оперативной памяти.
Эта подсистема имеет и другие интересные возможности, например отображение файлов на оперативную память, что значительно ускоряет и упрощает работу с ними.
Система Win32s поставляется фирмой Microsoft отдельно вместе с соответствующими средствами разработки, а также входит в комплект поставки трансляторов Borland C++ версии 4.0 и Symantec C++ версии 6.0.
1.2. Простейшее приложение Windows
В этом разделе мы создадим простейшее приложение Windows. Оно будет мало напоминать «настоящие» приложения, которые поставляются вместе с Windows, но на данном этапе наша основная задача — научиться создавать файл загрузочного модуля приложения Windows с использованием системы разработки Borland C++ версии 3.1.
Функция WinMain
Любая программа MS-DOS, составленная на языке программирования C или C++, должна содержать функцию с именем main. Эта функция первой получает управление сразу после того, как специальный стартовый модуль устанавливает расположение стека и кучи программы, а также выполняет все необходимые инициализирующие действия.
Если вы создаете приложение Windows с использованием языка программирования C или C++, прежде всего вы должны создать функцию с именем WinMain, которая является аналогом функции main в программах для MS-DOS.
Функция WinMain должна быть определена следующим образом:
В определении функции WinMain использованы, вероятно, незнакомые вам типы — PASCAL, HINSTANCE, LPSTR. Эти типы описаны в include-файле с именем windows.h, который поставляется вместе с компилятором и должен быть включен в исходный текст программы при помощи оператора #include.
Тип PASCAL определен в файле windows.h следующим образом:
Функция, описанная с ключевым словом _pascal, использует соглашение языка Паскаль при передаче параметров, которое отличается от соглашения, принятого в языке C. В частности, в отличие от определения обычной функции C, определение функции, использующей соглашение языка Паскаль, должно содержать точное перечисление всех используемых параметров.
Это связано с тем, что такая функция должна сама освобождать стек перед возвратом. Если перед вызовом функции, описанной с ключевым словом _pascal или PASCAL, записать в стек неправильное количество параметров, перед возвратом из функции обязательно произойдет неправильное освобождение стека.
В частности, функция WinMain должна использовать ровно четыре параметра, как показано в предыдущем примере. Привычная вам функция main программы MS-DOS могла либо совсем не иметь параметров, либо использовать параметры argc и argv.
Функция WinMain возвращает значение типа int, что позволяет передать операционной системе Windows или отладчику код завершения приложения.
Первые два параметра имеют тип HINSTANCE, который в Windows версии 3.1 является 16-разрядным идентификатором. Не следует однако думать, что тип HINSTANCE эквивалентен типу int. Изучив include-файл windows.h, вы сможете убедиться в том, что это не так.
Параметр с именем hInstance является идентификатором приложения. Любое приложение перед запуском получает свой уникальный идентификатор, который передается ему через параметр hInstance.
Идентификатор приложения используется при вызове многих функций программного интерфейса Windows, поэтому было бы неплохой идеей сохранить его в памяти на все время работы приложения.
Так как Windows — мультизадачная среда, вы можете запустить одновременно несколько приложений, и каждое приложение будет иметь свой, уникальный идентификатор.
У вас существует и другая возможность — вы можете запустить одно приложение несколько раз. Каждая копия приложения в этом случае также будет иметь свой собственный идентификатор.
Приложение может легко определить идентификаторы всех своих одновременно работающих копий. Для этого предназначен второй параметр функции WinMain — параметр hPrevInstance. Если запущена только одна копия приложения, этот параметр равен нулю. В противном случае параметр hPrevInstance равен идентификатору предыдущей копии данного приложения.
Анализируя параметр hPrevInstance, приложение может выполнять различные действия в зависимости от того, была ли уже загружена на момент запуска другая копия приложения.
Вы можете полностью блокировать возможность параллельной работы нескольких копий одного приложения, если в начале функции WinMain расположите следующую строку:
В этом случае если при запуске приложения параметр hPrevInstance отличен от нуля, то это означает, что ранее уже была запущена копия данного приложения. В этом случае приложение завершается с кодом, равным нулю.
Третий параметр функции WinMain имеет имя lpszCmdLine. Он имеет тип LPSTR, который определяется в include-файле windows.h следующим образом:
Из этого определения видно, что параметр lpszCmdLine является дальним указателем на символьную строку. Что это за строка?
Программе MS-DOS при запуске из командной строки вы можете передать параметры. Эти параметры программа получает через параметры argc и argv функции main.
Аналогично, при запуске приложения Windows вы также можете указать параметры. Эти параметры должны быть записаны в текстовом виде после имени exe-файла приложения. Параметры можно задать для любой пиктограммы любой группы Program Manager. Для этого выберите из меню «File» строку «Properties» и в поле «Command Line» после пути к exe-файлу приложения допишите необходимые параметры.
Еще один способ указания параметров заключается в использовании для запуска приложения командной строки, появляющейся в отдельной диалоговой панели при выборе строки «Run. » из меню «File» приложения Program Manager.
После запуска приложение может проанализировать строку параметров, пользуясь переменной lpszCmdLine как дальним указателем на строку параметров. Учтите, что перед передачей параметров приложению никакой обработки строки параметров не производится, приложение получает строку параметров точно в таком виде, в котором она была указана при запуске. В частности, если было указано несколько параметров, разделенных пробелом или другим символом, переменная lpszCmdLine будет указывать на строку, содержащую все разделительные пробелы (или другие символы). Строка параметров будет закрыта двоичным нулем.
Последний параметр функции WinMain имеет имя nCmdShow и тип int.
Этот параметр содержит рекомендации приложению относительно того, как оно должно нарисовать свое главное окно. Приложение может проигнорировать эти рекомендации, не учитывая значение параметра nCmdShow, однако это плохой стиль программирования.
Вы знаете, что практически любое приложение может увеличивать свое окно до размеров экрана видеомонитора (или до некоторого предельного размера, зависящего от самого приложения) или уменьшать его, сворачивая в пиктограмму. При запуске приложения с помощью строки «Run. » меню «File» приложения Program Manager кроме командной строки вы можете указать режим запуска «Run Minimized». В этом режиме правильно спроектированное приложение (способное анализировать параметр nCmdShow) при запуске сразу сворачивает свое главное окно в пиктограмму.
В нашей первой программе мы для простоты не будем анализировать этот параметр, поэтому пока отложим описание его возможных значений.
У вас может возникнуть вопрос — зачем переопределять тип _pascal как PASCAL, а тип _far как FAR?
Дело в том, что операционная система Windows задумана (во всяком случае Windows NT) как переносимая на различные платформы. Windows NT успешно работает не только на процессорах фирмы Intel, но и, например, на процессоре Alpha, созданном фирмой DEC и имеющем свою собственную архитектуру. Если вы планируете в будущем перетранслировать исходные тексты своих приложений для Windows NT, вам нельзя закладывать в них особенности архитектуры процессоров Intel. Например, тип FAR для Windows версии 3.1 определен как _far, а для Windows NT этот же тип может быть определен по-другому. Например, так:
Как дополнительная помощь в создании переносимых (мобильных) приложений, в операционной системе Windows программный интерфейс (API) отделен от самой операционной системы. Поэтому программный интерфейс Windows в принципе может быть реализован в среде другой операционной системы, такой, как UNIX или OS/2.
Программа «Hello, world!» для Windows
Давайте попробуем создать для Windows вариант известной всем программы, приведенной в книге Кернигана и Риччи, посвященной программированию на языке C:
Задачей этой программы, как следует из исходного текста, является вывод на экран строки «Hello, world!».
Как мы уже говорили, вместо функции main приложение Windows использует функцию WinMain, причем необходимо указать все четыре параметра этой функции.
К сожалению, вы не сможете воспользоваться функцией printf, так как ни эта, ни другие аналогичные функции консольного ввода/вывода в обычных приложениях Windows использовать нельзя. Для вывода текстовой строки «Hello, world!» мы воспользуемся функцией из программного интерфейса Windows с именем MessageBox.
Создайте на диске каталог с именем hello и скопируйте в него файлы hello.cpp и hello.prj из одноименного каталога, расположенного на дискете, которую вы купили вместе с книгой. Если вы приобрели книгу без дискеты, воспользуйтесь исходным текстом программы, приведенным в листинге 1.1.
Листинг 1.1. Файл hello\hello.cpp
Первой строкой в программе является определение символа STRICT:
Это определение влияет на обработку файла windows.h, обеспечивая более строгую проверку типов данных. Такая проверка облегчит вам в дальнейшем преобразование исходных текстов программ для 32-разрядных приложений Win32s или Windows NT. И хотя в нашем простом примере проверять почти нечего, мы включили определение STRICT для сохранения единого стиля во всех примерах программ.
Следующая строка сообщает компилятору, что не нужно выводить предупреждающее сообщение о том, что расположенная следом функция не пользуется своими параметрами:
В нашем первом примере мы игнорировали все четыре параметра, однако из-за использования соглашения о передаче параметров PASCAL все параметры функции WinMain должны быть описаны.
Для вывода строки «Hello, world!» мы использовали функцию MessageBox:
Прототип функции MessageBox определен в файле windows.h:
Мы в нашей книге будем использовать немного измененную форму прототипа, отражающую не только типы параметров, но и назначение параметров. Прототип функции MessageBox будет выглядеть следующим образом:
Не вдаваясь в подробности, скажем, что эта функция создает на экране диалоговую панель с текстом, заданным вторым параметром lpszText (в нашем случае — с текстом «Hello, world!»), и заголовком, заданным третьим параметром lpszTitle («Main Window»).
Параметр hwndParent указывает так называемый идентификатор родительского окна, создающего диалоговую панель (его мы рассмотрим позже). Этот параметр можно указывать как NULL, в этом случае у диалоговой панели не будет родительского окна.
Первый параметр в нашем примере необходимо указать как NULL.
Последний параметр fuStyle — константа MB_OK, значение которой определено в файле windows.h. Использование в качестве последнего параметра значения MB_OK приводит к появлению в диалоговой панели одной кнопки с надписью «OK». Когда вы нажмете на эту кнопку, функция MessageBox возвратит управление в функцию WinMain.
Обратите внимание на то, что имена параметров функции MessageBox имеют префиксы. Эти префиксы используются для того, чтобы включить в имя параметра информацию о его типе. При создании приложений Windows приходится использовать очень много типов данных. Префиксы позволяют избежать ошибок, связанных с неправильным использованием параметров. Подробнее о префиксах в именах параметров и переменных вы можете узнать из приложения с названием «Имена параметров функций».
Для завершения работы приложение использует функцию return:
Значение, возвращаемое этой функцией, не используется Windows, однако может быть проанализировано отладчиком.
Теперь займемся созданием приложения, для чего воспользуемся интегрированной средой разработки Borland C++ for Windows версии 3.1 или Borland Turbo C++ for Windows.
Для создания приложения прежде всего вам нужно образовать новый prj-файл. Запустите среду разработки и из меню «Project» выберите строку «Open Project. «. На экране появится диалоговая панель «Open Project File» (рис. 1.1).
Рис. 1.1. Диалоговая панель «Open Project File»
С помощью меню «Directories» выберите и сделайте текущим созданный вами каталог с именем hello. Если в этом каталоге уже имеется файл hello.prj, выберите его и нажмите кнопку «OK». Если файла нет (вы не купили дискету с примерами программ), наберите в поле «File Name» имя hello.prj и нажмите кнопку «OK». В этом случае будет создан новый файл проекта.
При создании нового проекта в нижней части основного окна Borland C++ появится окно «Project:hello» (рис. 1.2).
Рис. 1.2. Окно «Project: hello»
В этом окне отображается список файлов, входящих в проект hello.prj. При создании нового проекта этот список пуст. Нам надо добавить в проект файл с именем hello.cpp. Для добавления файла нажмите клавишу . На экране появится диалоговая панель «Add To Project List» (рис. 1.3), с помощью которой можно добавить к проекту файл с программой, объектным модулем, библиотекой объектных модулей и т. п.
Рис. 1.3. Диалоговая панель «Add To Project List»
Выберите при помощи меню или наберите в поле «File Name» имя hello.cpp, затем нажмите кнопку «Add». В списке файлов проекта появится имя добавленного вами файла. Так как проект нашего первого приложения состоит из одного файла, после добавления файла hello.cpp нажмите кнопку «Done».
Для того чтобы открыть окно редактирования файла сделайте в окне «Project:hello» двойной щелчок левой клавишей мыши по имени файла (установите курсор мыши на имя файла и нажмите с небольшим интервалом два раза левую клавишу мыши), в нашем случае по имени hello.cpp. В главном окне появится окно редактирования (рис. 1.4).
Рис. 1.4. Окно редактирования
Если вы добавили к проекту пустой файл hello.cpp и затем открыли его двойным щелчком по имени файла, окно редактирования не будет содержать никакого текста. Наберите в нем текст программы, приведенный в листинге 1.1.
Учтите, что транслятор Borland C++ версии 3.1 для отображения текста программы использует шрифт BorlandTE, в котором нет русских букв. Если вы будете работать с русскими буквами, замените этот шрифт на другой, например Courier Cyrillic. Для этого выберите в меню «Options» строку «Environment». Затем в появившемся подменю выберите строку «Preferences». На экране появится диалоговая панель «Preferences» (рис. 1.5).
Рис. 1.5. Диалоговая панель «Preferences»
В этой диалоговой панели с помощью меню «Font» вы можете выбрать шрифт для окна редактирования. Мы рекомендуем вам также в группе переключателей «Auto Save» включить все переключатели (как это показано на рис 1.5). После внесения всех изменений нажмите кнопку «OK».
Кроме этого в меню «Options» выберите строку «Application. » и в появившейся диалоговой панели нажмите мышью пиктограмму с надписью «Windows App» и затем кнопку «OK». При этом транслятор будет настроен на создание обычных приложений Windows.
Для сохранения внесенных изменений выберите в меню «Options» строку «Save. » и в появившейся диалоговой панели нажмите кнопку «OK», предварительно убедившись, что все три имеющихся там переключателя находятся во включенном состоянии (отмечены галочкой).
Подготовив файл hello.cpp, выберите из меню «Compile» строку «Build all». На экране появится диалоговая панель «Compile Status», в которой будет отображаться ход трансляции файлов проекта и сборки файла загрузочного модуля (рис. 1.6)
Рис. 1.6. Диалоговая панель «Compile Status»
Вам надо следить за сообщениями об ошибках (Errors) и предупреждениями (Warnings). Если вы не допустили ошибок при наборе содержимого файла hello.cpp, после редактирования вы должны получить только одно предупреждающее сообщение:
Это сообщение говорит о том, что в проект не включен так называемый файл определения модуля (module definition file) и поэтому используется файл, принятый по умолчанию. Для простоты мы сознательно не стали включать в проект этот файл, но проекты всех наших следующих приложений будут содержать файл определения модуля.
После завершения процесса редактирования в поле «Status» диалоговой панели «Compile Status» появится слово Success (успех). Для продолжения работы вы должны нажать кнопку «OK».
Теперь попробуем запустить созданное нами приложение. Для этого из меню «Run» выберите строку «Run». После проверки файлов проекта на экране появится диалоговая панель, озаглавленная «Main Window» (рис. 1.7).
Рис. 1.7. Диалоговая панель «Main Window»
Она содержит текстовую строку «Hello, world!», прочитав которую, вы можете нажать кнопку «OK». Это приведет к завершению работы нашего первого приложения.
Не следует думать, что все приложения Windows так же просты, как это. Мы еще не затронули основного в Windows — окон и сообщений! Однако теперь вы умеете создавать приложения Windows, что и было нашей основной задачей на данном этапе.
1.3. Окно и функция окна
В этом разделе вы узнаете, что операционная система Windows является объектно-ориентированной средой.
Как это следует из названия операционной системы, основным объектом в Windows является окно. И это действительно так. Однако давайте уточним, что понимается под окном.
Окна Windows
С точки зрения пользователя Windows окном является прямоугольная область экрана, в которой приложение может что-либо рисовать или писать, а также выполнять все операции взаимодействия с пользователем. Например, на рис. 1.8 показано главное окно приложения Media Player, которое в данном случае используется для проигрывания звукового компакт-диска.
Рис. 1.8. Главное окно приложения Media Player
С точки зрения программиста то, что изображено на рис. 1.8, является совокупностью большого количества отдельных объектов, которые созданы приложением Media Player и самой операционной системой Windows. Для каждого объекта в приложении имеются свои данные и методы. Все эти объекты обычно называются окнами.
Такие объекты, как пиктограмма системного меню, кнопка минимизации, отдельные фрагменты толстой рамки, предназначенной для изменения размера основного окна, заголовок окна с надписью «Media Player — CD Audio (stopped)», а также полоса меню, — не что иное, как различные окна, создаваемые самой операционной системой Windows. Приложение не принимает никакого участия в формировании этих окон, оно просто указывает Windows, какие из перечисленных выше элементов необходимо создать.
Иное дело область, располагающаяся ниже полосы меню и ограниченная рамкой. Эта область представляет собой место, в котором приложение может рисовать и создавать новые окна.
Кнопки управления проигрывателем компакт-дисков, в качестве которого используется дисковод CD-ROM, представляют собой окна небольшого размера, создаваемые приложением. Приложение само формирует рисунок внутри такого окна. Ползунок и кнопки, расположенные справа от ползунка, также сформированы приложением из нескольких окон.
Обычно приложение создает одно, главное окно большого размера, которое ограничено сверху полосой меню (если есть меню) или заголовком и рамкой изменения размера с других сторон. Можно создать такое окно, которое не имеет ни рамки, ни заголовка, ни меню, ни других стандартных элементов, таких как пиктограмма системного меню или кнопки минимизации или максимизации размеров главного окна.
Внутри главного окна приложение может рисовать геометрические фигуры и графические изображения, писать текст или создавать любое количество окон меньшего размера.
Таким образом, любое приложение Windows можно рассматривать как совокупность окон, внутри которых можно что-либо рисовать или писать. Для каждого окна в приложении определены данные и методы, предназначенные для работы с этими данными (в частности, для рисования в окне).
Окна Windows как объекты
Все окна, формируемые приложением или операционной системой Windows для приложения, можно рассматривать как объекты, над которыми можно выполнять различные операции.
Проведем параллель с языком C++. В терминах языка C++ объект называется классом, который представляет из себя совокупность данных и методов, с помощью которых эти данные должны обрабатываться. Например, вы можете определить класс как совокупность простых переменных и структур, а также функций-членов (нам больше нравится название «функция-метод» или просто «метод»), выполняющих обработку данных, записанных в этих переменных или структурах.
В операционной системе Windows объектами, с которыми можно что-либо делать, являются окна — те самые окна, из которых формируется «внешний вид» приложения.
Для каждого такого окна приложение должно создать свои данные и свой набор методов, то есть функций, реагирующих на те или иные действия, которые выполняет над окнами оператор или операционная система.
Что это за действия?
Например, вы можете щелкнуть левой (или правой) кнопкой мыши в то время, когда курсор мыши находится над окном. Это событие, на которое окно может реагировать, а может и не реагировать. Вы можете щелкнуть мышью по любому окну, принадлежащему приложению, и каждое окно должно реагировать на это по-своему.
В Windows существует механизм, позволяющий задать для каждого окна данные и набор методов обработки событий, имеющих отношение к любому окну, созданному приложением.
Этот механизм основан на использовании так называемой функции окна (window function) и сообщений.
Функция окна
Функция окна — это обыкновенная (почти) функция языка С, которая определяется для одного окна или группы окон. Каждый раз, когда происходит какое-либо событие, имеющее отношение к окну (например, щелчок мышью в окне), операционная система Windows вызывает соответствующую функцию окна и передает ей параметры, описывающие событие. Функция окна анализирует эти параметры и выполняет соответствующие действия.
При возникновении события операционная система Windows формирует сообщение, описывающее событие, и затем направляет его в нужную функцию окна. В качестве параметров, передаваемых функции окна, используются отдельные компоненты сообщения, соответствующего событию (или, иными словами, созданному в результате появления события). Поэтому основная задача функции окна — обработка сообщений, распределяемых окну операционной системой Windows.
Можно считать, что единственная для каждого окна функция окна реализует все методы окна как объекта. В языке программирования C++, напротив, каждый метод объекта (класса) реализуется отдельной функцией, называемой обычно функцией-членом. Для реализации всех методов функция окна анализирует код сообщения, однозначно идентифицирующий событие и, следовательно, определяющий нужный метод.
В объектно-ориентированных языках программирования используется такое понятие, как наследование. Объекты могут наследовать методы других объектов. В операционной системе Windows также предусмотрен механизм наследования методов. Он реализуется с использованием так называемых классов окна.
Для каждого класса окна определяется функция окна. При создании окна необходимо указать, к какому классу оно будет принадлежать и, соответственно, какую функцию окна будет использовать для обработки сообщений. Приложения могут создавать собственные классы окна, определяя свои функции окна (и следовательно, свои методы), либо использовать стандартные, определенные в Windows классы окна.
Приведем пример. Пусть, например, нам надо создать окно, выполняющее функцию кнопки. Мы можем создать собственный класс окна и для него определить собственную функцию окна. Эта функция будет обрабатывать сообщения и при необходимости изображать в окне нажатую или отжатую кнопку, а также выполнять другие действия. Однако в Windows уже определен класс окна, соответствующий кнопкам. Если вы воспользуетесь этим классом, вам не придется создавать свою функцию окна, так как будет использоваться функция, уже имеющаяся в Windows и выполняющая все необходимые действия.
Любое создаваемое вами окно может наследовать свойства уже созданных ранее окон, добавляя свои или переопределяя уже имеющиеся в базовом классе методы. В этом и заключается механизм наследования Windows.
Использование механизма наследования значительно упрощает процесс создания приложений, так как для большинства стандартных органов управления, таких, как кнопки, меню, полосы просмотра и т. п., в операционной системе Windows уже определены классы окон и все необходимые методы.
1.4. Обработка сообщений
Откуда берутся сообщения?
Большинство сообщений создают драйверы устройств ввода/вывода, таких, как клавиатура, мышь или таймер. Драйверы создают сообщения при поступлении аппаратных прерываний. Например, когда вы нажимаете и затем отпускаете клавишу, драйвер обрабатывает прерывания от клавиатуры и создает несколько сообщений. Аналогично сообщения создаются при перемещении мыши или в том случае, когда вы нажимаете кнопки на корпусе мыши. Можно сказать, что драйверы устройств ввода/вывода транслируют аппаратные прерывания в сообщения.
Куда направляются сообщения, созданные драйверами?
Прежде всего сообщения попадают в системную очередь сообщений Windows. Системная очередь сообщений одна. Далее из нее сообщения распределяются в очереди сообщений приложений. Для каждого приложения создается своя очередь сообщений.
Очередь сообщения приложений может пополняться не только из системной очереди. Любое приложение может послать сообщение любому другому сообщению, в том числе и само себе.
Основная работа, которую должно выполнять приложение, заключается в обслуживании собственной очереди сообщений. Обычно приложение в цикле опрашивает свою очередь сообщений. Обнаружив сообщение, приложение с помощью специальной функции из программного интерфейса Windows распределяет его нужной функции окна, которая и выполняет обработку сообщения.
Когда вы посмотрите на исходный текст нашего первого приложения, обрабатывающего сообщения, вас может удивить тот факт, что функция WinMain не выполняет никакой работы, имеющей отношение к поведению приложения. Внутри этой функции находятся инициализирующий фрагмент и цикл обработки сообщений (Message Loop). Вся основная работа выполняется в функции окна, которой передаются.
Грубо говоря, приложения Windows похожи на загружаемые драйверы MS-DOS. Эти драйверы также состоят из инициализирующего фрагмента и функции, обрабатывающей команды, выдаваемые драйверу. Приложение Windows после инициализации переходит в состояние постоянного опроса собственной очереди сообщений. Как только происходит какое-либо событие, имеющее отношение к приложению, в очереди приложения появляется сообщение и приложение начинает его обрабатывать. После обработки приложение вновь возвращается к опросу собственной очереди сообщений. Иногда функция окна может получать сообщения непосредственно, минуя очередь приложения.
Так как Windows — мультизадачная операционная система, ее разработчики должны были предусмотреть механизм совместного использования несколькими параллельно работающими приложениями таких ресурсов, как мышь и клавиатура. Так как все сообщения, создаваемые драйверами мыши и клавиатуры, попадают в системную очередь сообщений, должен существовать способ распределения этих сообщений между различными приложениями.
В Windows существует понятие фокуса ввода (input focus), помогающее в распределении сообщений. Фокус ввода — это атрибут, который в любой момент времени может относиться только к одному окну. Если окно имеет фокус ввода, все сообщения от клавиатуры распределяются сначала в очередь сообщений приложения, создавшего окно, а затем — функции окна, владеющего фокусом ввода. Нажимая определенные клавиши, вы можете перемещать фокус ввода от одного окна к другому. Например, если вы работаете с диалоговой панелью, содержащей несколько окон, предназначенных для ввода текста, с помощью клавиши вы можете переключать фокус с одного такого окна на другое и вводить текст в различные окна диалоговой панели. Существуют также комбинации клавиш, с помощью которых вы можете переключиться на другое приложение. При этом фокус ввода будет отдан другому окну, принадлежащему другому приложению.
Сообщения от драйвера мыши всегда передаются функции того окна, над которым находится курсор мыши. При необходимости приложение может выполнить операцию захвата (capturing) мыши. В этом случае все сообщения от мыши будут поступать в очередь приложения, захватившего мышь, вне зависимости от положения курсора мыши.
Простейший цикл обработки сообщений состоит из вызовов двух функций — GetMessage и DispatchMessage.
Функция GetMessage предназначена для выборки сообщения из очереди приложения. Сообщение выбирается из очереди и записывается в область данных, принадлежащую приложению.
Функция DispatchMessage предназначена для распределения выбранного из очереди сообщения нужной функции окна. Так как приложение обычно создает много окон и эти окна используют различные функции окна, необходимо распределить сообщение именно тому окну, для которого оно предназначено. Поэтому приложение должно распределить сообщение после его выборки из очереди приложения, в котором находятся сообщения для всех окон. Windows оказывает приложению существенную помощь в решении этой задачи — для распределения приложению достаточно вызвать функцию DispatchMessage.
Вот как выглядит простейший вариант цикла обработки сообщений:
Завершение цикла обработки сообщений происходит при выборке из очереди специального сообщения, в ответ на которое функция GetMessage возвращает нулевое значение. Тип MSG описан в файле windows.h.
Процесс обработки сообщений схематически показан на рис. 1.9.
Рис. 1.9. Процесс обработки сообщений
На этом рисунке слева показаны ресурсы и подсистемы Windows, справа — приложения. Сообщения поступают от драйверов таких устройств, как клавиатура, мышь, таймер, и попадают в системную очередь сообщений. Из системной очереди сообщений Windows выбирает сообщения, предназначенные для приложения, и помещает их в очередь сообщения приложения.
Функция WinMain в цикле обработки сообщений с помощью функции GetMessage выбирает сообщения из очереди сообщений приложения и распределяет их функциям окон, вызывая функцию DispatchMessage.
На нашем рисунке для простоты показано только одно приложение, в котором определена только одна функция окна. Реально же существует много приложений, и каждое приложение имеет несколько функций окна. Каждое приложение вызывает в цикле обработки сообщений функцию GetMessage, выбирая сообщения из своей очереди. Затем каждое приложение распределяет выбранное сообщение одной из своих функций окна, для чего используется функция DispatchMessage.
Функция окна получает сообщения при создании окна, в процессе работы приложения, а также при разрушении окна.
Сообщение с кодом WM_CREATE передается функции окна в момент создания окна. Функция окна при обработке этого сообщения выполняет инициализирующие действия (аналогично конструктору класса в языке C++). Коды сообщений определены в файле windows.h, включаемом в исходные тексты любых приложений Windows.
В процессе работы приложения функция окна может получать сообщения с различными кодами как через очередь сообщений приложения, так и непосредственно, в обход очереди сообщений. Обработчики этих сообщений, определенные в функции окна, являются методами для работы с окном как с объектом.
При разрушении структуры данных окна (при уничтожении окна) функция окна получает сообщение с кодом WM_DESTROY. Обработчик этого сообщения действует как деструктор. Если ваша функция окна во время обработки сообщения WM_CREATE создала какие-либо структуры данных, эти структуры должны быть разрушены (а заказанная для них память возвращена операционной системе) во время обработки сообщения WM_DESTROY.
1.5. Приложение с обработкой сообщений
В этом разделе мы рассмотрим простейшее приложение Windows, содержащее цикл обработки сообщений. Это приложение имеет только одно окно и одну функцию окна, однако это только начало.
Алгоритм работы приложения
Если вы, забегая вперед, посмотрите листинги файлов нашего приложения, то обратите внимание на то, что оно имеет несколько необычную (с точки зрения программиста, составляющего программы для MS-DOS) структуру. В частности, функция WinMain после выполнения инициализирующих действий входит в цикл обработки сообщений, после выхода из которого работа приложения завершается. Функция WndProc вообще не вызывается ни из какой другой функции приложения, хотя именно она выполняет всю «полезную» работу.
Составляя программы для MS-DOS, вы привыкли к тому, что за весь сценарий работы программы отвечает функция main. Эта функция выполняет вызов всех остальных функций (за исключением функций обработки прерываний), из которых и состоит программа.
Логика работы приложения Windows другая. Прежде всего выполняются инициализирующие действия, связанные, например, с определением классов, на базе которых в дальнейшем (или сразу) будут создаваться окна приложения. Для каждого класса необходимо указать адрес функции окна. Эта функция будет обрабатывать сообщения, направляемые окнам, создаваемым на базе класса.
Однако процесс распределения сообщений функциям окон, созданных приложением, происходит не сам по себе. Приложение должно само организовать этот процесс, для чего после выполнения инициализирующих действий в функции WinMain запускается цикл обработки сообщений.
Обработка сообщений, которые операционная система Windows посылает в очередь приложения (во время цикла обработки сообщений), выполняется соответствующей функцией окна.
Наше первое приложение с обработкой сообщений определяет один класс окна и на его базе создает одно, главное окно. Для обработки сообщений, поступающих в это окно, приложение определяет одну функцию окна.
Функция окна обрабатывает три сообщения с кодами WM_LBUTTONDOWN, WM_RBUTTONDOWN и WM_DESTROY.
Сообщение WM_LBUTTONDOWN записывается в очередь приложения и передается функции окна, когда вы устанавливаете курсор мыши внутри главного окна приложения и нажимаете левую клавишу мыши. В ответ на это сообщение функция окна выводит диалоговую панель с сообщением о том, что нажата левая клавиша мыши.
Сообщение WM_RBUTTONDOWN аналогично предыдущему, но оно записывается в очередь сообщений приложения, когда вы нажимаете правую кнопку мыши. В ответ на это сообщение функция выводит диалоговую панель с сообщением и дополнительно выдает звуковой сигнал.
Последнее сообщение, WM_DESTROY, передается приложению при разрушении структуры данных, связанной с окном. В нашем примере при завершении работы приложения разрушается главное (и единственное) окно. В ответ на это сообщение функция окна помещает в очередь приложения специальное сообщение с идентификатором WM_QUIT. Выборка этого сообщения в цикле обработки сообщений приводит к завершению цикла и, соответственно, к завершению работы приложения.
Схематически алгоритм работы функции WinMain приложения можно представить следующим образом:
Адрес функции окна указывается при создании класса окна. Этот адрес используется операционной системой Windows для вызова функции окна (напомним, что приложение само не вызывает функцию окна). Приведем алгоритм работы функции окна нашего простейшего приложения:
Из приведенного выше алгоритма работы функции окна видно, что наша функция обрабатывает только три сообщения. Все остальные сообщения, которые попадают в очередь приложения и распределяются функции окна (а их очень много), также должны быть обработаны. Для этого необходимо использовать функцию программного интерфейса Windows с именем DefWindowProc.
Если ваша функция окна проигнорирует вызов функции DefWindowProc для тех сообщений, которые она сама не обрабатывает, Windows не сможет обработать такие сообщения. Это может привести к неправильной работе или блокировке как приложения, так и всей операционной системы Windows.
Листинги файлов приложения
В отличие от первого приложения (листинг 1.1) новое приложение состоит из двух файлов — файла window.cpp (листинг 1.2), содержащего исходный текст функций приложения, и файла window.def (листинг 1.3), который является файлом определения модуля.
В файле window.cpp определены функции WinMain, InitApp и функция окна WndProc, то есть все функции, из которых состоит приложение. Файл window.def содержит инструкции редактору связей. Эти инструкции используются при создании загрузочного exe-файла приложения.
Рассмотрим подробно файл window.cpp.
Листинг 1.2. Файл window\window.cpp
Определения типов, констант и функций
Первые две строки используются для определения типов данных и констант:
Определение символа STRICT, сделанное до включения файла windows.h, позволит выполнить более строгую проверку типов данных. Просмотрев исходный текст файла windows.h (этот файл находится в подкаталоге include каталога, в который вы выполняли установку транслятора), вы обнаружите, что способ определения многих типов и функций сильно зависит от того, был ли ранее определен символ STRICT.
Далее в программе приведены прототипы двух функций — InitApp и WndProc.
После прототипов определены две строки символов, содержащие имя класса окна (szClassName) и заголовок окна (szWindowTitle). Имя класса окна используется при создании класса окна, а заголовок нужен, разумеется, для того, чтобы озаглавить создаваемое главное окно приложения. Так как мы в приложении не собираемся изменять эти строки, они описаны как const.
Функция WinMain определена так же, как и в нашем самом первом приложении (листинг 1.1.). В области стека функции созданы две переменные с именами msg и hwnd:
Переменная msg представляет собой структуру типа MSG, описанную в файле windows.h следующим образом:
Эта переменная предназначена для временного хранения сообщений и используется в цикле обработки сообщений.
Переменная hwnd имеет тип HWND, также описанный в файле windows.h, и используется для хранения идентификатора (handle) главного окна приложения. Заметьте, что в структуре MSG также присутствует поле с типом HWND. Это поле используется для хранения идентификатора окна, к которому относится сообщение.
В операционной системе Windows очень широко практикуется использование идентификаторов для ссылки на различные ресурсы. При определении символа STRICT все такие идентификаторы имеют различный тип, поэтому вы не сможете по ошибке выполнить присвоение значений идентификаторов, относящихся к разным ресурсам.
Вы уже знакомы с двумя типами идентификаторов — идентификатор копии приложения HINSTANCE и идентификатор окна HWND. Существуют десятки других типов идентификаторов, о которых мы будем рассказывать по мере изучения программного интерфейса Windows.
Инициализация приложения
Первое, что делает функция WinMain после запуска приложения, это проверяет наличие уже запущенной ранее копии этого же приложения:
Как мы уже говорили, параметр hPrevInstance функции WinMain содержит нуль, если приложение запустили в первый раз, или идентификатор предыдущей копии приложения, запущенной ранее и работающей на момент запуска текущей копии.
Если параметр hPrevInstance равен нулю, функция WinMain вызывает функцию инициализации приложения InitApp. В противном случае на экран выводится сообщение о невозможности запуска второй копии приложения.
Вы можете разрешить запуск нескольких копий приложения, убрав соответствующую проверку. В данном случае нет никаких причин запрещать параллельную работу нескольких приложений. Мы сделали это только для иллюстрации использования параметра hPrevInstance .
Обратите внимание на вызов функции MessageBox. В качестве последнего параметра указано значение MB_OK | MB_ICONSTOP. Последний параметр функции представляет собой набор битовых флагов, которые мы рассмотрим позже. Флаг MB_OK предназначен для создания на диалоговой панели кнопки с надписью «OK», флаг MB_ICONSTOP нужен для изображения пиктограммы с надписью «STOP» (рис. 1. 10).
Рис. 1.10. Сообщение об ошибке
Регистрация класса окна
Задача функции InitApp — регистрация класса окна. На базе этого класса будет создано главное окно приложения.
Если допускается одновременная работа нескольких копий одного приложения, регистрация класса окна должна выполняться только один раз первой копией приложения.
В области локальных переменных функции определены две переменные — aWndClass и wc:
Переменная aWndClass используется для временного хранения кода возврата функции RegisterClass. Эта функция относится к функциям программного интерфейса Windows, она и выполняет регистрацию класса. В качестве единственного параметра функции необходимо указать адрес соответствующим образом подготовленной структуры типа WNDCLASS:
Приведем прототип функции RegisterClass:
Таким образом, процедура регистрации класса окна является несложной. Вам достаточно подготовить одну структуру и вызвать функцию RegisterClass.
Для вас, возможно, непривычно использование переменной специального типа ATOM для передачи результата выполнения функции. Однако такое использование не создает никаких дополнительных трудностей. Тип ATOM отображается на тип UINT, который, в свою очередь, отображается на тип unsigned int (см. файл windows.h):
Переменные типа ATOM используются как идентификаторы текстовых строк (атомы), хранящихся в области памяти, принадлежащей операционной системе Windows. Существует набор функций для работы с этими идентификаторами (для работы с атомами), который мы сейчас не будем рассматривать. Отметим только, что в этом наборе есть функции для получения адреса строки, соответствующей идентификатору, для создания и удаления, а также поиска идентификаторов.
В нашем приложении функция InitApp использует переменную типа ATOM для формирования кода возврата:
Если регистрация класса произошла успешно, функция RegisterClass возвращает атом с ненулевым значением, при этом функция InitApp возвращает значение TRUE. Последнее означает, что инициализация приложения выполнена без ошибок.
Теперь займемся структурой WNDCLASS, которая используется для регистрации класса окна. Эта структура определена в файле windows.h:
Перед регистрацией вам необходимо заполнить все поля в этой структуре.
Поле style определяет стиль класса и задается в виде констант (описанных, как всегда, в файле windows.h), имя которых начинается с префикса CS_, например CS_HREDRAW, CS_VREDRAW. Стиль задает реакцию окна на изменение его размера, на выполнение в окне операции двойного щелчка мышью (double click), а также позволяет определить другие характеристики окна, создаваемого на базе данного класса. Например, если для стиля задать значение CS_HREDRAW | CS_VREDRAW, при изменении вертикального или горизонтального размера окна приложение должно его перерисовать, то есть нарисовать заново все или часть того, что было изображено в окне до изменения размера.
В нашем приложении стиль класса не используется, поэтому для него задается нулевое значение:
В поле lpfnWndProc необходимо записать адрес функции окна, которая будет выполнять обработку сообщений, поступающих во все окна, созданные на базе данного класса. Имя функции окна можно выбрать любое. В нашем случае используется имя WndProc, хотя вы можете использовать другое имя. Запись адреса функции окна должна выполняться следующим образом:
Поле lpfnWndProc имеет тип WNDPROC (дальний указатель на функцию), который мы рассмотрим чуть позже, при описании функции окна. Для того чтобы избежать получения от компилятора предупреждающего сообщения о несоответствии типов, вы должны использовать явное преобразование типа.
Поле cbClsExtra используется для резервирования дополнительной памяти, общей и доступной для всех окон, создаваемых на базе данного класса.
Чтобы это было понятно, отметим, что при регистрации класса окна в памяти, принадлежащей операционной системе Windows, резервируется и заполняется некоторая область (структура данных). В этой области хранится вся информация о классе, необходимая для создания окон на базе этого класса. Вы можете увеличить размер области описания класса для хранения своей собственной информации, предназначенной для всех создаваемых на базе этого класса окон. Поле cbClsExtra определяет размер дополнительной памяти в байтах. В программном интерфейсе Windows имеются специальные функции, предназначенные для работы с дополнительной областью памяти.
Наше приложение не создает в описании класса никаких дополнительных областей, поэтому для заполнения поля cbClsExtra используется нулевое значение:
Так же как и при создании нового класса, при создании нового окна Windows резервирует в своей памяти область, описывающую окно. С помощью параметра cbWndExtra вы можете увеличить размер этой области для хранения информации, имеющей отношение к создаваемому окну. В нашем случае размер области описания окна не увеличивается, поэтому для заполнения поля cbWndExtra используется нулевое значение:
Поле hInstance перед регистрацией класса окна должно содержать идентификатор приложения, создающего класс окна. В качестве этого идентификатора следует использовать значение, полученное функцией WinMain в параметре hInstance:
Следующее поле имеет имя hIcon и тип HICON. Это идентификатор пиктограммы, в которую превращается окно при уменьшении его размеров до предела (при минимизации окна).
В нашем приложении мы указываем пиктограмму, используемую Windows по умолчанию. Для Microsoft Windows версии 3.1 вид этой пиктограммы приведен на рис. 1.11.
Рис. 1.11. Пиктограмма приложения
Для загрузки пиктограммы в приложении вызывается функция программного интерфейса Windows с именем LoadIcon:
Прототип функции LoadIcon:
Первый параметр функции (hinst) содержит идентификатор приложения, второй (pszicon) — имя ресурса-пиктограммы.
Позже мы научим вас определять для окон собственные пиктограммы, нарисованные с помощью приложения Resource Workshop, входящего в комплект поставки Borland C++ for Windows.
В поле hCursor (имеющем тип HCURSOR) вы можете задать вид курсора мыши при его прохождении над окном. Вы знаете, что курсор мыши меняет свою форму при перемещении над различными окнами приложений Windows. При регистрации класса окна вы можете указать форму курсора, для чего и используется поле hCursor.
В нашем приложении мы задаем курсор в виде стандартной стрелки, для чего вызываем функцию LoadCursor и указываем в качестве второго параметра константу IDC_ARROW:
Прототип функции LoadCursor:
Вы можете определить для окна свой курсор, нарисовав его аналогично пиктограмме при помощи такого приложения, как Resource Workshop или Microsoft SDKPaint. Однако пока для простоты мы будем использовать стандартный курсор.
Далее нам необходимо заполнить поле hbrBackground, имеющее тип HBRUSH. Это поле позволяет определить кисть (brush), которая будет использована для закрашивания фона окна. В качестве кисти можно использовать «чистые» цвета или небольшую пиктограмму размером 8 х 8 точек.
В нашем приложении мы использовали системный цвет, который Windows использует для закрашивания фона окон:
Системный цвет можно изменять при помощи приложения Control Panel. Позже мы научим вас задавать для фона окна другие цвета и раскрашивать окно при помощи пиктограмм.
Поле lpszMenuName (указатель на строку типа LPCSTR) определяет меню, располагающееся в верхней части окна. Если меню не используется, при заполнении этого поля необходимо использовать значение NULL:
Тип LPCSTR определяется как константный дальний указатель на строку символов:
Очень важно поле lpszClassName. В это поле необходимо записать указатель на текстовую строку, содержащую имя регистрируемого класса окон:
На этом подготовку структуры wc к регистрации класса окна можно считать законченной. Можно вызывать функцию RegisterClass.
После регистрации функция InitApp возвращает управление обратно в функцию WinMain.
Создание главного окна приложения
Далее приложение вызывает функцию CreateWindow для того, чтобы создать главное окно приложения:
В случае успеха функция CreateWindow возвращает идентификатор окна (типа HWND). Если окно создать не удалось, функция возвращает нулевое значение.
Приведем прототип функции CreateWindow:
Многочисленные параметры функции CreateWindow дополняют описание окна, сделанное при создании класса окна.
Первый параметр функции (lpszClassName) — указатель на строку, содержащую имя класса, на базе которого создается окно. В процессе инициализации приложения мы зарегистрировали класс с именем «WindowAppClass» (переменная szClassName).
Второй параметр функции (lpszWindowName) — указатель на строку, содержащую заголовок окна (Title Bar). В нашем случае окно будет иметь заголовок «Window Application» (переменная szWindowTitle).
Третий параметр (dwStyle) — стиль создаваемого окна. Этот параметр задается как логическая комбинация отдельных битов. Константа WS_OVERLAPPEDWINDOW соответствует окну, которое может перекрывать другие окна, имеет заголовок, системное меню, кнопки для минимизации и максимизации окна, а также рамку вокруг окна, с помощью которой можно изменять размер окна. Операционная система Windows позволяет задавать различные стили для создаваемых окон. Мы их рассмотрим в дальнейшем.
Четвертый и пятый параметры функции CreateWindow для окна данного стиля определяют горизонтальную (x) и вертикальную (y) координату относительно верхнего левого угла экрана видеомонитора.
Шестой и седьмой параметры определяют ширину (nWidth) и высоту (nHeight) создаваемого окна.
Наше приложение в качестве координат окна и его размеров использует константу CW_USEDEFAULT. При этом операционная система Windows сама определяет положение и размеры создаваемого окна.
Восьмой параметр (hwndParent) определяет индекс родительского окна. Для нашего приложения в качестве значения используется нуль, так как в приложении создается только одно окно.
Девятый параметр (hmenu) — идентификатор меню или идентификатор порожденного (child) окна. В нашем случае никакого меню или порожденного окна нет, поэтому в качестве значения используется нуль.
Десятый параметр (hinst) — идентификатор приложения, которое создает окно. Необходимо использовать значение, передаваемое функции WinMain через параметр hInstance.
Одиннадцатый, последний параметр функции (lpvParam) представляет собой дальний указатель на область данных, определяемых приложением. Этот параметр передается в функцию окна вместе с сообщением WM_CREATE при создании окна. Наше приложение не пользуется этим параметром.
Отображение окна на экране
Итак, окно создано. Однако на экране оно еще не появилось, в чем вы можете убедиться, запустив приложение под управлением отладчика. Поэтому, проверив, что создание окна выполнено успешно (функция CreateWindow вернула ненулевое значение), необходимо сделать окно видимым (нарисовать его на экране). Это можно сделать с помощью функции ShowWindow:
Функция отображает окно, идентификатор которого задан первым параметром (hwnd), в нормальном, максимально увеличенном или уменьшенном до пиктограммы виде, в зависимости от значения второго параметра (nCmdShow). Наше приложение использует в качестве второго параметра значение, передаваемое функции WinMain через параметр nCmdShow.
После отображения окна в нормальном или максимально увеличенном виде внутренняя поверхность окна закрашивается кистью, определенной при регистрации класса.
Внешний вид окна, создаваемого нашим приложением, показан на рис. 1.12.
Рис. 1.12. Главное окно приложения
Сразу после функции ShowWindow в приложении вызывается функция UpdateWindow.
Функция UpdateWindow вызывает функцию окна, заданного идентификатором, передаваемым в качестве параметра hwnd, и передает ей сообщение WM_PAINT. Получив это сообщение, функция окна должна перерисовать все окно или его часть. Наше приложение не обрабатывает это сообщение, передавая его функции DefWindowProc. Сообщение WM_PAINT и способ его обработки будут описаны позже, когда мы займемся рисованием в окне.
Цикл обработки сообщений
После отображения окна функция WinMain запускает цикл обработки сообщений:
Функция GetMessage предназначена для выборки сообщений из очереди приложения и имеет следующий прототип:
Первый параметр функции (lpmsg) является дальним указателем на структуру типа MSG, в которую будет записано выбранное из очереди сообщение. Тип LPMSG определен в файле windows.h следующим образом:
Второй параметр (hwnd) является идентификатором окна, для которого необходимо выбрать сообщение из очереди приложения.
Очередь приложения содержит сообщения, предназначенные для всех окон, созданных приложением. Если в качестве второго параметра функции GetMessage указать нуль, будет выполняться выборка всех сообщений, предназначенных для всех окон приложения. В нашем случае приложение создает только одно окно, поэтому можно указать либо идентификатор созданного окна, либо нуль.
Третий (uMsgFilterMin) и четвертый (uMsgFilterMax) параметры функции GetMessage позволяют определить диапазон сообщений, выбираемых из очереди приложения, задавая соответственно минимальное и максимальное значение кодов выбираемых сообщений. Если для этих параметров указать нулевые значения (как это сделано в нашем приложении), из очереди приложения будут выбираться все сообщения.
Как работает функция GetMessage?
Эта функция может выбирать сообщения только из очереди того приложения, которое ее вызывает. Если очередь сообщений приложения пуста или содержит только сообщения с низким приоритетом, функция GetMessage передает управление другим работающим приложениям, обеспечивая невытесняющую мультизадачность. Таким образом, проверяя очередь сообщений, приложение может передать управление другим приложениям. Эти приложения, в свою очередь, тоже вызывают функцию GetMessage. Таким образом, приложения распределяют между собой процессорное время.
Выбранное функцией GetMessage сообщение удаляется из очереди сообщений приложения и записывается в структуру, адрес которой задан первым параметром функции.
Если из очереди выбирается сообщение с кодом WM_QUIT, функция GetMessage возвращает значение FALSE. В этом случае приложение должно завершить цикл обработки сообщений. При выборке из очереди любых других сообщений функция GetMessage возвращает значение TRUE.
После выборки сообщения из очереди в цикле обработки сообщений его необходимо распределить функции окна, для которой это сообщение предназначено. Для этого должна быть использована функция программного интерфейса Windows с именем DispatchMessage. Эта функция имеет следующий прототип:
Единственный параметр функции (lpmsg) является указателем на структуру, содержащую сообщение. Функция DispatchMessage возвращает значение, полученное при возврате из функции окна. Обычно это значение игнорируется приложением.
Даже если ваше приложение содержит только одно окно и одну функцию окна, вы не должны вызывать функцию окна самостоятельно. Функция окна имеет нестандартный пролог и эпилог, поэтому ее прямой вызов может привести к аварийному завершению приложения. Единственный правильный способ вызова функции окна в цикле обработки сообщений — косвенный вызов при помощи функции DispatchMessage.
С помощью специальных функций, таких, как SendMessage или CallWindowProc, вы все же можете при необходимости вызвать функцию окна. Однако в цикле обработки сообщений следует использовать именно функцию DispatchMessage, так как она для каждого сообщения вызывает именно ту функцию окна, которой это сообщение предназначено.
Завершение работы приложения
Если операционная система Windows завершает свою работу, функциям окна каждого работающего приложения передается сообщение WM_QUERYENDSESSION. Обрабатывая это сообщение соответствующим образом, приложение может завершить свою работу, предварительно сохранив все необходимые данные. Если, например, вы создали документ в текстовом процессоре, а затем, не сохранив его, попытаетесь завершить работу Windows, текстовый процессор получит сообщение WM_QUERYENDSESSION, в ответ на которое он может попросить вас сохранить документ, отказаться от сохранения или от завершения работы Windows.
Функция окна
В нашем приложении был создан класс окна, в котором определена функция окна с именем WndProc:
Мы уже говорили, что эта функция нестандартна и ее нельзя вызывать напрямую из функции WinMain или из какой-либо другой функции.
Функция окна должна иметь следующий прототип (имя функции окна может быть любым, а не только WndProc):
Тип LRESULT определен в файле windows.h следующим образом:
Таким образом, функция окна возвращает значение с типом LONG, что в текущей реализации Windows (версия 3.1) соответствует двойному слову.
Модификатор CALLBACK указывает на соглашения о передаче параметров _pascal и определяет функцию окна как функцию _far:
Функция окна, так же как и функция WinMain (и все функции программного интерфейса Windows), использует для передачи параметров соглашения языка Паскаль.
Вы могли бы описать функцию окна как long _far _pascal, однако для обеспечения возможности переноса вашей программы в 32-разрядные версии Windows (Windows NT) или для использования 32-разрядного расширения Win32s текущей версии Windows следует пользоваться символами LRESULT и CALLBACK.
Обратите внимание на ключевое слово _export, которое используется в определении функции окна. Если описать функцию с этим ключевым словом, ее имя станет экспортируемым. При этом функция будет иметь специальный пролог и эпилог.
Пролог и эпилог экспортируемых функций представляет собой небольшое добавление в начало и конец функции, обеспечивающее возможность вызова функции из ядра операционной системы Windows. В частности, пролог обеспечивает экспортируемой функции доступ к сегменту данных приложения при вызове этой функции из модулей операционной системы Windows.
В отличие от обычных функций, вызываемых вашим приложением, функция окна вызывается не приложением, а операционной системой Windows. Для обеспечения возможности такого вызова функция должна быть определена с ключевым словом _export или описана специальным образом в модуле определения файла, который будет рассмотрен позже.
Займемся теперь параметрами функции окна.
Первый параметр является индексом окна, для которого предназначено сообщение. Напомним, что адрес функции окна указывается при регистрации класса окна:
На базе одного класса может быть создано несколько окон, каждое из которых имеет собственный идентификатор. Для обработки сообщений, поступающих в окна, созданные на базе одного класса, используется общая функция окна. Функция окна может определить окно, для которого предназначено сообщение, анализируя свой первый параметр. В нашем случае имеется только одно окно, поэтому идентификатор окна не используется.
Следующие три параметра функции окна соответствуют полям msg, wParam и lParam структуры MSG. В поле msg записывается код сообщения, поля wParam и lParam описывают дополнительную информацию, передаваемую в функцию окна вместе с сообщением. Формат этой информации зависит от кода сообщения.
В нашем приложении функция окна представляет собой переключатель, выполняющий различные действия для сообщений с разными кодами. Сообщения WM_LBUTTONDOWN, WM_RBUTTONDOWN и WM_DESTROY обрабатываются функцией окна, остальные передаются функции DefWindowProc.
Сообщение WM_LBUTTONDOWN попадает в функцию окна, когда вы устанавливаете курсор мыши в окно приложения и нажимаете левую клавишу мыши. При этом функция окна вызывает функцию MessageBox и на экране появляется диалоговая панель с сообщением о том, что была нажата левая клавиша мыши (рис. 1.13).
Рис. 1.13. Диалоговое окно с сообщением
Аналогично сообщение WM_RBUTTONDOWN попадает в функцию окна, когда вы устанавливаете курсор мыши в окно приложения и нажимаете правую клавишу мыши. В этом случае функция окна вызывает функцию MessageBeep с параметром -1 и затем функцию MessageBox для вывода сообщения о том, что была нажата правая клавиша мыши.
Приведем прототип функции MessageBeep:
Параметр функции MessageBeep позволяет выбрать один из нескольких системных звуковых сигналов:
Параметр | Описание |
-1 | Проигрывание стандартного звукового сигнала с помощью громкоговорителя, установленного в корпусе компьютера |
MB_ICONASTERISK | Воспроизведение звука, указанного в строке SystemAsterisk раздела [sounds] файла win.ini |
MB_ICONEXCLAMATION | Воспроизведение звука, указанного в строке SystemExclamation раздела [sounds] файла win.ini |
MB_ICONHAND | Воспроизведение звука, указанного в строке SystemHand раздела [sounds] файла win.ini |
MB_ICONQUESTION | Воспроизведение звука, указанного в строке SystemQuestion раздела [sounds] файла win.ini |
MB_OK (значение этой константы равно 0) | Воспроизведение звука, указанного в строке SystemDefault раздела [sounds] файла win.ini |
Функцию MessageBeep удобно использовать для отладки, задавая ей в качестве параметра значение -1.
При завершении работы приложения функции окна передается сообщение WM_DESTROY, в ответ на которое функция окна помещает в очередь приложения сообщение WM_QUIT, вызывая функцию PostQuitMessage. При выборке сообщения WM_QUIT завершается цикл обработки сообщений и работа приложения.
Все остальные сообщения передаются без изменения функции DefWindowProc для дальнейшей обработки.
Обратим ваше внимание на тот факт, что в ответ на сообщение WM_DESTROY приложение помещает в свою собственную очередь сообщение WM_QUIT. При этом получается, что одно сообщение, появившееся в очереди сообщений, порождает другое. Такая практика широко используется приложениями Windows. Через функцию окна вашего простейшего приложения проходят многочисленные сообщения, некоторые из которых порождают новые сообщения после того, как достигают функции DefWindowProc.
Весь этот поток сообщений незаметен для вашего приложения, однако оно может перехватить любое сообщение и обработать его самостоятельно. В этом заключается сила механизма обработки сообщения. Фактически приложение может подменить частично или полностью любой метод, используемый Windows для реализации той или иной операции с окном или приложением.
Файл определения модуля
Для того чтобы создать программу MS-DOS, вам достаточно было иметь ее исходный текст, компилятор и редактор связей. Компилятор создавал один или несколько объектных модулей, транслируя файлы исходных текстов программы, редактор собирал из этих модулей с использованием библиотек загрузочный модуль. Наше самое первое приложение Windows (листинг 1.1) также состояло из одного файла, однако при сборке загрузочного модуля вы получали предупреждающее сообщение о том, что в проекте недостает файла определения модуля.
Файл определения модуля (мы будем называть его def-файлом) используется редактором связей при создании exe-файла: в нем указывается имя загрузочного модуля приложения, тип exe-файла, атрибуты сегментов кода и данных, необходимый объем оперативной памяти для стека и кучи, а также имена экспортируемых функций, определенных в приложении.
Наше приложение использует def-файл, представленный в листинге 1.3.
Листинг 1.3. Файл window\window.def
Файл содержит отдельные операторы, такие, как NAME, DESCRIPTION или EXETYPE, причем практически все эти операторы имеют дополнительные параметры.
Оператор NAME определяет имя приложения, которое используется операционной системой Windows для идентификации приложения в своих сообщениях. К этому имени предъявляются такие же требования, как и к имени любого файла MS-DOS. Дополнительно после имени приложения может быть указано ключевое слово WINDOWAPI, означающее, что данное приложение предназначено для работы в среде Windows. Это ключевое слово используется по умолчанию и может быть опущено (что мы и сделали).
Оператор DESCRIPTION (необязательный) помещает в exe-файл дополнительную информацию, такую, как название приложения и сведения о разработчике. Эта информация никогда не загружается в оперативную память.
Оператор EXETYPE отмечает exe-файл как предназначенный для работы в среде Windows. Если для создания приложения вы используете среду разработки, способную создавать приложения OS/2, вам следует указать в def-файле нужный тип загрузочного модуля. При использовании транслятора Borland C++ версии 3.1 этот оператор может быть опущен.
Оператор STUB определяет имя файла с программой, подготовленной для работы в среде MS-DOS, которая получит управление при попытке запустить приложение Windows из командной строки MS-DOS. В составе Borland C++ поставляется программа с именем winstub.exe, которая выводит сообщение о том, что данный модуль предназначен для работы в среде Windows:
Однако в принципе вы можете подготовить два варианта своей программы — для MS-DOS и для Windows. В зависимости от того, в какой среде будет запущено ваше приложение, будет выбран соответствующий вариант.
Другой способ использования программы, описанной при помощи оператора STUB, заключается в том, что эта программа запускает Windows, передавая ей при запуске в качестве параметра имя файла, в котором она находится. При попытке запуска приложения из среды MS-DOS будет запущена операционная система Windows и затем нужное приложение. Но будьте осторожны — вы можете случайно запустить приложение из виртуальной машины MS-DOS, работающей под управлением Windows. Поэтому перед запуском Windows следует проверить среду, в которой был выполнен запуск приложения.
Оператор STACKSIZE определяет размер стека для приложения. Стандартное значение параметра для этого оператора составляет 5120 байт. Однако при использовании некоторых функций Windows версии 3.1 может потребоваться увеличение размера стека до 9 — 10 Кбайт. Такой большой размер стека (по сравнению с размером стека программ MS-DOS) связан с широко распространенной в Windows практикой неявного рекурсивного вызова функций.
Оператор HEAPSIZE определяет размер кучи приложения. Однако размер, указанный в качестве параметра, не является критичным. Можно указать любое число, большее нуля. При попытке заказать дополнительную память из кучи приложения операционная система Windows динамически увеличивает размер кучи до необходимой величины (если это возможно). Для кучи, стека и хранения ближних (описанных с ключевым словом near), глобальных, а также статических переменных может быть использовано максимально 64 Кбайт оперативной памяти.
Оператор CODE определяет атрибуты стандартного сегмента кода с именем _TEXT. Атрибут preloaded используется для обеспечения автоматической загрузки сегмента в оперативную память сразу после запуска приложения. Атрибут movable позволяет Windows перемещать сегмент в памяти, например для освобождения большого сплошного участка памяти. И наконец, атрибут discardable позволяет Windows при необходимости уничтожить сегмент и отдать занимаемую им память другим приложениям. При необходимости данный сегмент будет автоматически прочитан из exe-файла и восстановлен.
Оператор DATA определяет атрибуты стандартного сегмента данных, принадлежащего к группе с именем DGROUP. Наш сегмент данных описан как предварительно загружаемый и перемещаемый. Дополнительно указан атрибут multiple, означающий, что для каждой копии приложения необходимо создать отдельный сегмент данных.
Файл определения модуля может содержать и другие операторы, которые мы опишем позже по мере необходимости.
1.6. О сложности программирования для MS-DOS и Windows
Итак, вы познакомились с простейшим приложением Windows, обрабатывающим сообщения. У вас могло сложиться впечатление, что создание приложений Windows — достаточно сложная задача, гораздо более сложная, чем программирование для MS-DOS. Попытаемся убедить вас в обратном.
Вспомните, какие проблемы были у вас с резидентными программами, реализующими примитивное переключение задач в MS-DOS. Даже появление в составе MS-DOS версий 5.0 и 6.0 специального переключателя задач не позволяет достигнуть такого эффекта, который получается в Windows. Все, что должно сделать приложение Windows для обеспечения работы в мультизадачном режиме, — это создать цикл обработки сообщений и следить за тем, чтобы в процессе обработки отдельных сообщений процессор не захватывался на большой период времени. Такие задачи, как совместное использование параллельно работающими приложениями экрана видеомонитора, клавиатуры или мыши, значительно упрощаются благодаря поддержке со стороны Windows.
Операционная система Windows NT еще больше упрощает решение проблемы создания мультизадачных приложений. В ее среде каждое приложение Windows NT выполняется на отдельной виртуальной машине и операционная система сама следит за правильным распределением процессорного времени.
При оценке сложности проектирования диалоговой части приложения Windows следует принять во внимание наличие в программном интерфейсе Windows сотен функций, позволяющих решить практически любую задачу, встающую при проектировании диалогового интерфейса.
Любая программа MS-DOS вынуждена использовать для вывода изображения на экран собственные средства. Так как MS-DOS и BIOS не содержат практически никакой поддержки для создания диалоговых интерфейсов (окон, меню, диалоговых панелей и т. п.), программе MS-DOS приходится выполнять самой всю работу по созданию диалогового интерфейса.
Задача значительно усложняется при необходимости обеспечить графический интерфейс с пользователем. Так как программа MS-DOS работает непосредственно с аппаратурой компьютера (в частности, с видеоконтроллером), она должна учитывать наличие у потребителей большого количества самой разнообразной и несовместимой аппаратуры. Практически в комплекте с программой требуется поставлять драйверы для десятков типов видеоконтроллеров, принтеров, мышей и другой подобной аппаратуры. Причем все эти драйверы разработчик программы вынужден создавать сам, для чего ему необходимы исчерпывающие сведения об аппаратуре.
Операционная система Windows содержит драйверы для работы с любой стандартной аппаратурой компьютера. Причем поставщики оборудования, как правило, в комплекте с устройствами поставляют и драйверы для Windows. Приложение Windows никогда не работает напрямую с аппаратурой компьютера, поэтому всю сложнейшую работу по созданию драйверов вы как разработчик приложения Windows можете смело переложить на широкие плечи фирмы Microsoft и разработчиков аппаратного обеспечения.
Иными словами, ваша задача заключается в разработке приложения с использованием программного интерфейса Windows, а задача фирмы Microsoft и разработчиков оборудования — в обеспечении операционной системы Windows необходимым комплектом драйверов. И последнее они сделают значительно лучше вас, так как обладают всей необходимой информацией и отличными специалистами.
Задача пользователя также упрощается. Ему требуется при покупке компьютера обеспечить совместимость всех его устройств только с операционной системой Windows, но не со всеми программами, которые он использует сейчас или планирует приобрести в будущем. В этом случае на его компьютере будет работать любое приложение Windows. И не имеет ни малейшего значения ни конкретный тип или разрешение видеомонитора, ни тип принтера или другого оборудования.
Кроме всего перечисленного выше, Windows дисциплинирует разработчика приложений, предлагая стандарт на диалоговый интерфейс с пользователем. Если ваше приложение будет соответствовать этому стандарту, на его изучение и освоение со стороны пользователя будет затрачено минимальное время.
Для стандартизации пользовательского диалогового интерфейса разработчики приложений Windows должны пользоваться руководством по разработке интерфейса «The Windows Interface: An Application Design Guide», созданным фирмой Microsoft. Это руководство поставляется в составе средств разработки приложений Windows, таких, как Windows SDK или FoxPro for Windows. Это руководство содержит очень подробные рекомендации по разработке как интерфейса в целом, так и его отдельных компонентов.
По сравнению с MS-DOS операционная система Windows предлагает гораздо более удобный механизм управления памятью, предоставляя в распоряжение приложения гигантские области непосредственно адресуемой памяти, реальной или виртуальной. Это позволяет легко решать такие задачи, которые даются в MS-DOS с большим трудом и, как правило, требуют использования специальных расширителей MS-DOS (DOS-экстендеров).