Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 30% recurring commission
  • Выплаты в USDT
  • Вывод каждую неделю
  • Комиссия до 5 лет за каждого referral

Рабочая функция потока очень проста. В ней организован цикл while до сброса переменной bFin. В цикл включена функция Sleep(), которая усыпляет поток на указанный интервал времени (1000 мс). При переводе переменной bFin в состояние false цикл while разрывается и выполнением предложения return рабочая функция потока завершает свою работу. Завершение рабочей функции автоматически приводит к уничтожению самого потока и освобождению всех занимаемых им ресурсов.

В цикл while включено единственное содержательное предложение – вызов функции InvalidateRect(), которая инициирует посылку в главное окно приложения сообщения WM_PAINT и, соответственно, перерисовку окна.

В функции OnPaint() обработки сообщения WM_PAINT определяется текущее время, значения минут и секунд преобразуются в символьную форму, и сформированная строка текста выводится в окно. Этот алгоритм выполняется только при ненулевом значении дескриптора потока hThread1, что предохраняет от вывода в окно приложения текущего времени при запуске программы, когда в окно посылается первое сообщение WM_PAINT.

Синхронизация потоков

Общие характеристики объектов Windows

Организация программного комплекса, состоящего из нескольких процессов или потоков, почти неминуемо приводит к необходимости синхронизации выполняемых фрагментов программы. Синхронизация может иметь различные аспекты: сообщение потоку о завершении другого потока или его фрагмента, запрет потоку обращаться к некоторым данным, пока с ними работает другой поток, наложение ограничений на количество потоков, использующих одни и те же данные, и др.

НЕ нашли? Не то? Что вы ищете?

Синхронизация процессов и входящих в них потоков осуществляется с помощью различных объектов Windows, к которым относятся как сами процессы и потоки, так и программные средства, созданные специально для целей синхронизации: критические секции, мьютексы, семафоры, события и некоторые другие. Мы рассмотрим только синхронизацию потоков с помощью самих же потоков, а также событий.

Для объектов, которые могут служить целям синхронизации, в системе Windows предусматриваются два состояния – свободное, или сигнальное (signaled), и занятое, или несигнальное (nonsig­naled). Занятое состояние объекта используется для запрета тех или иных программных действий; свободное состояние, наоборот, снимает запрет и разрешает эти действия.

Имеются различные способы как изменения состояния синхронизирующего объекта, так и анализа его состояния. Для некоторых объектов имеются функции, позволяющие переводить объект в требуемое (свободное или занятое) состояние. Например, для объекта “событие” (event) функция SetEvent() устанавливает объект в свободное (сигнальное) состояние, а функция ResetEv­ent(), наоборот, сбрасывает его в занятое состояние. В соответствии с именами этих функций свободное состояние объекта называют еще установленным (set), а занятое – сброшенным (reset).

Анализ состояния объекта осуществляется с помощью двух основных функций синхронизации – WaitForSingleObject() и WaitForMultipleObjects(). Суть их одинаковая – они останавливают выполнение потока, “усыпляя” его, если анализируемый объект занят, и разрешают потоку дальнейшее выполнение (“будят” поток), как только объект освобождается. При этом первая из двух упомянутых выше функций позволяет анализировать состояние только одного объекта Windows, а вторая функция может анализировать состояние группы объектов.

Усыпление потока является высокоэффективной системной операцией, которая почти не требует процессорного времени. Поэтому в то время как один или несколько потоков спят в ожидании некоторого события, остальные действующие в системе потоки забирают себе практически все процессорное время. Как только ожидаемое событие произойдет, спящий поток мгновенно пробуждается и продолжает свое выполнение.

Функция WaitForSingleObject() имеет следующий прототип:

DWORD WaitForSingleObject(

HANDLE hObject,//Дескриптор синхронизирующего объекта

DWORD dwTimeout//Лимит времени ожидания в миллисекундах

);

Если нам нужно, чтобы функция ожидала событие неограниченное время, в качестве второго параметра следует задать константу INFINITE.

Мы видим, что функция ожидания освобождения объекта требует указания дескриптора этого объекта. Вообще для объектов Windows характерно обладание тремя описателями: дескриптором, именем и состоянием. Как мы уже видели, дескриптор объекта возвращается функциями, которые этот объект либо создают, либо открывают; обычно дескриптор используется для обращения к объекту внутри процесса (хотя, возможно, и из другого потока). Имя объекта, если оно необходимо, также задается объекту на этапе его создания; оно является общесистемной характеристикой и служит для обращения к объекту из другого процесса. Что касается состояния, то для объектов, которые будут рассматриваться в этом разделе, характерны следующие состояния:

·  Поток – состояние сбрасывается при создании потока и устанавливается при его завершении.

·  Событие – требуемое состояние события задается при его создании функцией CreateEvent(). Кроме того, событие может быть установлено в процессе выполнения потока явным образом функцией SetEvent() или сброшено функцией ResetEvent(). В некоторых случаях, когда события используются в асинхронных операциях, состояние события задается системой.

Синхронизация с помощью состояний потока

Представим себе электронную таблицу, в которой хранятся взаимосвязанные данные, т. е. исходные данные вместе с результатами их обработки. Ввод новых данных в таблицу или изменение старых потребует пересчета всей или части таблицы. Если таблица велика, а формулы, связывающие данные, сложны, этот пересчет может занять весьма значительное время, и в течение этого времени программа будет “висеть”, не позволяя пользователю вводить новые данные. Для повышения эффективности программы можно алгоритм пересчета данных выделить в отдельный поток, который будет выполняться параллельно с основным. Тогда при вводе данных, например, с клавиатуры, когда процессор практически свободен, процессорное время будет отдаваться потоку пересчета, не замедляя ввод новых данных. Если еще воспользоваться встроенной в Windows системой приоритетов и назначить потоку пересчета низкий приоритет, то эффективность программы еще более повысится, так как поток пересчета не будет претендовать на время процессора, пока выполняются какие-либо более приоритетные потоки. Однако возникновение любой паузы в выполнении приоритетных потоков приведет к активизации потока пересчета.

Однако в описываемой задаче может возникнуть ситуация, когда первичный поток, дойдя до некоторой точки, не может продолжить выполнение, пока не будут пересчитаны все данные. Если считать, что вторичный поток завершается, закончив пересчет данных, а при вводе новых данных запускается снова, то в рассматриваемом случае первичный поток должен остановиться в “критической точке” (когда ему понадобились обновленные данные) и ждать завершения вторичного. Взаимодействие таких потоков вместе с функциями, которые его организуют, показано на рис. 7.2.

Рис. 7.2. Синхронизация с помощью объекта “поток”

В некотором месте приложение (строго говоря, его первичный поток) создает вторичный поток с рабочей функцией Calc(), и начиная с этого момента в системе параллельно выполняются оба потока, и первичный, и вторичный. В той точке, где приложению требуются результаты выполнения потока Calc(), вызывается функция ожидания одного объекта WaitForSingleObject(), которая останавливает дальнейшее выполнение первичного потока в ожидании установки состояния вторичного. Однако пока вторичный поток выполняется, его состояние сброшено, и первичный поток, “застряв” на функции WaitForSingleObject(), будет находиться в спящем состоянии. Завершение рабочей функции Calc() приводит к завершению вторичного потока, установке его состояния и снятию блокировки первичного потока, который может теперь воспользоваться результатами вычислений, выполненных во вторичном потоке.

Синхронизация с помощью событий

Объект “событие” относится к числу весьма эффективных средств синхронизации потоков и процессов. С помощью этого объекта поток может уведомить другой поток об окончании любой операции или вообще о возникновении любой оговоренной заранее ситуации, могущей повлиять на выполнение другого потока. Другими словами, события не привязаны к определенным действиям, например, завершению процесса, окончанию операции ввода-вывода и др. Программист имеет возможность устанавливать и сбрасывать события в любой точке программы, организуя тем самым взаимосвязь потоков нужным ему образом. Заметим, что, поскольку при создании события ему можно дать произвольное имя и затем открыть это событие в другом процессе, события можно использовать для синхронизации не только потоков одного процесса, но и самостоятельных процессов, точнее, потоков, работающих в контексте самостоятельных процессов.

Перед тем, как использовать событие в целях синхронизации, его надо создать. Событие создается с помощью функции Cre­ateEvent() со следующим прототипом:

HANDLE CreateEvent(

LPSECURITY_ATTRIBUTES

lpEventAttributes,//Адрес атрибутов защиты

BOOL bManualReset,//Флаг ручного сброса события

BOOL bInitialState,//Флаг начального состояния события

LPCTSTR lpName//Имя события

);

Существует два типа событий: со сбросом вручную и с автоматическим сбросом (автосбросом). События со сбросом вручную требуют явной установки их в свободное состояние функцией SetEvent() и столь же явного переключения в занятое состояние функцией ResetEvent(); очевидно, что эти переключения можно выполнять в произвольных точках программы. События с автосбросом точно так же устанавливаются явным образом функцией SetEvent(), однако их сброс происходит автоматически, как только в управляемом потоке функция ожидания события обнаружила, что событие установлено и, соответственно, разбудила поток. Событиями с автосбросом удобно пользоваться в циклических фрагментах программ, так как такое событие, автоматически сброшенное функцией WaitForSingleObject() в начале разблокированного ею же фрагмента, предотвращает повторное его выполнение до новой установки этого события в управляющем потоке.

Если в функции CreateEvent() параметр bManualReset равен TRUE, создается событие со сбросом вручную; если этот параметр равен FALSE, создается событие с автосбросом.

Параметр bInitialState задает начальное состояние создаваемого события. Если он равен TRUE, создается установленное событие; если этот параметр равен FALSE, созданное событие будет сброшено.

Функция CreateEvent(), как и другие функции создания объектов Windows, возвращает дескриптор созданного события типа HANDLE, который затем используется в функциях переустановки и ожидания события.

Для установки события в свободное (сигнальное) состояние используется функция SetEvent(), в качестве параметра которой указывается дескриптор устанавливаемого события. Если событие удалось установить, функция возвращает значение TRUE.

Для сброса события в занятое (несигнальное) состояние используется функция ResetEvent(), в качестве параметра которой указывается дескриптор сбрасываемого события. Если событие удалось сбросить, функция возвращает значение TRUE.

На рис. 7.3 показан пример взаимодействия потоков, синхронизация которых осуществляется с помощью двух событий.

Первичный поток создает два события в сброшенном состоянии, сохраняя их дескрипторы hEvent1 и hEvent2, после чего запускает вторичный поток, из которого должен получить последовательно два результата. Созданные события пока никак не влияют на ход приложения. Оба потока выполняются параллельно до тех пор, пока первичному потоку не потребуется первый результат из вторичного. В этой точке первичный поток останавливается вызовом функции WaitForSingleObject(), в качестве первого параметра которой указывается дескриптор первого (пока сброшенного) события hEvent1. Как только вторичный поток получил первый результат, он вызовом функции SetEvent(hEvent1) устанавливает первое событие, что приводит к “побудке” первичного потока. Такая же процедура (только с использованием второго события) повторяется, когда первичный поток доходит до точки, в которой ему нужен второй результат из вторичного потока.

Рис. 7.3. Синхронизация потоков с помощью событий

Критические секции и защита данных

Критической секцией называется произвольный фрагмент программы, который должен обладать монопольным доступом к некоторым данным любого содержания и объема. Критические секции можно использовать только для синхронизации потоков, входящих в единый процесс; если требуется обеспечить поочередный доступ к общим данным потоков, входящих в разные процессы, то в этом случае надо вместо критических секций использовать другие объекты Windows – мьютексы.

Для использования в программе критических секций следует прежде всего объявить среди глобальных данных структурную переменную типа CRITICAL_SECTION:

CRITICAL_SECTION CritSect;

Переменная CritSect внешне никак не связана ни с защищаемыми данными, ни с тем фрагментом программы, который к этим данным обращается; она, можно сказать, носит организационный характер: с ее помощью Windows определяет, можно ли в данный момент предоставить доступ к общим данным тому или иному потоку.

В первичном потоке программы, еще до создания дочерних потоков, следует выполнить инициализацию объекта Windows “критическая секция”. Эта инициализация выполняется вызовом функции InitializeCriticalSection() с указанием в качестве ее параметра адреса созданной ранее глобальной структурной переменной типа CRITICAL_SECTION.

Фрагменты потоков, в которых осуществляется обращение к общим данным и которые, собственно, и называются критическими секциями, защищаются функциональными скобками

EnterCriticalSection(&CritSect)

...

LeaveCriticalSection(&CritSect)

с указанием в качестве параметра адреса той же структурной переменной типа CRITICAL_SECTION.

Какие изменения в ход выполнения потоков вносит такая организация? Обычно каждому потоку система по очереди выделяет кванты времени, в течение которых потоки и выполняются. "Отнять" время процессора у потока система может в любой момент, на любой его команде. При наличии в потоке критической секции алгоритм квантования времени усложняется. Если первый поток вошел в свою критическую секцию, система по-прежнему может в какой-то момент времени приостановить его выполнение и передать время процессора другому потоку, в том числе и второму потоку, связанному с той же переменной типа CRITICAL_SECTION. Однако как только второй поток подойдет к своей критической секции и вызовет функцию EnterCriticalSection(), система прервет его выполнение и отдаст время процессора либо снова первому потоку, либо другим потокам, действующим в системе. Пока первый поток не выйдет из своей критической секции (вызовом функции LeaveCriticalSection()), второй поток войти в свою критическую секцию не может. В результате, пока первый поток не закончит работу с общими данными, они будут недоступны второму потоку.

Разумеется, в процессе может существовать любое количество потоков, работающих с общими данными. В любом случае обращаться к общим данным они смогут только поочередно.

В каких случаях надо защищать данные от другого потока? Если общие для нескольких потоков данные только читаются, то к таким данным может обращаться любое число потоков. Проблемы возникают в том случае, если потоки как-то видоизменяют данные – модифицируют отдельные элементы, уничтожают их или добавляют. Пусть общие данные олицетворяют собой счет в банке, доступный нескольким клиентам – мужу и жене, например, или нескольким агентам по закупке некоторой продукции. Клиенты снимают деньги в банке с помощью программы, в которой каждому клиенту отведен отдельный поток. Получив запрос клиента на снятие некоторой суммы, программа проверяет наличие такой суммы на счете, оформляет необходимые документы, выдает клиенту деньги и уменьшает величину счета на снятую сумму.

Представим теперь, что где-либо между действиями по проверке суммы на счете и ее уменьшением система передала управление потоку второго клиента, который тоже поставил запрос на снятие денег со счета. Поскольку на счете пока еще имеется прежняя сумма, второй поток ее проанализирует и, обнаружив, что она достаточно велика, уменьшит в соответствии с запросом второго клиента. После того как управление вернется в первый поток, он продолжит анализ старой суммы и тоже снимет деньги со счета, хотя их там, возможно, недостаточно или вообще нет.

Защита с помощью критической секции всего блока действий по анализу суммы счета, выдаче денег клиенту и коррекции счета приведет к тому, что запросы всех клиентов на снятие денег будут выполняться по очереди и не смогут прерывать друг друга, чем и будет обеспечен правильный алгоритм анализа остатка средств на счете.

С целью экономии системных ресурсов созданные объекты критических секций следует удалять после того, как отпала необходимость в их использовании. Удаление критической секции осуществляется вызовом предусмотренной для этого функции Windows с указанием адреса соответствующей структурной переменной:

DeleteCriticalSection(CritSect);

Критические секции являются одним из наиболее используемых механизмов защиты данных общего пользования в многопоточных системах. Еще раз укажем, что защищать следует лишь те данные, которые модифицируются потоками; если потоки только читают данные, не изменяя их, то обращение к ним многих потоков осуществляется безболезненно.

8. Библиотеки динамической
компоновки

Библиотеки динамической компоновки, или динамически подключаемые библиотеки, которые обычно сокращенно называют DLL-библиотеками или просто DLL (от Dynamic Link Library) можно смело назвать краеугольным камнем операционной системы Windows. Все функции обслуживания 32-разрядных приложений содержатся в таких библиотеках; прежде всего это файлы KERNEL32.EXE (управление памятью, потоками и процессами), USER32.DLL (поддержка интерфейса пользователя) и GDI32.EXE (графика и вывод текста).

DLL-библиотеки содержат программы общего пользования, т. е. фактически подпрограммы, используемые многими (или всеми) загружаемыми приложениями. При обычной, не динамической компоновке все подпрограммы, в которых нуждается данное приложение, подсоединяются к основной программе и входят в состав ее выполнимого файла. Если некоторая подпрограмма нужна многим приложениям, то ее коды размножаются, повторяясь во всех скомпонованных приложениях. Такая система приводит к увеличению объемов загрузочных файлов и крайне неэффективному использованию памяти компьютера.

Суть использования DLL-библиотек заключается в следующем. Все функции DLL-библиотек находятся в памяти в одном экземпляре. При компоновке приложения в его загрузочный файл включаются не коды требуемых функций, а лишь их имена вместе с именами содержащих их DLL-библиотек. В процессе загрузки приложения в память система проецирует коды DLL на адресное пространство выполняемого процесса. В этом случае связь приложения с DLL-библиотекой сохраняется на все время выполнения приложения; такая процедура носит название неявного или статического подключения.

В целях оптимизации используемых ресурсов приложение может пойти дальше, организовав загрузку библиотеки (фактически проецирование DLL на свое адресное пространство) в нужный момент по ходу своего выполнения. Когда необходимость в функциях DLL отпадет, приложение может выгрузить библиотеку, освободив свое адресное пространство. Такая процедура носит название динамического подключения (динамической загрузки).

Необходимость разработки прикладных DLL-библиотек возникает в тех случаях, когда в сложном программном комплексе, включающем в себя много отдельных программ (например, приема, обработки и сохранения данных, их графического отображения в разных видах и т. д.) какие-то его элементы имеют общий характер и должны использоваться многими составляющими комплекса. Оформление этих элементов в виде функций DLL-библиотек позволяет сократить объем программного обеспечения и повысить эффективность его функционирования.

Создание собственной DLL-библиотеки не составляет особого труда. Современные интегрированные среды разработки (Borland C++, Microsoft Visual Studio и др.) имеют в своем составе средства подготовки как обычных выполнимых программ, так и библиотек динамической компоновки. Фактически оформление некоторой прикладной функции в виде элемента DLL-библиотеки сводится к установке соответствующих флажков при создании проекта, в котором данная функция будет компилироваться.

Статическое подключение DLL-библиотеки к приложению осуществляется также на этапе создание проекта, в котором компилируется и отлаживается данное приложение. Динамическое же подключение требует включения в текст приложения функций загрузки и выгрузки DLL-библиотеки.

Техника создания и использования прикладных DLL-библиотек описана в соответствующих лабораторных работах данного пособия.

Часть 2
Лабораторный практикум

Работы лабораторного практикума

Работа 1. Создание логических шрифтов

a. Создайте приложение с главным окном большого размера, покрасьте его каким-либо бледным цветом и сформируйте часть титульного листа некоторого отчета, содержащую следующие три отдельные строки:

Тема: название работы

Студент: ваша фамилия

Группа: ваша учебная группа

Строки оформите разными шрифтами. Для этого в функциии OnCreate() обработки сообщения WM_CREATE создайте три логических шрифта, различающихся гарнитурами, размером и характеристиками:

·  для первой строки – шрифт Times New Roman Cyr высотой 50 пикселов;

·  для второй строки – шрифт Courier New Cyr высотой 40 пикселов, повышенной жирности;

·  для третьей строки – шрифт Arial Cyr высотой 40 пикселов, курсивный.

В функции OnPaint() обработки сообщения WM_PAINT последовательно выбирайте в контекст устройства дескрипторы соз­данных шрифтов и выводите заданные строки. Покрасьте первую строку в красный цвет, а две другие – в синий. Все строки разместите по горизонтали по центру окна, для чего вместо функции вывода текста TextOut() воспользуйтесь функцией DrawText() с указанием соответствующих флагов и координат.

b. Создайте четвертый шрифт с именем Wingdings, Wingdings 2 или Webdings размером 60 – 70 пикселов. С помощью таблицы символов Windows и таблицы кодировки символов (см., например, [2], стр. 15) определите коды нескольких символов этих шрифтов и выведите их в главное окно, сформировав в программе строку этих символов в виде последовательности кодов. Не забудьте завершить строку нулем.

Работа 2. Таймеры Windows (индивидуальное задание А)

Включите в оконную функцию обработку сообщения WMTIMER. В функции OnCreate() обработки сообщения WM_CRE­ATE установите таймер с частотой, указанной в задании. В цветное главное окно приложения выведите фигуру, указанную в индивидуальном задании, и в каждом такте таймера изменяйте ее указанным в задании образом. Одновременно отсчитывайте в глобальной переменной число прошедших тактов и по истечении заданного числа тактов уничтожьте таймер, остановив изменение изображения.

Работа 3. Дочернее окно в главном окне приложения

Модифицируйте программу из предыдущей работы. После регистрации класса главного окна зарегистрируйте класс дочернего окна, назначив ему свою оконную процедуру и другой цвет фона. Создайте это окно в функции OnCreate(), назначив ему стиль WS_CHILD|WS_DLGFRAME|WS_VISIBLE. В оконную процедуру дочернего окна включите обработку единственного сообщения WM_PAINT, в которой выводите в это окно надпись "Осталось ... тактов" с указанием текущего числа оставшихся тактов. Для этого в каждом такте таймера преобразуйте оставшееся число тактов (хранящееся в глобальной переменной) в строку символов с помощью функции itoa(), а затем с помощью функций strcpy() и strcat() образуйте итоговую строку, выводимую в дочернее окно. Не забудьте, что в каждом такте таймера теперь надо инициировать посылку сообщения WM_PAINT в оба окна – в главное для изменения конфигурации фигуры и в дочернее для обновления выводимого сообщения.

Работа 4. Вывод растровых изображений с использованием
совместимой памяти

a. Создайте приложение с главным окном, в которое выводится нарисованный предварительно в графическом редакторе Paint цветной рисунок произвольного содержания размером, например, 200 ´ 200 пикселов. Содержательный алгоритм программы заключен в функциях обработки сообщений WM_CREATE и WM_PAINT.

Состав функции OnCreate():

·  получение контекста окна (функция GetDC());

·  создание совместимой памяти с одновременной загрузкой в него изображения (функция LoadImage());

·  создание совместимого с окном контекста совместимой памяти (функция CreateCompatibleDC());

·  выбор совместимой памяти с изображением в совместимый контекст (макрос SelectBitmap());

·  получение характеристик объекта совместимой памяти (функция GetObject());

·  освобождение контекста окна (функция ReleaseDC()).

Состав функции OnPaint():

·  получение контекста окна (функция BeginPaint());

·  копирование совместимой памяти в окно (функция BitBlt();

·  освобождение контекста окна (функция EndPaint()).

b. Модифицируйте предыдущую программу, выведя в совместимую память, помимо загруженного в нее рисунка, дополнительные графические образы: заключите рисунок в рамку, нарисованную толстым цветным пером, и поместите в поле рисунка свою фамилию в качестве подписи. Поскольку совместимая память вместе с совместимым контекстом создается в функции OnCreate(), все эти манипуляции можно выполнить там же. Для того чтобы рамка рисунка выглядела аккуратно, при создании пера используйте константу PS_INSIDEFRAME. Для определения размеров изображения воспользуйтесь функцией GetObject и структурной переменной типа BITMAP.

Выполните пункт b задания в двух вариантах: с загрузкой изображения посредством файла ресурсов и непосредственно из файла. BMP.

Работа 5. Измерение временных характеристик программы с помощью мультимедийного таймера

Включите в предыдущую программу измерение времени выполнения одного из ее фрагментов, например, функции OnCre­ate(). С этой целью вызовите функцию timeGetTime() дважды, в самом начале изучаемого фрагмента и перед его завершением, сохранив в глобальной переменной разность значений, возвращаемых этой функцией. Выведите эту разность в виде текста в главное окно приложения, дополнив его обозначением единиц времени (миллисекунд). Выполните многократный (5 – 10 раз) прогон программы, сравните и проанализируйте полученные результаты.

Работа 6. Вывод движущихся изображений с синхронизацией от системного таймера (индивидуальное задание B)

Разработайте программу, в которой в главном окне черного цвета размером 256´256 пикселов по траектории, указанной в задании, непрерывно перемещается залитая ярким цветом фигура (например, круг диаметром 50 – 60 пикселов).

В функции OnCreate() создайте таймер с минимально возможным периодом (50 мс), а также яркую цветную кисть для заливки движущейся фигуры.

В функции OnTimer() вычисляйте текущие координаты фигуры, изменяя их в соответствии с заданием; после этого вызовом функции InvalidateRect() инициируйте посылку в главное окно сообщения WM_PAINT.

В функции OnPaint() выводите в окно фигуру, используя текущие значения координат.

Убедитесь в невысокой скорости перемещения изображения и в наличии у него характерного мерцания. Сохраните отлаженную программу, чтобы иметь возможность сравнить ее со следующей.

Работа 7. Повышение качества движущихся изображений с помощью совместимой памяти

Выполните модификацию предыдущей программы так, чтобы изменение изображения выполнялось не непосредственно в главном окне, а в совместимой памяти с последующим копированием этой памяти в главное окно. Содержательный алгоритм программы заключен в функциях обработки сообщений WM_CREATE, WM_TIMER и WM_PAINT.

Состав функции OnCreate():

·  получение контекста окна (функция GetDC());

·  создание пустой совместимой памяти объемом 256´256 пикселов (функция CreateCompatibleBitmap());

·  создание совместимого с окном контекста совместимой памяти (функция CreateCompatibleDC());

·  выбор совместимой памяти в совместимый контекст (макрос SelectBitmap());

·  выбор в контекст совместимой памяти прозрачного пера и яркой кисти для рисования изображения в этой памяти;

·  образование структурной переменной типа RECT, описывающей размер совместимой памяти, и ее инициализация;

·  установка таймера с периодом 50 мс;

·  освобождение контекста окна (функция ReleaseDC()).

Состав функции OnTimer():

·  вычисление текущих координат фигуры в соответствии с заданием;

·  закрашивание совместимой памяти черным цветом (функция FillRect());

·  рисование в совместимой памяти фигуры с использованием текущих значений координат;

·  вызов функции InvalidateRect(), инициирующей посылку в главное окно сообщения WM_PAINT без перерисовки фона (последний параметр функции InvalidateRect() должен быть равен FALSE).

Состав функции OnPaint():

·  получение контекста окна (функция BeginPaint());

·  копирование совместимой памяти в окно (функция BitBlt();

·  освобождение контекста окна (функция EndPaint()).

Убедитесь в повышении качества изображения (исчезло его мерцание), хотя скорость перемещения изображения осталась, естественно, низкой.

Работа 8. Движение изображения по фоновому рисунку

Найдите в каталогах Windows какой-либо рисунок размером 256´256 пикселов (например, Лес. bmp или Чешуя. bmp) и скопируйте его в свой рабочий каталог. Увеличьте размер главного окна на 8 пикселов по горизонтали и на 26 пикселов по вертикали для компенсации толщины рамок и строки заголовка (чтобы размер собственно рабочей области окна точно соответствовал размеру фонового рисунка).

В функции OnCreate() вместо создания одной пустой области совместимой памяти размером 256´256 пикселов, как это делалось в работе 7, образуйте две таких области, одновременно загрузив и в ту, и в другую рисунок из файла. bmp. Одна из этих областей будет служить источником фонового рисунка, а другая использоваться как рабочая; в ней будет формироваться движущееся изображение. Для каждой из областей совместимой памяти предусмотрите свой совместимый контекст и выберите в эти контексты дескрипторы соответствующих областей. Далее, как и в предыдущей работе, выберите в контекст рабочей совместимой памяти дескрипторы прозрачного пера и цветной яркой кисти, освободите контекст окна и установите таймер.

В функции OnTimer() вместо закрашивания рабочей совместимой памяти черным цветом, как это делалось в предыдущей работе, скопируйте в нее функцией BitBlt() фоновый рисунок из фоновой совместимой памяти, после чего нарисуйте новое (сдвинутое) изображение и инициируйте посылку в главное окно сообщения WM_PAINT.

Функция OnPaint() не претерпевает никаких изменений; в ней осуществляется копирование рабочей совместимой памяти (фон плюс наша фигура) в окно приложения.

Работа 9. Работа с файлами (индивидуальное задание C)

Составьте программу, которая с помощью 32-разрядных функций работы с файлами CreateFile(), WriteFile(), SetFilePointer() и ReadFile() выполняет базовые операции с файлами, именно: создание нового файла, запись в него данных и их контрольное чтение. Зарезервируйте в памяти массив и заполните его в цикле данными, указанными в индивидуальном задании. Создайте файл с произвольным именем и расширением. Выведите созданный массив в файл. С помощью функции SetFilePointer() установите указатель на начало файла и прочитайте созданный файл в другой, пустой, массив такого же размера. Выведите для контроля в окно сообщения два-три элемента этого массива. Программа состоит из одной функции WinMain() без каких-либо окон или элементов управления (кроме окна вывода сообщения).

С помощью программы NortonCommander изучите физическое содержимое файла на диске и удостоверьтесь, что файл имеет правильную длину и содержит данные в соответствии с заданием.

Работа 10. Стандартные диалоги Windows
для работы с файлами

Составьте программу, которая вызовом стандартного диалога Windows “Открытие файла” позволяет найти на диске нужный файл, после чего открывает этот файл и читает из него данные в предусмотренный в программе пустой массив. Выведите для контроля в окно сообщения несколько элементов этого массива. Прочитайте созданный в предыдущей работе файл и убедитесь в правильном функционировании программы.

Стандартный диалог Windows “Открытие файла” создается вызовом функции GetOpenFileName(), в качестве параметра которой указывается адрес созданной в программе структурной переменной типа OPENFILENAME. Эту структуру необходимо предварительно обнулить, а затем заполнить в ней по меньшей мере следующие элементы:

lStructSize

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6