- •Лабораторный практикум «Основы разработки приложений Windows» Книга 2
- •Часть 1. Теоретические сведения4
- •Часть 2. Лабораторный практикум73
- •Часть 1 Теоретические сведения
- •1. Основы архитектурЫ защищенного режима Регистры процессора
- •Адресация памяти
- •2. Логические шрифты Создание логических шрифтов
- •Вывод на экран текстовых строк
- •3. Таймеры Windows Организация и обслуживание таймеров
- •Мультимедийные таймеры
- •Измерение интервалов времени
- •Организация периодического процесса
- •Задание однократного интервала времени
- •4. Дочерние окна Создание и использование дочерних окон
- •Окна предопределенных классов в главном окне
- •5. Вывод растровых изображений
- •Процедура вывода растрового изображения
- •Компоновка составных изображений
- •6. Обслуживание файлов в 32-разрядных приложениях Windows
- •Базовые операции с файлами Открытие и создание файла
- •Запись и чтение файла
- •Файлы, проецируемые в память
- •7. Процессы и потоки
- •Создание дочернего процесса
- •Создание дочернего потока
- •Синхронизация потоков Общие характеристики объектов Windows
- •Синхронизация с помощью состояний потока
- •Синхронизация с помощью событий
- •Критические секции и защита данных
- •8. Библиотеки динамической компоновки
- •Часть 2 Лабораторный практикум Работы лабораторного практикума Работа 1. Создание логических шрифтов
- •Работа 2. Таймеры Windows(индивидуальное задание а)
- •Работа 3. Дочернее окно в главном окне приложения
- •Работа 4. Вывод растровых изображений с использованием совместимой памяти
- •Работа 5. Измерение временных характеристик программы с помощью мультимедийного таймера
- •Работа 6. Вывод движущихся изображений с синхронизацией от системного таймера (индивидуальное задание b)
- •Работа 7. Повышение качества движущихся изображений с помощью совместимой памяти
- •Работа 8. Движение изображения по фоновому рисунку
- •Работа 9. Работа с файлами (индивидуальное задание c)
- •Работа 10. Стандартные диалоги Windows для работы с файлами
- •Работа 11. Проецирование файла в память
- •Работа 12. Потоки (индивидуальное задание d)
- •Работа 13. Синхронизация потоков с помощью событий
- •Работа 14. Защита данных с помощью критической секции
- •Работа 15. Библиотеки динамической компоновки
- •Работа 16. Передача параметров в функции dll-библиотек
- •Индивидуальные задания лабораторного практикума
- •Задание c2.Массив записываемых в файл данных должен представлять собой последовательный ряд из 2000 целых четных чисел.
- •Лабораторный практикум «Основы разработки приложений Windows» Книга 2
ФЕДЕРАЛЬНОЕ АГЕНСТВО ПО ОБРАЗОВАНИЮ
МОСКОВСКИЙ ИНЖЕНЕРНО-ФИЗИЧЕСКИЙ ИНСТИТУТ
(ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ)
К.Г. Финогенов
Лабораторный практикум «Основы разработки приложений Windows» Книга 2
Москва 2005
УДК 32.973.1
ББК 681.3
Ф59
Финогенов К.Г. Лабораторный практикум «Основы разработки приложений Windows». Книга2. Уч. пособие.
М.:МИФИ, 2005. 108 с.
Пособие предназначено для широкого круга читателей, приступающих к освоению программирования на языке С++ в операционной системе Windows. В первой части описано использование в прикладных программах таких средств системы Windows, как шрифты, таймеры, дочерние окна, растровые изображения и др. Уделено внимание специфическим возможностям 32-разрядных приложений – особенностям обслуживания файлов и организации параллельных вычислений.
Вторая часть пособия – описание лабораторного практикума по изучению основ разработки приложений Windows.
Предназначено для обучения студентов кафедры компьютерных медицинских систем факультета автоматики и электроники МИФИ по курсам “Информатика”, “Компьютерный практикум” и “Языки программирования и операционные системы”. Пособие может быть также полезно студентам, аспирантам и преподавателям, имеющим представление о языке С++ и желающим самостоятельно освоить принципы разработки прикладных программ, предназначенных для работы в системе Windows.
Рецензенты: С. М. Зайцев и В. В. Комаров
Рекомендовано редсоветом МИФИ в качестве учебного пособия
© Московский инженерно-физический институт (государственный университет), 2005
О г л а в л е н и е
Часть 1. Теоретические сведения4
1. Основы архитектуры защищенного режима4
Регистры процессора 4
Адресация памяти 9
2. Логические шрифты13
Создание логических шрифтов 13
Вывод на экран текстовых строк 17
3. Таймеры Windows19
Организация и обслуживание таймеров 19
Мультимедийные таймеры 22
Измерение интервалов времени22
Организация периодического процесса23
Задание однократного интервала времени25
4. Дочерние окна26
Создание и использование дочерних окон 26
Окна предопределенных классов в главном окне 33
5. Вывод растровых изображений35
Процедура вывода растрового изображения 35
Компоновка составных изображений 42
6. Обслуживание файлов в 32-разрядных приложениях Windows44
Базовые операции с файлами 45
Открытие и создание файла45
Запись и чтение файла47
Файлы, проецируемые в память 50
7. Процессы и потоки54
Создание дочернего процесса 54
Создание дочернего потока 55
Синхронизация потоков 61
Общие характеристики объектов Windows61
Синхронизация с помощью состояний потока63
Синхронизация с помощью событий65
Критические секции и защита данных68
8. Библиотеки динамической компоновки70
Часть 2. Лабораторный практикум73
Работы лабораторного практикума 73
Индивидуальные задания лабораторного практикума 88
Список литературы107
Часть 1 Теоретические сведения
1. Основы архитектурЫ защищенного режима Регистры процессора
Процесс выполнения любой программы заключается в том, что процессор считывает из памяти команду за командой, выполняет их, а результаты записывает снова в память или, возможно, оставляет в своих внутренних регистрах для использования в следующих командах. Таким образом, для программиста важнейшими элементами архитектуры процессора и компьютера в целом является оперативная память и средства обращения к ней, а также регистры процессора, в которых сохраняются промежуточные результаты выполняемых операций.
Работая на языке высокого уровня, например на С++, программист лишь в редких случаях непосредственно использует в своих программах обозначение регистров и практически никогда не обращается по абсолютным адресам памяти. Однако при отладке программ приходится иметь дело и с тем, и с другим, так как отладчик выводит на экран текст программы в виде команд процессора, почти в каждой из которых фигурирует обозначение того или иного регистра, а также адреса ячеек памяти.
Количество и назначение регистров процессора, как и механизм адресации памяти, зависит от типа процессора. Мы будем рассматривать 32-разрядные процессоры фирмы Intel(напримерPentium) и совместимые с ними процессоры других фирм.
В процессоре имеется около трех десятков программно адресуемых регистров, из которых важнейшими для программиста являются регистры общего назначения и сегментные. Кроме того, необходимо иметь представление о назначении указателя команд и регистра флагов.
Процессор содержит 8 регистров общего назначения (рис. 1.1), носящих мнемонические обозначения EAX, EBX, ECX и т. д. Все они имеют размер по четыре байта, т. е. могут хранить по одному целому числу типа intилиunsigned int.
Рис. 1.1. Регистры общего назначения
Для того чтобы программа могла работать с более короткими переменными (например, short– 2 байта илиchar– 1 байт), младшие половины регистров EAX, EBX, ECX и EDX имеют самостоятельные обозначения – AX, BX, CX и DX соответственно; при этом они, в свою очередь, еще разделены пополам с образованием регистров длиной всего 1 байт. Старшая половина (старший байт) регистра AX носит название AH (от high – старший), младший байт – AL (от low – младший). Аналогично построены имена остальных байтовых регистров.
Компилятор, преобразуя текст программы в машинные коды, активно использует регистры процессора для временного хранения операндов команд и результатов выполняемых операций. На рис. 1.2 показано, как может выглядеть простой фрагмент программы на языке С++ после компиляции. Видно, что компилятор выделил для хранения переменных xиyрегистры EAX и EDX, и в тот же регистр EDX поместил результат сложения. В дальнейшем по ходу программы этот результат может быть перенесен в память, а регистр EDX использован для выполнения других операций.
int x=0x12345678; mov EAX,12345678h
int y=0x11111111; mov EDX,11111111h
int z=x+y; add EDX,EAX
Рис. 1.2. Фрагмент программы и результат его компиляции
Особое значение имеют сегментные регистры процессора (рис. 1.3). Свое название они получили потому, что любая программа располагается в связных участках памяти, называемых сегментами.
Рис. 1.3. Сегментные регистры
Как правило, для программы выделяются три сегмента: команд, данных и стека (рис. 1.4). В сегменте команд хранятся программные строки, в сегменте данных располагаются глобальные данные, которые видны из любой точки программы, а сегмент стека служит для временного хранения локальных данных, место под которые выделяется лишь на время выполнения той функции, в которой они объявлены.
Рис. 1.4. Сегменты программы и роль сегментных регистров
В сегментных регистрах хранится информация, позволяющая процессору определить, в каком месте памяти располагается тот или иной программный сегмент. Поскольку в программу обычно входят три сегмента, для обращения к ним используются три сегментных регистра. Регистр CS всегда служит для адресации сегмента команд, в регистре DS обычно хранится адрес сегмента данных, а регистр SS используется исключительно для работы со стеком. Остальные сегментные регистры используются по мере надобности для обращения, например, к системным полям памяти.
По содержимому сегментного регистра процессор может определить, каков адрес начала сегмента в памяти (этот адрес называется базовым). Однако ему еще нужно добраться до конкретного данного, хранящегося в сегменте. Для этого служит смещение, которое представляет собой номер начального байта конкретного данного от начала сегмента. Таким образом, адрес любого байта памяти состоит из двух частей: содержимого сегментного регистра, по которому процессор определяет базовый адрес сегмента, и смещения, которое следует прибавить к базовому адресу, чтобы получить истинный адрес адресуемой ячейки памяти.
Смещение очередной команды, которую надлежит выполнять процессору, всегда находится в регистре-указателе команд EIP. Он не входит в состав регистров общего назначения (и, соответственно, не был показан на рис. 1.1), потому что не является программно адресуемым регистром, т. е. к нему нельзя напрямую обратиться из программы и изменить его значение. Содержимым регистра EIP управляет исключительно сам процессор: прочитав из памяти очередной байт команды, процессор увеличивает содержимое EIP на 1, чем и обеспечивается строго последовательное чтение байтов программы, находящейся в памяти. Поскольку при обращении к сегменту команд процессор использует сегментный регистр CS, содержимое пары регистров CSиEIP, которое обычно обозначается, как CS:EIP, в каждый момент времени характеризует то место, до которого дошла программа по ходу своего выполнения.
Текущее смещение в стеке всегда находится в регистре-указателе стека ESP. Поскольку адрес стека процессор определяет с помощью сегментного регистра SS, содержимое пары регистров SS:ESP характеризует то место стека, в котором находится последнее отправленное туда данное.
К сегменту данных процессор обращается обычно с помощью регистра DS; смещение же конкретной ячейки может находиться в этом случае в любом регистре общего назначения: EAX, EBX и других (за исключением, разумеется, ESP). Смещение может также входить непосредственно в код команды, т. е. храниться не в регистре, а в составе кода команды в памяти.
Еще один регистр процессора, о котором следует иметь представление – это регистр флагов. В отличие от остальных регистров, в каждом из которых хранится единое данное, регистр флагов представляет собой, в основном, набор однобитовых флагов. Некоторые из этих флагов устанавливаются и сбрасываются программистом по мере необходимости (например, флаг IFуправления аппаратными прерываниями процессора), хотя следует отметить, что в защищенном режиме обращение к этим флагам осуществляется, как правило, не непосредственно из прикладной программы, а из специальных программ, работающих на высшем уровне привилегий – так называемых драйверов.
Другими флагами управляет сам процессор, устанавливая или сбрасывая их по результатам выполнения каждой команды. Если, например, в результате выполнения арифметической операции образовалось отрицательное число, устанавливается флаг знака SF; если получился нулевой результат, устанавливается флаг нуля ZF и т. д. Анализ флагов позволяет процессору выполнять операции сравнения и условных переходов. Содержимое регистра флагов отображается в отладчике, и программист имеет возможность определить состояние флагов в любой точке программы.