Данный раздел называется “первое знакомство”. Здесь вы действительно познакомитесь с первым приложением для Windows. Но не думайте, что это знакомство только с простейшим приложением. Здесь вы познакомитесь с некоторыми основными идеями, заложенными в системы типа Windows, а также с их влиянием на работу (и написание) приложений. Простейшее приложение оказывается лишь поводом для серьезного знакомства с целым классом систем.
Не ищите здесь подробных обсуждений тех или иных вопросов — здесь будут встречаться только обзоры, а более полные сведения вы сможете найти в последующих разделах. Такая структура принята потому, что программирование в Windows требует использования функций и инструментов из самых разных подсистем, так что последовательное рассмотрение WindowsAPI практически невозможно.
Так, например, даже в простейшем приложении надо осуществлять вывод данных в окно. Для этого необходимо изучить графический интерфейс устройств (GDI) и контекст устройства. Но для этого надо уже быть знакомым с диспетчером памяти, с описанием ресурсов приложения, с использованием стандартных диалогов, с обработкой сообщений и многим другим. При рассмотрении любого из перечисленных пунктов пришлось бы ссылаться на другие разделы, в том числе на тот же графический интерфейс.
Сразу оговоримся — в Windows возможно запускать приложения (application) двух разных типов — приложения Windows и приложения MS–DOS. Методы разделения ресурсов, применяемые этими приложениями существенно различаются, как различаются и методы доступа к ресурсам. В этой ситуации мы будем, говоря о приложении вообще, подразумевать приложение Windows. Если разговор зайдет о приложениях MS–DOS, то это будет оговорено отдельно.
Рассматривая работу приложения в среде Windows надо отталкиваться от того факта, что Windows является многозадачной средой. В этом случае в системе может выполняться одновременно[1] несколько разных приложений. Каждое приложение для своей работы требует некоторых ресурсов системы — дискового пространства, оперативной памяти, времени процессора, устройств ввода и вывода информации и пр. Соответственно Windows должен выполнять функции арбитра, осуществляющего разделение ресурсов между приложениями и контролирующего корректность работы приложений с выделенными им ресурсами.
С этой точки зрения можно рассмотреть развитие операционных систем, начиная от простейших (типа MS–DOS) и заканчивая достаточно сложными (как Windows NT, Unix, OpenVMS), для того что бы лучше понять возможности и ограничения разных реализаций Windows.
Простейшие однопользовательские однозадачные операционные системы являются, одновременно, примитивными арбитрами. Операционная система предоставляет набор средств для выполнения тех или иных операций по работе с ресурсами и организует запуск приложений. При этом приложению передается управление и система ожидает, пока оно завершит работу. Во время работы это приложение может обращаться к системе для получения ресурсов, а может это делать и в обход системы. Корректность работы приложения никак не контролируется — система только лишь предоставляет примитивный, часто неоптимальный и необязательный метод доступа к ресурсу. Приложение пользуется всеми ресурсами практически без ограничений (за исключением тех, которые заняты самой системой) и имеет непосредственный доступ к аппаратуре компьютера.
Такой подход типичен для операционных систем небольших компьютеров: сравнительно слабая поддержка периферийных устройств, простая файловая система и уникальная открытость, почти вседозволенность для приложений — так как конфликтовать им не с кем. Яркий пример — MS DOS первых версий.
Практически такие операционные среды мало применимы и, если система оказалась удачной, то она начинает развиваться в сторону поддержки многозадачного режима. Более того — обработка прерываний в однозадачной системе — неизбежный “многозадачный минимум”. Резидентная программа в однозадачной среде — еще одна попытка приближения к многозадачности. Те, кто пытался создать сколько–нибудь сложную резидентную программу обязательно сталкивался с трудностями разделения ресурсов, так как эффективных механизмов для этого однозадачные ОС не содержат. А благодаря своей открытости такие системы оказались благодатной почвой для развития вирусов.
Наиболее мощными представляются многозадачные, многопользовательские ОС, особенно в мультипроцессорных системах. Работа такой системы основана на параллельном исполнении нескольких приложений. Приложение может выполняется либо на отдельном процессоре, либо в режиме разделения времени — в определенные временные интервалы.
Приложение не знает, и практически не наблюдает присутствия и выполнения других приложений. Все проблемы бесконфликтного разделения ресурсов между разными задачами лежат на системе. Приложение не знает, в какой момент времени ее выполнение будет прервано, не знает когда будет возобновлено и вообще не регистрирует факта прерывания. Для одной задачи процесс выполнения представляется непрерывным.
Многопользовательские многозадачные системы обычно встречаются на более производительных компьютерах, при этом они содержат мощные средства взаимодействия с периферией, практически исключающие необходимость доступа приложений непосредственно к аппаратуре. Кроме того операционная система имеет очень гибкую и эффективную систему распределения ресурсов, привилегий и пределов доступа ко всем ресурсам компьютера, а также содержит средства защиты от несанкционированного доступа к ним. В качестве примеров можно привести OpenVMS, Unix, в несколько меньшей мере Windows NT (реализована для персонального компьютера).
Однако такие удобные и мощные ОС часто не имеет смысл реализовывать на персональном компьютере для которого (по идее его использования) не нужна многопользовательская система, и поэтому его открытость далеко не всегда является уязвимостью, но часто даже повышает эффективность его использования. Понятно также, что с ростом мощности персонального компьютера, появляется необходимость создания простых многозадачных ОС, ориентированных на работу одного пользователя.
Такая система занимает промежуточное положение между однозадачной и многопользовательской многозадачной системами. Очевидно, что ее ядро будет обладать большей открытостью, большей возможностью доступа к аппаратуре и относительно простыми методами разделения ресурсов, чем в сложных системах.
В качестве иллюстраций к этому можно привести Windows 3.x, разделение ресурсов в которой в значительной мере основано на методах MS–DOS, а также Windows–95, которая занимает промежуточное положение между Windows NT и Windows 3.x, предоставляя существенно упрощенные методы доступа к ресурсам, чем Windows NT и, в то же время, обеспечивая качественно лучшую защиту, чем Windows 3.x.
Для того, что бы Windows мог успешно разделять ресурсы, на разрабатываемые программы накладывают ряд требований[2]. Понятно, что первым таким требованием является требование не использования аппаратуры непосредственно. Вы можете использовать только средства Windows для выполнения всех своих задач, но не осуществлять доступ к аппаратуре непосредственно.
Например, приложение не должно обращаться к видеопамяти, средствам BIOS и т.д. Если приложение должно вывести на дисплей какое–либо изображение оно обязано воспользоваться существующими функциями Windows.
Конечно, ограничение на доступ к аппаратуре — не самое приятное ограничение, но оно имеет и свою оборотную, причем приятную, сторону: пользователь (и разработчик) может не думать о том, с чем конкретно он имеет дело — об этом заботиться Windows. То есть Windows сам обеспечивает выполнение всех требуемых операций на имеющемся оборудовании, причем используя практически все возможности этого оборудования.
Интересно обзорно рассмотреть методы разделения основных ресурсов компьютера между задачами, принятыми в Windows, и влияние этих методов на правила написания программ. Причем для первого знакомства отталкиваться мы будем от наиболее простой системы — Windows 3.x.
К основным разделяемым ресурсам несомненно относятся дисплей, клавиатура, мышь, оперативная память, процессор и диск. Стоит коротко отметить методы разделения каждого из этих ресурсов.
Для разделения дисплея между разными задачами в Windows используются окна (window). Каждой задаче назначено, по меньшей мере, одно окно, и осуществлять вывод приложение может (точнее должно) только в это окно.
Приложение может обладать несколькими окнами. В этом случае, обычно, одно окно является родительским (parent), а другие являются дочерними (child) окнами по отношению к родительскому окну. Как правило, приложение имеет только одно окно, не имеющее родителей — это так называемое главное окно приложения (main window). Все остальные окна приложения являются дочерними по отношению к этому окну.
Окна могут перемещаться по экрану, перекрывая полностью или частично другие окна. Окна могут находиться в максимизированном (“распахнутом” на весь экран, maximized, zoomed), нормальном или минимизированном (minimized, iconed) состоянии. В минимизированном состоянии окно заменяется на специальную небольшую картинку, называемую пиктограммой (иконой, icon), либо помещается в специальный список окон (taskbar или systray для Windows–95 и Windows NT 4.0).
Основная логика использования перекрывающихся окон реализована в Windows, хотя программистам остается достаточно много работы — приложение вынуждено перерисовывать окно каждый раз, когда возникает в этом необходимость (например, при увеличении размеров окна, если ранее скрытое окно становится видимым и в других случаях).
То, что не вся работа по перерисовке перекрывающихся окон выполняется системой, связано с использованием графического режима отображения окон. Для полной автоматизации необходимо было бы “виртуализовать” всю работу с окнами — то есть в обычной оперативной памяти должна находиться копия изображения окна. Тогда Windows мог бы полностью или частично восстанавливать изображение при появлении ранее невидимой части окна по этой копии. Однако общий размер нескольких копий (для каждого окна своя копия) может быть сопоставим с объемом всей оперативной памяти компьютера. Скажем для режима 1280x1024, 16 бит/пиксель (это далеко не самый хороший) битмап экрана занимает примерно 2.5MB. Кроме того, размер окна может быть больше экрана и таких окон может быть несколько. Таким образом Windows практически не может использовать виртуальные окна — ресурсов компьютера для этого явно не хватает (их еще надо разделять с выполняемыми приложениями и с компонентами самой системы).