|
Ознакомление с приложениями Windows
Ознакомление с приложениями Windows
Первое знакомство Данный раздел называется “первое знакомство”. Здесь вы действительно познакомитесь с первым приложением для Windows. Но не думайте, что это знакомство только с простейшим приложением. Здесь вы познакомитесь с некоторыми основными идеями, заложенными в системы типа Windows, а также с их влиянием на работу (и написание) приложений. Простейшее приложение оказывается лишь поводом для серьезного знакомства с целым классом систем.Не ищите здесь подробных обсуждений тех или иных вопросов -- здесь будут встречаться только обзоры, а более полные сведения вы сможете найти в последующих разделах. Такая структура принята потому, что программирование в Windows требует использования функций и инструментов из самых разных подсистем, так что последовательное рассмотрение Windows API практически невозможно.Так, например, даже в простейшем приложении надо осуществлять вывод данных в окно. Для этого необходимо изучить графический интерфейс устройств (GDI) и контекст устройства. Но для этого надо уже быть знакомым с диспетчером памяти, с описанием ресурсов приложения, с использованием стандартных диалогов, с обработкой сообщений и многим другим. При рассмотрении любого из перечисленных пунктов пришлось бы ссылаться на другие разделы, в том числе на тот же графический интерфейс.Такие разные операционные системыСразу оговоримся -- в Windows возможно запускать приложения (application) двух разных типов -- приложения Windows и приложения MS-DOS. Методы разделения ресурсов, применяемые этими приложениями существенно различаются, как различаются и методы доступа к ресурсам. В этой ситуации мы будем, говоря о приложении вообще, подразумевать приложение Windows. Если разговор зайдет о приложениях MS-DOS, то это будет оговорено отдельно.Рассматривая работу приложения в среде Windows надо отталкиваться от того факта, что Windows является многозадачной средой. В этом случае в системе может выполняться одновременно Так как процессор, обычно, только один, то в данный момент времени будет работать только одно приложение. Однако, так как переключение между приложениями осуществляется достаточно быстро, то возникает впечатление одновременной работы нескольких приложений. Эта оговорка не влияет на последующие рассуждения. несколько разных приложений. Каждое приложение для своей работы требует некоторых ресурсов системы -- дискового пространства, оперативной памяти, времени процессора, устройств ввода и вывода информации и пр. Соответственно 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Для того, что бы Windows мог успешно разделять ресурсы, на разрабатываемые программы накладывают ряд требований В принципе требования могут и нарушаться. Так приложения под Windows 3.x сравнительно легко могут получить доступ к аппаратуре, хотя делать это не рекомендуется. Приложениям Win32 уже значительно сложнее получить доступ -- система лучше защищена от таких попыток, особенно Windows NT.. Понятно, что первым таким требованием является требование не использования аппаратуры непосредственно. Вы можете использовать только средства 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 практически не может использовать виртуальные окна -- ресурсов компьютера для этого явно не хватает (их еще надо разделять с выполняемыми приложениями и с компонентами самой системы).Строго говоря, окно в Windows является тем самым объектом, для которого частично реализован объектно-ориентированный подход. Интересно, что в документации Windows термин “объект” никогда не применяется к окну, а то, что называется “объектами”, ни в коей мере не является объектами ООП.Клавиатура и мышьЭти устройства относятся к устройствам ввода данных. Разделение этих устройств между разными задачами легко можно разбить на два круга вопросов:определение, к какой задаче относятся данные, полученные от устройства ввода. передача полученных от устройства данных требуемой задаче. Для мыши определение, к какой задаче относятся данные, вполне очевидно: так как каждая задача имеет по крайней мере одно окно, то информация от мыши должна использоваться окном, на фоне которого находится курсор мыши. Для клавиатуры дело обстоит несколько сложнее: нам придется ввести понятие активное окно (active window). В данный момент времени обязательно существует только одно активное окно, это окно выделяется цветом заголовка, рамки или подписи (если окно минимизировано). Активное окно является пользователем клавиатуры в данный момент времени. Для того, что бы другая задача могла получать данные, необходимо сделать активным окно, принадлежащее этой задаче. Передача полученных от устройства данных требуемой задаче. Хотя интуитивно кажется, что здесь не должно быть особых сложностей, но именно здесь очень ярко проявляются особенности организации Windows. Мы к этому вернемся, рассматривая метод разделения процессора. ДискДля разделения дискового пространства используется файловая система. Здесь Windows 3.x просто пользуется уже имеющимся -- файловой системой DOS; Windows-95 использует слегка модернизированную файловую систему DOS (поддерживаются имена файлов длиной до 256 символов и возможно использование так называемого FAT32 вместо FAT16 или FAT12). И только Windows NT предоставляет собственную файловую систему -- NTFS, хотя может работать и с FAT. NTFS отличается от FAT существенно более сложной организацией, позволяющей создавать единые тома из нескольких дисков, организовывать зеркальные тома или тома с избыточностью для хранения важных данных, а также задавать права доступа к отдельным файлам конкретным пользователям. Естественно, более сложная система оказывается более чувствительной к сбоям (несмотря на специально принятые меры) и менее производительной (несмотря на специальную оптимизацию).Для доступа к файлам Windows предоставляет свои собственные функции. В случае Windows 3.x эти функции в основном соответствуют функциям DOS для доступа к файлам и разделения доступа. Для нормальной работы Windows надо устанавливать программу SHARE.EXE до запуска Windows 3.1, либо, в случае Windows 3.11, будет использован специальный компонент Windows -- VSHARE.386. Более того, по версию Windows 3.0 включительно, имел место любопытный нюанс: Windows имел собственную функцию для открытия файлов (OpenFile), но совершенно не предоставлял средств для чтения/записи -- они были просто не декларированы, хотя внутри самого Windows содержались. Программисту рекомендовалось либо применять функции Run-Time библиотеки принятого языка (что можно было делать лишь ограниченно), либо написать свои процедуры на ассемблере. Либо, что делалось гораздо чаще, использовать не декларированные функции Windows для работы с файлами. С тех пор Microsoft просто декларировал эти функции.Для приложений, работающих в Win32 про функции DOS надо просто забыть -- Win32 предоставляет более богатый набор операций над файлами, поддерживает работу с разными файловыми системами Строго говоря, обычный DOS тоже может работать с различными файловыми системами -- для CD_ROM дисков специально спроектирована своя собственная файловая система (CDFS). При этом надо установить драйвер CD-ROM, обеспечивающий физический доступ к диску, и программу MSCDEX, которая осуществляет работу с дисками в формате CDFS. а, кроме того, исключает возможность применения прерываний DOS.ПамятьРеализация методов разделения памяти в Windows API и Win32 API качественно различаются. Для этого придется рассмотреть историю развития диспетчера памяти, что будет сделано позже. Сейчас надо обратить внимание только на некоторые общие идеи разделения памяти.В обоих API память делится на отдельные блоки. Однако деление осуществляется совершенно разными методами.Windows APIКоротко можно отметить, что вся доступная для Windows память называется глобальной (иногда глобальный хип, глобальная куча, global heap). Эта глобальная память делится на отдельные блоки, которые могут быть перемещаемыми в памяти. В виде блоков глобальной памяти в Windows представляются даже программы -- в этом случае каждому программному сегменту соответствует один блок глобальной памяти.Сегмент данных программы, представленный в виде блока глобальной памяти, может содержать свою локальную кучу (локальный хип, local heap). Эта память также может делиться на блоки, называемыми локальными. Термин локальный применяется к памяти, если она принадлежит сегменту данных программы.Windows предоставляет программные средства для манипуляции с блоками обоих видов -- и глобальными, и локальными. Каждый блок может быть фиксированным, перемещаемым или удаляемым в/из памяти. Это обеспечивает возможность как выделения больших непрерывных блоков данных (за счет перемещения других блоков), так и возможность удаления части блоков при недостатке памяти.Win32 APIВ Windows-95 и в Windows NT используется так называемая виртуальная память. Для каждого запущенного приложения выделяется собственное адресное пространство, размером 4Г, которым приложение владеет монопольно. В этом пространстве не находится никаких данных или кода других приложений. Таким образом приложения Win32 изолированы друг от друга. Необходимо учесть, что “адресное пространство” не соответствует реально выделяемой памяти -- это тот диапазон адресов, в котором может размещаться память, реально выделенная приложению. Очевидно, что из возможных 4Г адресного пространства используются обычно только несколько мегабайт, занимаемые кодом и данными приложения и необходимыми компонентами системы.Адресное пространство приложения делится на отдельные фрагменты, содержащие код, данные, служебную информацию и пр., необходимые для этого приложения. Однако такое деление статично -- перемещение фрагментов в адресном пространстве не происходит. Оптимизация доступа к памяти осуществляется не с помощью перемещения блоков или их удаления, а с помощью механизма отображения виртуального адресного пространства на физически доступную память компьютера (упрощенно можно считать, что виртуальное адресное пространство приложения -- это специальный файл подкачки страниц; оперативная память при этом выполняет роль кэша, в котором находятся только активно используемые код и данные).Помимо этого в адресном пространстве приложения могут выделяться одна или несколько куч (хипов), разделяемых на отдельные блоки. Вот эти-то блоки могут перемещаться внутри своей кучи и даже удаляться из памяти. Сама куча в адресном пространстве приложения перемещаться не будет. Для каждого приложения выделяется по меньшей мере одна куча, называемая стандартной (default heap). Все функции Windows API, работающие с глобальной или локальной кучами перенесены в Win32 API так, что они работают именно с этой стандартной кучей. При этом нет никакой разницы между глобальной и локальной кучами.ПроцессорВыше, при рассмотрении разных типов операционных систем, было выделено два “чистых” типа систем: однопользовательские однозадачные и многопользовательские многозадачные. Windows во всех его версиях занимает некоторые промежуточные положения между двумя этими крайними типами. Так версии Windows 3.x приближаются к простейшему типу однопользовательских однозадачных систем (с очень ограниченной реализацией некоторых возможностей как многопользовательской работы, так и многозадачного режима), а наиболее сложная Windows NT является истинно многозадачной системой с развитыми средствами разделения доступа пользователей.Windows API и объектно-ориентированное программированиеМетоды разделения процессора, применяемые разными реализациями Windows, интересно рассмотреть в их развитии -- от простого к сложному. Так в идеальной однозадачной среде, приложение, раз начавшись, выполняется без перерывов до полного завершения. В истинно многозадачной среде приложение выполняется за много шагов, причем само приложение не знает, когда его прервут для обработки других приложений -- этим ведает только система Кроме некоторых особых случаев, связанных с обработкой критичных по времени процессов, скажем некоторых операций ввода/вывода, взаимодействия с периферией и пр. Однако обычно такие задачи решаются драйверами устройств, так что приложение об этом опять-же не ведает..Промежуточным решением является среда, получившая название псевдомногозадачной (невытесняющая многозадачность, non-preemptive multitasking). В такой среде, подобно однозадачной, система не прерывает выполнения приложения. Однако само приложение должно быть разделено на небольшие, быстро выполняемые фрагменты. После выполнения такого фрагмента система может перейти к выполнению другого приложения. При этом приложение само уведомляет систему, где ее можно прервать для выполнения других задач.В Windows 3.x это может быть реализовано двумя разными методами:обычно приложение разбивается на набор небольших, быстро выполняемых функций. В этом случае система просто вызывает нужные функции для выполнения требуемых задач. После завершения обработки одной функции система может вызвать другую функцию другого приложения, осуществляя таким образом выполнение нескольких приложений как-бы одновременно. можно воспользоваться специальной функцией, передающей управление системе, и возвращающей его назад приложению после обработки других приложений. Таких функций в Windows 3.x две -- Yield и DirectYield. Однако этот путь используется в очень специальных случаях, например при разработке отладчиков, из-за довольно жестких ограничений на применение этих функций. При написании нормальных приложений для Windows 3.x разбиение программы на отдельные функции производится не механически, скажем через 100 строк, а функционально -- каждая функция выполняет определенные действия. При этом система, вызывая соответствующую функцию, передает ей некоторые данные, которые указывают, что надо сделать. Это очень важный момент. До сих пор все программы состояли из алгоритма, управляющего данными. На практике это означало, что алгоритм, описывающий программу, предусматривал когда, где и в какой мере возможно получение данных и управляющих воздействий, и как и куда направлять вывод результатов. Например, при необходимости ввода данных с клавиатуры, программа включала в себя вызов к операционной системе (или BIOS, на худой конец), который и возвращал требуемые данные. Еще раз: обычная программа генерирует вызовы к операционной среде для получения и вывода данных: алгоритм управляет данными В рассмотренном нами случае получается совершенно иная ситуация: поступающие от системы данные управляют поведением программы. Часто такими данными являются управляющие воздействия пользователя (например, изменение размеров окна, вызов меню и др.). Эти воздействия, вообще говоря, не синхронны с работой вашей программы, то есть получается, что данные управляют алгоритмом -- один из основных принципов объектно-ориентированного программирования (ООП). Введем новые понятия: данные, передаваемые от системы к соответствующей функции называются сообщением (message). процесс обращения к требуемой функции называется посылкой (post) или передачей (send) сообщения. функция, обрабатывающая сообщения, называется процедурой обработки сообщений (message handler). Таким образом, когда вы создаете программу, работающую в псевдомногозадачной среде (здесь: Windows 3.x), вы должны написать требуемые процедуры обработки сообщений. Далее Windows будет передавать вашим процедурам сообщения для их обработки. С точки зрения ООП все объекты должны обладать 3мя свойствами: инкапсуляция -- объединение в единое целое алгоритмов и необходимых данных; наследование -- возможность порождения новых объектов, основываясь на существующих, наследуя их свойства; полиморфизм -- разность реакций на одинаковые воздействия; наследники одного объекта могут отличаться своими свойствами друг от друга и от предка. С точки зрения этих свойств объект, определенный процедурой обработки сообщений, удовлетворяет всем этим требованиям. Процедура обработки сообщений может пользоваться специфичными, сгруппированными в каких-либо структурах, данными (инкапсуляция). Мы можем создавать новый объект со своей процедурой обработки сообщений, которая может ссылаться на процедуру ранее описанного объекта (наследование), а также выполнять обработку дополнительных сообщений или иначе обрабатывать прежние сообщения (полиморфизм). Обычно говорят, что процедура обработки сообщений определяет свойства объекта, так как задает реакцию этого объекта на воздействия (сообщения). Именно с такой трактовкой объекта возникли первые языки ООП. В Windows объектом ООП является окно. Соответственно говорят, что сообщения направлены не процедуре, а окну. Процедура обработки сообщений определяет окно с конкретными свойствами, даже больше -- одна процедура может обслуживать несколько разных окон, но тогда эти окна будут иметь одинаковую реакцию на одинаковые воздействия. То есть процедура обработки сообщений определяет не одно окно, а целый класс (class) окон. Сообщения, которые Windows направляет окну, отражают то, что происходит с этим окном. Например, есть сообщения, информирующие об изменении размеров окна, или о перемещении мыши, или нажатии на клавишу и др. Передача сообщений является механизмом разделения многих ресурсов, не только процессора. Так, с помощью одних сообщений, реализовано разделение мыши или клавиатуры между задачами, другие сообщения, получаемые окном, помогают осуществить разделение дисплея и т.д. Таким образом псевдомногозадачный метод разделения процессора оказался основой для построения объектно-ориентированной среды и попутно перевернул всю привычную нам философию написания программ -- мы теперь создаем не управляющий алгоритм, а набор процедур, обеспечивающий реакцию нашего окна (то есть нашей программы) на внешние события. Обработка сообщений является очень распространенным способом организации ООП-библиотек или ООП-языков. Существенное отличие (причем не в лучшую сторону) Windows 3.x заключается в том, что обработка сообщений является методом разделения процессора в псевдомногозадачной среде. Так как система не прерывает выполнение приложения в процессе обработки сообщения, то его обработка не должна занимать много времени Это сильно затрудняет применение Windows 3.x для расчетных задач -- либо мы должны их выполнить быстро, либо разбить на быстро выполняемые части, и понемногу обрабатывать по мере получения сообщений. Понятно, что обычно приходится разбивать на части, а это существенно замедляет вычисления. Вообще говоря, при обработке сообщения лучше укладываться в интервал менее 1 секунды, что бы задержка в реакции Windows на управляющие воздействия не была очень большой; критичной является задержка порядка 1-2 минуты -- при этом Windows 3.x может просто дать сбой или зависнуть (что очень сильно зависит от наличия других работающих приложений). Win32 APIВ более сложном Win32 API применяется так называемая истинная многозадачность (вытесняющая, preemptive multitasking). В этом случае разделение процессора осуществляется по определенным временным интервалам (квантам времени). Обработка сообщений перестала быть методом разделения процессора, и в процессе обработки сообщения система может передавать управление другим приложениям. Сама же идея применения объектно-ориентированного подхода к окнам осталась неизменной.Однако надо отметить, что реализация истинной многозадачности оказалась неполной. В рамках Win32 API могут работать как настоящие Win32 приложения, так и их 16ти разрядные собратья, написанные для Windows API. При запуске таких 16ти разрядных приложений под Win32 для них запускается специальная виртуальная 16ти разрядная Windows-машина, причем в Windows-95 для всех 16ти разрядных приложений используется одна общая виртуальная машина. Это значит, что истинная многозадачность реализована только между Win32 приложениями, в то время как 16ти разрядные приложения между собой используют обработку сообщений для разделения отведенного им процессорного времени. В случае Windows NT для каждого 16ти разрядного приложения запускается собственная Windows-машина, что позволяет им разделять процессор общим способом с приложениями Win32.Истинная многозадачность в Win32 позволила реализовать так называемые многопотоковые приложения (multithread application). При этом выделяют два новых понятия -- процесс (proccess) и поток (thread). Процессы в Win32 API примерно эквивалентны приложениям в Windows API. Для каждого процесса выделяются определенные системные ресурсы -- адресное пространство, приоритеты и права доступа к разделяемым ресурсам и прочее, но не процессорное время. Процесс только лишь описывает запущенную задачу, как она есть, без непосредственных вычислений. Для разделения процессора используются не процессы, а потоки, которым и выделяется процессорное время. В рамках каждого процесса выделяется свой поток, называемый первичным (primary thread), создаваемый по умолчанию при создании процесса. При необходимости в пределах одного процесса может быть создано много потоков, конкурирующих между собой (и с потоками других процессов) за процессорное время, но не за адресное пространство.Как написать приложение для WindowsПока мы рассмотрели только основную идею использования сообщений для реализации объектно-ориентированной операционной среды. Сейчас надо перейти к особенностям организации приложения, работающего в такой среде.Каждое приложение открывает по меньшей мере одно окно (в принципе могут существовать приложения вообще без окон, но как небольшие специализированные процедуры, не требующие никакого управления). Свойства окна определяются процедурой обработки сообщений этого окна. Таким образом, что бы определить свойства нужного окна, надо написать процедуру обработки сообщений, посылаемых этому окну (оконную процедуру или оконную функцию -- window procedure, она же процедура обработки сообщений, message handler).Одна процедура может обслуживать сообщения, посылаемые разным окнам с одинаковыми свойствами. Говорят, что окна, имеющие одну и ту же оконную функцию, принадлежат к одному классу окон. Вы должны эту процедуру зарегистрировать -- это называется регистрацией класса окон.Далее необходимо предусмотреть средства для создания и отображения окна зарегистрированного класса. С таким окном пользователь будет работать -- передвигать его по экрану, изменять размеры, вводить текст и т.д. Вам необходимо обеспечить реакцию этого окна (то есть вашего приложения) на действия пользователя. Фактически вы должны запустить механизм, обеспечивающий доставку сообщений, адресованных вашему окну, до получателя -- оконной процедуры. Этот механизм должен работать, пока работает ваше приложение. Такой механизм называется циклом обработки сообщений (message loop).Таким образом вы должны выполнить несколько шагов для создания собственного приложения:написать оконную функцию; зарегистрировать эту функцию (класс) в Windows, присвоив классу уникальное имя; создать окно, принадлежащее данному классу; обеспечить работу приложения, организовав цикл обработки сообщений. Чуть подробнее рассмотрим, что происходит с приложением за время его “жизни” -- от запуска до завершения -- перед тем, как перейдем к рассмотрению конкретного примера. Когда вы запускаете приложения для Windows, система сначала находит исполняемый файл и загружает его в память. После этого приложение осуществляет инициализацию необходимых объектов, регистрирует необходимые ему оконные классы, создает нужные окна. можно считать, что, начиная с этого момента, приложение способно нормально взаимодействовать с пользователем и необходимым образом реагировать на его действия. В это время должен работать цикл обработки сообщений, который будет распределять поступающие сообщения конкретным окнам. Сообщения, которые будет получать окно, информируют приложение о всех действиях, которые предпринимает пользователь при работе с данным окном. Так, существуют сообщения, информирующие о создании окна, изменении его положения, размеров, вводе текста, перемещении курсора мыши через область окна, выборе пунктов меню, закрытии окна и т.д. Для удобства работы системы все сообщения имеют уникальные номера, по которым определяется назначение этого сообщения; а для удобства разработки приложений для всех сообщений определяются символические названия. Например: #define WM_MOVE 0x0003 #define WM_SIZE 0x0005 В большинстве случаев названия сообщений начинаются на WM_, однако названия некоторых сообщений имеют префиксы BM_, EM_, LBM_, CBM_ и другие. Для начала выделим четыре сообщения, с которыми мы будем знакомится первыми. Это сообщения применяются при создании окна (WM_CREATE), при закрытии Точнее при уничтожении. Термин “закрытие” в Windows часто имеет другой смысл, в том числе -- сворачивание окна в пиктограмму. (WM_DESTROY и WM_QUIT) и при его перерисовывании (WM_PAINT). В тот момент, когда приложение создает новое окно, оконная процедура получает специальное сообщение WM_CREATE, информирующее окно о его создании. При этом окно создается с помощью вызова специальной функции (CreateWindow, CreateWindowEx и некоторые другие), которая выполняет все необходимые действия; сообщение при этом имеет лишь “информационный” характер -- оно информирует окно о том, что его создают. Однако реальное создание происходит не в обработчике этого сообщения, а в той функции, которую вызвали для создания окна. На сообщении перерисовки окна WM_PAINT надо остановиться чуть подробнее. Дело в том, что какая-либо часть окна может быть скрыта от пользователя (например, перекрыта другим окном). Далее в процессе работы эта часть может стать видимой, например вследствие перемещения других окон. Сама система при этом не знает, что должно быть нарисовано в этой, ранее невидимой части окна. В этой ситуации приложение вынуждено позаботиться о перерисовке нужной части окна самостоятельно, для чего ему и посылается это сообщение каждый раз, как видимая область окна изменяется. Когда окно закрывается, оно получает сообщение WM_DESTROY, информирующее о закрытии окна. Как и в случае создания, сообщение о закрытии является информационным; реальное закрытие осуществляется специальной функцией (обычно DestroyWindow), которая, среди прочего, и известит окно о его уничтожении. Все время, пока пользователь работает с приложением, работает цикл обработки сообщений этого приложения, обеспечивающий доставку сообщений окнам. В конце работы приложения этот цикл, очевидно, должен завершиться. В принципе, можно сделать так, что бы в цикле проверялось наличие окон у приложения. При закрытии всех окон цикл тоже должен завершить свою работу. Однако можно несколько упростить задачу -- и в Windows именно так и сделано -- вместо проверки наличия окон можно предусмотреть специальный метод завершения цикла при получении последним окном (обычно это главное окно приложения) сообщения о его уничтожении (WM_DESTROY). Для этого применяется специальное сообщение WM_QUIT, которое посылается не какому-либо окну, а всему приложению в целом. При извлечении этого сообщения из очереди цикл обработки сообщений завершается. Для посылки такого сообщения предусмотрена специальная функция -- PostQuitMessage. После завершения цикла обработки сообщений приложение уничтожает оставшиеся ненужные объекты и возвращает управление операционной системе. Сейчас в качестве примера мы рассмотрим простейшее приложение для Windows, традиционную программу “Hello, world!”. После этого подробнее рассмотрим, как это приложение устроено. Здесь же можно заметить, что при создании практически любых, написанных на “C”, приложений для Windows этот текст может использоваться в качестве шаблона. Пример 1A -- первое приложениеФайл 1a.cpp#define STRICT #include <windows.h> #define UNUSED_ARG(arg) (arg)=(arg) static char szWndClass[] = "test window"; LRESULT WINAPI _export WinProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { UNUSED_ARG( wParam ); UNUSED_ARG( lParam ); PAINTSTRUCT ps; switch ( uMsg ) { case WM_CREATE: return 0L; case WM_PAINT: BeginPaint( hWnd, &ps ); TextOut( ps.hdc, 0, 0, "Hello, world!", 13 ); EndPaint( hWnd, &ps ); return 0L; case WM_DESTROY: PostQuitMessage( 0 ); return 0L; default: break; } return DefWindowProc( hWnd, uMsg, wParam, lParam ); } static BOOL init_instance( HINSTANCE hInstance ) { WNDCLASS wc; wc.style = 0L; wc.lpfnWndProc = WinProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon( NULL, IDI_APPLICATION ); wc.hCursor = LoadCursor( NULL, IDC_ARROW ); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = szWndClass; return RegisterClass( &wc ) == NULL ? FALSE : TRUE; } int PASCAL WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow ) { UNUSED_ARG( lpszCmdLine ); MSG msg; HWND hWnd; if ( !hPrevInst ) { if ( !init_instance( hInst ) ) return 1; } hWnd= CreateWindow( szWndClass, "window header", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInst, NULL ); if ( !hWnd ) return 1; ShowWindow( hWnd, nCmdShow ); UpdateWindow( hWnd ); while ( GetMessage( &msg, NULL, NULL, NULL ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } return msg.wParam; } Рисунок 1. Приложение 1a.cpp в среде Windows 3.x или Windows NT 3.x (слева) или в среде Windows-95 или Windows NT 4.0 (справа). В зависимости от платформы, на которой запускается это приложение, внешний вид окна может несколько изменяться. Это связано с изменившимся интерфейсом пользователя при переходе от Windows 3.x и Windows NT 3.x к Windows-95 и Windows NT 4.0. Далее мы рассмотрим исходный текст более подробно. При первом взгляде на него обращают на себя внимание сразу несколько необычных (по сравнению с программами для DOS) вещей: новые типы данных странные имена переменных обилие используемых функций и передаваемых им параметров Примерно в таком порядке мы и рассмотрим эти вопросы. Новые типы данныхИтак, еще раз рассмотрим первое Windows-приложение (1a.cpp).Обычно в начале “С”-программы помещается директива препроцессора #include для включения файла, содержащего основные определения и прототипы функций. При написании Windows-приложений вы должны включить файл WINDOWS.H. Этот файл содержит определения типов, констант и функций, используемых в Windows В Win32 API заголовочный файл windows.h просто включает в себя набор директив #include для включения необходимых заголовочных файлов и директив условной компиляции..
Страницы: 1, 2, 3, 4
|
|