- •Дополнительная информация из интернета
- •Открытие файлов
- •Стандартные диалоговые панели для открытия файлов
- •NMaxCustFilter - Определяет размер буфера в байтах, указанного в поле lpstrCustomFilter. Размер этого буфера должен быть не меньше 40 байт.
- •Закрытие файлов
- •Создание файлов
- •Чтение и запись
- •Позиционирование
- •Определение типа устройства ввода/вывода
- •Использование стандартной библиотеки транслятора
МЕТОДИЧЕСКИЕ УКАЗАНИЯ К ЛАБОРАТОРНЫМ РАБОТАМ
по дисциплине "Операционные системы"
Цикл лабораторных работ по дисциплине "Операционные системы"предназначен для получения студентами практических навыков работы с операционной системы (ОС) на уровнепрограммного интерфейса.
В лабораторных работах используется многозадачная ОС Windows. Для написания программ будет использован язык программирования высокого уровня C++.
Приведенные примеры программ написаны в среде визуального программирования Microsoft Visual C++ Express. Код программы распределен по двум файлам: с расширением .h, в который записываются прототипы функций и их описание, и с расширением .cpp, в который записывается основной текст программы.
Для создания проекта в среде MicrosoftVisualC++ выполняем следующее:
Файл
Создать проект
Visual C++
Пустой проект
Вводим имя проекта, решения и путь для сохранения данных
Нажимаем «OK»
Для добавления новых файлов в проект необходимо выполнить следующие действия:
Проект
Добавить новый элемент
Выбираем тип элемента (файл C++ (.cpp) или заголовочный файл (.h))
Вводим имя элемента
Нажимаем «Добавить»
Ex00.h
int Sum(int x, int y);
int Sum(int x, int y)
{
return x+y;
}
Ex00.cpp
#include "Ex00.h"
#include <iostream>
using namespace std;
int main(void)
{
cout<<Sum(2, 3); // Вызов функции из заголовочного файла
cin.get(); // Ожидание нажатия клавиши
}
Лабораторная работа №1
«Работа с многопоточными программами»
Цель работы: получение навыков программирования многопоточных приложений на языке программирования высокого уровняC++.
Общие сведения:
Процессом (process) называется экземпляр программы, загруженной в память. Этот экземпляр может создавать потоки (нити) (thread), которые представляют собой последовательность инструкций на выполнение. Важно понимать, что выполняются не процессы, а именно нити.
Причем любой процесс имеет хотя бы одну нить. Эта нить называется главной (основной) нитью приложения.
Так как практически всегда нитей гораздо больше, чем физических процессоров для их выполнения, то нити на самом деле выполняются не одновременно, а по очереди (распределение процессорного времени происходит именно между нитями). Но переключение между ними происходит так часто, что кажется, будто они выполняются параллельно.
В зависимости от ситуации нити могут находиться в трех состояниях. Во-первых, нить может выполняться, когда ей выделено процессорное время, т.е. она может находиться в состоянии активности. Во-вторых, она может быть неактивной и ожидать выделения процессора, т.е. быть в состоянии готовности. И есть еще третье, тоже очень важное состояние - состояние блокировки. Когда нить заблокирована, ей вообще не выделяется время. Обычно блокировка ставится на время ожидания какого-либо события. При возникновении этого события нить автоматически переводится из состояния блокировки в состояние готовности. Например, если одна нить выполняет вычисления, а другая должна ждать результатов, чтобы сохранить их на диск. Вторая могла бы использовать цикл типа "while( !isCalcFinished ) continue;", но легко убедиться на практике, что во время выполнения этого цикла процессор занят на 100 % (это называется активным ожиданием). Таких вот циклов следует по возможности избегать, в чем оказывает неоценимую помощь механизм блокировки. Вторая нить может заблокировать себя до тех пор, пока первая не установит событие, сигнализирующее о том, что чтение окончено.
В Windows реализована вытесняющая многозадачность - это значит, что в любой момент система может прервать выполнение одной нити и передать управление другой.
Все нити, принадлежащие одному процессу, разделяют некоторые общие ресурсы - такие, как адресное пространство оперативной памяти или открытые файлы. Эти ресурсы принадлежат всему процессу, а значит, и каждой его нити. Следовательно, каждая нить может работать с этими ресурсами без каких-либо ограничений. Если одна нить еще не закончила работать с каким-либо общим ресурсом, а система переключилась на другую нить, использующую этот же ресурс, то результат работы этих нитей может чрезвычайно сильно отличаться от задуманного. Такие конфликты могут возникнуть и между нитями, принадлежащими различным процессам. Всегда, когда две или более нитей используют какой-либо общий ресурс, возникает эта проблема.
Именно поэтому необходим механизм, позволяющий потокам согласовывать свою работу с общими ресурсами. Этот механизм получил название механизма синхрон
изации нитей (thread synchronization).
Чтобы создать тот или иной объект синхронизации, производится вызов специальной функции WinAPI типа Create... (напр. CreateMutex). Этот вызов возвращает дескриптор объекта (HANDLE), который может использоваться всеми нитями, принадлежащими данному процессу. Есть возможность получить доступ к объекту синхронизации из другого процесса - либо унаследовав дескриптор этого объекта, либо, что предпочтительнее, воспользовавшись вызовом функции открытия объекта (Open...). После этого вызова процесс получит дескриптор, который в дальнейшем можно использовать для работы с объектом. Объекту, если только он не предназначен для использования внутри одного процесса, обязательно присваивается имя. Имена всех объектов должны быть различны (даже если они разного типа). Нельзя, например, создать событие и семафор с одним и тем же именем.
По имеющемуся дескриптору объекта можно определить его текущее состояние. Это делается с помощью т.н. ожидающих функций. Чаще всего используется функция WaitForSingleObject. Эта функция принимает два параметра, первый из которых - дескриптор объекта, второй - время ожидания в мсек. Функция возвращает WAIT_OBJECT_0, если объект находится в сигнальном состоянии, WAIT_TIMEOUT - если истекло время ожидания, и WAIT_ABANDONED, если объект-взаимоисключение не был освобожден до того, как владеющая им нить завершилась. Если время ожидания указано равным нулю, функция возвращает результат немедленно, в противном случае она ждет в течение указанного промежутка времени. В случае, если состояние объекта станет сигнальным до истечения этого времени, функция вернет WAIT_OBJECT_0, в противном случае функция вернет WAIT_TIMEOUT. Если в качестве времени указана символическая константа INFINITE, то функция будет ждать неограниченно долго, пока состояние объекта не станет сигнальным.
Очень важен тот факт, что обращение к ожидающей функции блокирует текущую нить, т.е. пока нить находится в состоянии ожидания, ей не выделяется процессорного времени.
Мы рассмотрим один из типов синхронизации, называемый взаимоисключением.
Объекты-взаимоисключения (мьютексы, mutex - от MUTual EXclusion) позволяют координировать взаимное исключение доступа к разделяемому ресурсу. Сигнальное состояние объекта (т.е. состояние "установлен") соответствует моменту времени, когда объект не принадлежит ни одной нити и его можно "захватить". И наоборот, состояние "сброшен" (не сигнальное) соответствует моменту, когда какая-либо нить уже владеет этим объектом. Доступ к объекту разрешается, когда нить, владеющая объектом, освободит его.
Две (или более) нити могут создать мьютекс с одним и тем же именем, вызвав функцию CreateMutex. Первая нить действительно создает мьютекс, а следующие - получают дескриптор уже существующего объекта. Это дает возможность нескольким нитям получить дескриптор одного и того же мьютекса, освобождая программиста от необходимости заботиться о том, кто в действительности создает мьютекс. Если используется такой подход, желательно установить флаг bInitialOwner в FALSE, иначе возникнут определенные трудности при определении действительного создателя мьютекса.
Несколько нитей могут получить дескриптор одного и того же мьютекса, что делает возможным взаимодействие между процессами. Можно использовать следующие механизмы такого подхода:
Дочерний процесс, созданный при помощи функции CreateProcess может наследовать дескриптор мьютекса в случае, если при создании мьютекса функцией CreateMutex был указан параметр lpMutexAttributes.
Нить может получить дубликат существующего мьютекса с помощью функции DuplicateHandle.
Нить может указать имя существующего мьютекса при вызове функций OpenMutex или CreateMutex.
Для того чтобы объявить взаимоисключение принадлежащим текущей нити, надо вызвать одну из ожидающих функций. Нить, которой принадлежит объект, может его "захватывать" повторно сколько угодно раз (это не приведет к самоблокировке), но столько же раз она должна будет его освобождать с помощью функции ReleaseMutex.
Пример программы:
Ex01.h
DWORD WINAPI Calculate(LPVOID lpParam);
DWORD WINAPI Show(LPVOID lpParam);
#define e 1000000
HANDLE mutex;
int x = 2;
int y = 1;
int s = 0;
DWORD WINAPI Calculate(LPVOID lpParam)
{
while(s<e)
{
WaitForSingleObject(mutex,INFINITE);
s += y;
y *= x;
ReleaseMutex(mutex);
Sleep(400);
};
return 0;
};
DWORD WINAPI Show(LPVOID lpParam)
{
while(s<e)
{
WaitForSingleObject(mutex,INFINITE);
printf("CurrentValue = %d \n",s);
Sleep(100);
ReleaseMutex(mutex);
};
return 0;
};
Ex01.cpp
#include <Windows.h>
#include <stdio.h>
#include "Ex01.h"
#define THREADS_COUNT 2
HANDLE hThreads[THREADS_COUNT];
DWORD dwThreadsID[THREADS_COUNT];
int main(void)
{
mutex = CreateMutex(NULL,false,NULL);
hThreads[0] = CreateThread(NULL,0,Calculate,NULL,0,&dwThreadsID[0]);
hThreads[1] = CreateThread(NULL,0,Show,NULL,0,&dwThreadsID[1]);
printf("Process ready...\n");
printf("Start...\n");
WaitForMultipleObjects(THREADS_COUNT,hThreads,true,INFINITE);
for(int i = 0;i < THREADS_COUNT; i++)
{
CloseHandle(hThreads[i]);
};
printf("End...\n");
system("pause");
return 0;
};
Задания для самостоятельной работы:
№ вар. |
Задание |
1 |
Разработать
программу, вычисляющую сумму трех
рядов:
|
2 |
Разработать
программу, одновременно
строящую
графики функций:
|
3 |
Разработать программу, выполняющую одновременно три действия над одним и тем же массивом: сложение чисел, умножение чисел, нахождение наименьшего. |
4 |
Разработать программу, одновременно кодирующую два сообщения. Коды символов в сообщении выводятся на печать параллельно. Значения кодовой комбинации для каждого символа априори известно. |
5 |
Разработать программу, одновременно квантующую по времени два непрерывных сигнала: и . Квантованные значения параллельно выдаются на печать. |
6 |
Разработать программу, имитирующую работу одновременно двух конечных автоматов. На печать параллельно выводятся выходные символы от каждого автомата. |
Содержание отчета:
Титульный лист.
Краткое теоретическое описание.
Задание на лабораторную работу, включающее формулировку задачи.
Результаты выполнения работы.
Контрольные вопросы:
Что такое многопоточность ?
Для чего организуют многопоточность в программах?
Всегда ли возможна организация многопоточности (многонитевости)? Если нет, то при каких условиях ее можно организовать?
Дополнительная информация из интернета
Только Mutex позволяет проводить синхронизацию не только между потоками(thread), но и процессами(process), то есть между приложениями.
Данный объект синхронизации регистрирует доступ к ресурсам и может находиться в двух состояниях:
установлен
сброшен
Mutex - установлен в тот момент когда ресурс свободен. Если к объекту есть доступ, то говорят, что сброшен. Для работы с Mutex есть ряд функций. Сначала объект нужно создать CreateMutex(), для доступа OpenMutex(), а для освобождения ресурса ReleaseMutex(). Для доступа к объекту Mutex используется ожидающая функция WaitForSingleObject(). Тонкость здесь следующая. Каждая программа создает объект Mutex по имени. То есть Mutex это именованный объект. А если такой объект синхронизации уже создала другая программа, то по вызову CreateMutex() мы получим указатель на объект, который уже создала первая программа. То есть у обоих программ будет один и тот же объект. Вот это и позволяет производить синхронизацию.
Лабораторная работа №2
«Работа с интерфейсом WIMP»
Цель работы: получение навыков создания оконных приложений для ОС Windows.
Общие сведения:
Для создания окон в Windows приложениях используется функция CreateWindow(). Рассмотрим ее прототип:
function CreateWindow(ExStyle: Longint; ClassName, WindowName: PChar; Style: Longint; X, Y, Width, Height: Integer; WndParent: HWnd; Menu: HMenu; Instance: THandle; Param: Pointer): HWnd;
ExStyle: Одинизследующих pасшиpенныхстилейокна: ws_ex_DlgModalFrame, или ws_ex_NoParentNotify. См. pаздел "Расшиpенные стили окон, ws_ex_" в главе 1.
ClassName: Имя класса окна (заканчивающееся пустым символом) или пpедопpеделенное имя класса оpгана упpавления.
WindowName: Заголовок или имя окна (заканчивающееся пустым символом).
Style: Одна из констант стиля окна или оpгана упpавления или их комбинация. К этим константам относятся константы ds_, ws_, bs_, cbs_, es_, lbs_, sbs_, ss_.
X, Y: Начальное положение окна или cw_UseDefault.
Width: Начальная шиpина окна (в пикселах).
Height: Начальная высота окна (в пикселах).
WndParent: Окно владельца.
Menu: Идентификатоp меню или дочеpнего окна.
Instance: Экземпляp соответствующего модуля.
Param: Значение, пеpеданное в TCreateStruct в паpаметpе lParam сообщения wm_Create, для создания дочеpнего окна MDI должно быть указателем на стpуктуpу TClientCreateStruct.
Для того чтобы создать окно, сначала необходимо зарегистрировать в системе новый оконный класс. Для этого нужно заполнить структуру WNDCLASS и передать указатель на область памяти, содержащую эту структуру, функции RegisterClass(). Данная функция создает и регистрирует новый оконный класс, используя значения элементов переданной ей структуры для определения характеристик класса. Ниже приведены описания всех элементов структуры WNDCLASS.
style - стиль окна данного класса.
lpfnWndProc - указатель на функцию окна, которая вызывается каждый раз при получении окном нового сообщения.
cbClsExtra и cbWndExtra - дополнительный размер резервируемой памяти (в байтах) для структур класса и окна.
hInstance - идентификатор приложения.
hIcon - идентификатор иконки, связанной с классом.
hCursor - идентификатор курсора, связанного с классом.
hbrBackground - идентификатор кисти, которой будет закрашен фон окна.
lpszMenuName - имя меню.
lpszClassName - имя создаваемого класса окна.
Как вы, наверное, заметили, в приведенном выше коде явно заполняются не все поля структуры, а только те, которые необходимы в для данного класса. Все остальные поля инициализируются нулевыми значениями с помощью функции ZeroMemory(), которая выполняет аналогичное ее имени действие.
После регистрации класса окна можно приступать к созданию самого окна. Для этого используется функция CreateWindow(), которая возвращает хендл создаваемого окна.
Создав окно, нам необходимо отобразить его на экране. Этим занимается функция ShowWindow(), которая принимает в качестве аргументов идентификатор окна, и флаг, указывающий на способ отображения окна (в данном случае SW_SHOW, определяющий, что окно необходимо показать на экране). Затем, с помощью функции UpdateWindow() мы посылаем нашему окну сообщение WM_PAINT, указывающее на необходимость перерисовать клиентскую область окна.
Далее следует цикл обработки сообщений. Он состоит из управляющей структуры while, которая при каждом проходе цикла получает очередное сообщение из очереди, посредством функции GetMessage(), затем переводит все сообщения от виртуальных клавиш в символьные сообщения с помощью функции TranslateMessage(), и после этого отсылает полученное сообщение на обработку оконной процедуре, используя функцию DispatchMessage().
Функция GetMessage() возвращает ненулевое значение, поэтому цикл не завершается до момента завершения программы. При завершении программы окну посылается сообщение WM_QUIT, которое является единственным сообщением, при получении которого функция GetMessage() возвращает ноль, и цикл обработки сообщений завершается, а код выхода из программы, хранящийся в элементе wParam структуры MSG, возвращается функцией WinMain.
И наконец, пора разобраться каким же образом оконная процедура обрабатывает переданные ей сообщения. У функции окна имеется четыре аргумента:
hWnd - идентификатор окна.
msg - код текущего сообщения.
wParam и lParam - дополнительная информация о сообщении.
В Windows существует более тысячи стандартных сообщений. Конечно же, программист не должен обрабатывать их все. Для этого существует функция DefWindowProc(), которая обрабатывает переданное ей сообщение по умолчанию. Таким образом, вы должны обрабатывать только те сообщения, обработка по умолчанию которых вас не устраивает. Также, функция DefWindowProc() не обрабатывает сообщение WM_DESTROY, поэтому вы должны предусмотреть его обработку самостоятельно. В приведенном примере, при получении окном сообщения WM_DESTROY, мы, с помощью функции PostQuitMessage(), ставим в очередь сообщение WM_QUIT, чтобы завершить работу программы.
Заметьте, каким образом сообщения обрабатываются по умолчанию. В структуре switch оконной процедуры предусмотрена метка default, которая пересылает все необрабатываемые нашей программой сообщения функции DefWindowProc() и возвращает результат этой функции. А если сообщение обрабатывается нашей программой, тогда возвращается ноль.
Примечание: в элементе style, структуры WNDCLASS, определяется общий стиль для всех окон данного класса. Следует заметить, что стиль класса это не тоже самое что и стиль окна, указанный в вызове функции CreateWindow(). Тот стиль, который устанавливается посредством функции CreateWindow(), является индивидуальным стилем окна, а не общим стилем, определенным в классе.
Для добавления компонента на форму используется аналогичная схема при помощи функции CreateWindow(). Для этого достаточно в ее первый параметр передать значение, в котором содержится название компонента
CreateWindow("edit", "edit1", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 100, 130, 120, 30, hWnd, NULL, hInst, NULL);
В девятый параметр передается хэндл (описатель) компонента, через который можно будет обращаться к самому компоненту в дальнейшем, типа HMENU, которые принято обозначать IDC_ХЭНДЛ, который в свою очередь описывается константой в разделе #define, например:
#define IDC_BUTTON1 100
…
HWND button1;
…
button1 = CreateWindow("button", "button1", WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,
100, 70, 120, 30, hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
Для получения текста, находящейся в текстбоксе используется функция GetWindowText(), а для записи в текстбокс используется функция SetWindowText().
При нажатии на компонент button генерируется сообщение WM_COMMAND, обработать ее можно в теле функции WndProc(), добавив его в оператор switch.
В приведенной программе при нажатии на кнопку button1 значение, находящееся в компоненте edit1 запишется в edit2 и выведется сообщение с содержанием компонента edit1.
Примерпрограммы:
Ex02.h
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // Оконная процедура
void button1_click(void);
#define IDC_BUTTON1 100
#define IDC_EDIT1 101
#define IDC_EDIT2 102
HWND hWnd; // Уникальный идентификатор окна (handle)
HWND button1; // Уникальный идентификатор кнопки
HWND edit1; //Уникальный идентификатор текстбокса
HWND edit2;
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
switch(wParam)
{
case IDC_BUTTON1:
button1_click();
break;
}
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
void button1_click(void)
{
TCHAR buf[256];
GetWindowText(edit1, buf, lstrlen (buf));
SetWindowText(edit2, buf);
MessageBox (hWnd, buf, "", 0);
}
Ex02.cpp
#include <windows.h>
#include "Ex02.h"
// Объявление функции окна (оконной процедуры)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
HINSTANCE hInst; // Идентификатор приложения
// Указатель на константную строку символов - имя программы и класса окна
LPCSTR AppName = "MyProgramm";
// Точка входа в программу - функция WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG msg; // Объявление структуры типа MSG, для работы с сообщениями
hInst = hInstance; // Сохраняем идентификатор приложения
// Заполняем структуру WNDCLASS
WNDCLASS wc;
// Инициализируем выделенную для структуры память нулями
ZeroMemory(&wc, sizeof(wc));
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(hInst, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = AppName;
RegisterClass(&wc); // Создаемирегистрируемоконныйкласс
// Создаем окно программы
hWnd = CreateWindow(
AppName, // Имя класса окна
AppName, // Заголовок окна
WS_OVERLAPPEDWINDOW, // Стиль окна
500, 200, // Горизонтальная и вертикальная позиции окна
300, 300, // Ширина и высота окна
NULL, // Хендл родительского окна
NULL, // Хендл меню
hInst, // Идентификатор приложения
NULL); // Дополнительные данные окна
button1 = CreateWindow("button", "button1", WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON, 100, 70, 120, 30, hWnd, (HMENU)IDC_BUTTON1, hInst, NULL);
edit1 = CreateWindow("edit", "edit1", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 100, 130, 120, 30, hWnd, (HMENU)IDC_EDIT1, hInst, NULL);
edit2 = CreateWindow("edit", "edit2", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 100, 180, 120, 30, hWnd, (HMENU)IDC_EDIT2, hInst, NULL);
ShowWindow(hWnd, SW_SHOW); // Отображаемокно
UpdateWindow(hWnd); // Перерисовываем окно
// Стандартный цикл обработки сообщений
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
Задание для самостоятельной работы:
Написать программу, вычисляющую следующие два выражения параллельно (использовать многонитевость) выражение (для ввода и вывода данных использовать компоненты edit, static и др.):
Номер варианта |
Задание |
1 |
Разработать программу, вычисляющую следующие два выражения параллельно (использовать многонитевость) выражение (для ввода и вывода данных использовать компоненты edit, static и др.):
|
2 |
Разработать программу, в которой по нажатию определенных кнопок будут создаваться новые компоненты |
3 |
Разработать программу, в которой размеры и положение окна можно регулировать внутри программы |
4 |
Разработать программу, в которой компоненты можно перетаскивать мышью |
5 |
Разработать программу, по нажатию кнопки в которой можно создать новое окно с компонентами |
6 |
Разработать двухоконное приложение, в котором одно окно рабочее, а другое управляет его внешним видом |
Содержание отчета:
Титульный лист.
Краткое теоретическое описание.
Задание на лабораторную работу, включающее формулировку задачи.
Результаты выполнения работы.
Контрольные вопросы:
Что выполняет функция CreateWindow()?
Для чего нужно объявлять функцию WndProc()?
Чем отличается стиль передающийся в функцию CreateWindow() от стиля, которые присваивается классу окна?
Лабораторная работа №3
«Контекст устройства»
Цель работы: приобретение навыков получения и использования контекста устройства для выполнения графических операций в программе.
Общие сведения:
С точки зрения программиста Windows является системой, не зависящей от устройств (device independent). Эту независимость со стороны Windows обеспечивает библиотека GDI32.dll, а со стороны устройства - драйвер этого устройства. С точки зрения программы связующим звеном между программой и устройством является контекст устройства (Device Context - DC). Если программе нужно осуществить обмен с внешним устройством, программа должна оповестить GDI о необходимости подготовить устройство для операции ввода-вывода. После того, как устройство подготовлено, программа получает хэндл контекста устройства, т.е. хэндл структуры,содержащей набор характеристик этого устройства. В этот набор входят: bitmap(битовая карта, изображение), отображаемый в окне, перо для прорисовки линий, кисть, палитра, шрифти т.д. Программа никогда напрямую не обращается к контексту устройства, она обращается к нему опосредствованно, через определенные функции. После того, как все действия произведены, и необходимость в использовании устройства отпала, программа должна освободить контекст устройства, чтобы не занимать память. Есть еще одна причина, из-за которой необходимо освобождать контекст устройства. В системе может существовать одновременно только ограниченное число контекстов устройств. Если контекст устройства не будет освобождаться после операций вывода, то через несколько перерисовок окна система может повиснуть. Так что не забывайте освобождать контексты устройств
Когда программа требует контекст устройства, она получает его уже заполненным значениями по умолчанию. Объект в составе контекста называется текущим объектом. Само слово - текущий - говорит о том, что контекст устройства можно изменить. Программа может создать новый объект, скажем, bitmap или шрифт, и сделать его текущем. Замещенный объект автоматически из памяти не удаляется, его необходимо позже удалить отдельно. Само собой разумеется, что программа может получить характеристики текущего устройства. А вот изменить эти характеристики можно только через замену объекта.
В Windows поддерживаются следующие типы контекстов устройств: контекст дисплея(обеспечивает работу с дисплеем); контекст принтера(обеспечивает работу с принтером); контекст в памяти(моделирует в памяти устройство вывода); информационный контекст(служит для получения данных от устройства). В этой лабораторной будет рассмотрен только контекст дисплея.
Windows поддерживает три типа контекста дисплея - контекст класса, приватный контекст и общий контекст. Первые два типа используются в приложениях, которые выводят на экран большое количество информации. Ярким примером такого рода приложений являются настольные издательские системы, графические пакеты и т.д.
Приложения, которые не очень интенсивно работают с экраном, используют общий контекст. Контекст класса является устаревшим и поддерживается только для обеспечения совместимости с предыдущими версиями Windows. Microsoft не рекомендует использовать его при разработке новых приложений и рекомендует использовать только приватный контекст.
Контексты устройств хранятся в кэше, управляемом системой. Хэндл общего контекста программа получает с помощью функций GetDC(), GetDCEx() или BeginPaint(). После того, как программа отработает с дисплеем, она должна освободить контекст, вызвав функцию ReleaseDC() или EndPaint() (в случае, если контекст получался с помощью BeginPaint()). После того, как контекст дисплея освобожден, все изменения, внесенные в него программой, теряются и при повторном получении контекста все действия по изменению контекста необходимо повторять заново.
Приватный контекст отличается от общего тем, что сохраняет изменения даже после того, как прикладная программа освободила его. Приватный контекст не хранится в кэше, поэтому прикладная программа может не освобождать его. Естественно, что в этом случае за счет использования большего объема памяти достигается более высокая скорость работы с дисплеем.
Для работы с приватным контекстом необходимо при регистрации класса окна указать стиль CS_OWNDC. После этого программа может получать хэндл контекста устройства точно так же, как и в случае общего контекста. Система удаляет приватный контекст в том случае, когда удаляется окно.
При работе с контекстами необходимо запомнить, что хэндлы контекста устройства с помощью функции BeginPaint() необходимо получать только в случае обработки сообщения WM_PAINT. Во всех остальных случаях необходимо использовать функции GetDC() или GetDCEx().
Получив контекст дисплея мы можем использовать графические возможности программы через специальные функции. Для того чтоб нарисовать прямую используем две функции MoveToEx() и LineTo(), которые устанавливают начальный и конечные координаты прямой соответственно. Для рисования эллипса есть функция Ellipse(), для рисование прямоугольника – Rectangle(), для вывода текста Textout(). Для установки параметров пера, кисти и шрифта используются соответственно функции CreatePen(), CreateSolidBrush(), CreateFont(). Функция SelectObject устанавливает созданный нами объект и возвращает предыдущий. После того как нарисовали необходимые объекты принято восстановить старый для установления параметров по умолчанию, поэтому устанавливая новый объект всегда сохраняйте старый. Анимации можно добиться по следующему алгоритму:
Рисуем объект
Рисуем точно такой же объект,цвет которого совпадает с цветом фона
Рисуем новый объект с новыми координатами
Перейти к п.2
При нажатии левой кнопки мыши создается сообщение WM_LBUTTONDOWN (WM_LBUTTONUP–отпускание левой кнопки мыши, WM_RBUTTONDOWN–нажатие правой кнопки мыши, WM_RBUTTONUP–отпускании правой кнопки мыши, WM_LBUTTONDBCLICK–двойное нажатие левой кнопки мыши), которое передается в функцию обработки сообщений. Относительные координаты клиентской области (координаты внутри окна) передаются в параметр lParam (LOWORD(lParam) – координата X, HIWORD(lParam)–координата Y). Абсолютные координаты курсора можно получить с помощью функции:
BOOL WINAPI GetCursorPos(
_Out_ LPPOINT lpPoint
);
Из полученных абсолютных координат можно вычислить относительные клиентские, если из них вычесть координаты левого верхнего угла клиентской области.
Далее будет приведен пример программы, в которой по нажатию первой кнопки будут рисоваться линия, эллипс и прямоугольник, а по нажатию второй кнопки – текст.
При нажатии какой-либо клавиши клавиатуры создается сообщение «VK_» + название клавиши, например VK_SPACE.
Пример программы:
Ex03.h
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void DrawFigures (void);
void DrawText(void);
#define IDC_BUTTON1 100
#define IDC_BUTTON2 101
HWND hWnd;
HWND button1;
HWND button2;
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
switch(wParam)
{
case IDC_BUTTON1:
DrawFigures();
break;
case IDC_BUTTON2:
DrawText();
break;
}
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
void DrawFigures (void)
{
HDC hDC = GetDC (hWnd); // получаемконтекстустройства
RECT rc; // создаем область
GetClientRect(hWnd, &rc); // получаем координаты клиентского окна
HPEN hPen = CreatePen (PS_SOLID, 3, RGB(255, 0, 0)); // создаемновоеперо
HPEN hOldPen = (HPEN)SelectObject (hDC, hPen); // устанавливаемновоеперосохранивстароев hOldPen
HBRUSH hBrush = CreateSolidBrush(RGB(0, 255, 0)); // создаемновуюкисть
HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, hBrush); //устанавливаем новую кисть сохраняем старую в hOldBrush
MoveToEx(hDC, rc.left, rc.top, NULL); // рисование линии от левого верхнего угла
LineTo(hDC, rc.right, rc.bottom); // к правому нижнему
Ellipse(hDC, 100, 100, 200, 150); // рисование эллипса
Rectangle(hDC, 100, 160, 200, 230); // рисование прямоугольника
SelectObject(hDC, hOldPen); // возвращаем старое перо
SelectObject(hDC, hOldBrush); // возвращаем старую кисть
DeleteObject (hPen); // уничтожаем созданное перо
DeleteObject(hBrush); // уничтожаем созданную кисть
ReleaseDC (hWnd, hDC); // освобождаем контекст устройства
return;
}
void DrawText(void)
{
HDC hDC = GetDC (hWnd); // получаем контекст устройства
RECT rc; // создаем область
GetClientRect(hWnd, &rc); // получаем координаты клиентского окна
HFONT hFont = CreateFont (20,15,FW_DONTCARE,FW_DONTCARE,FW_DONTCARE, // Устанавливаем шрифт
FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_DONTCARE,"MS Sans Serif");
HFONT hOldFont = (HFONT)SelectObject(hDC,hFont); // Устанавливаемновыйшрифтсохранивстарый
TextOut(hDC, 40, 240, "THIS IS TEXT",12); // Функциявыводатекста
SelectObject(hDC, hOldFont); // Возвращаемстарыйшрифт
DeleteObject(hFont); // уничтожаемсозданныйшрифт
ReleaseDC(hWnd, hDC); // освобождаемконтекстустройства
return;
}
Ex03.cpp
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void DrawFigures (void);
void DrawText(void);
#define IDC_BUTTON1 100
#define IDC_BUTTON2 101
HWND hWnd;
HWND button1;
HWND button2;
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
switch(wParam)
{
case IDC_BUTTON1:
DrawFigures();
break;
case IDC_BUTTON2:
DrawText();
break;
}
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
void DrawFigures (void)
{
HDC hDC = GetDC (hWnd); // получаемконтекстустройства
RECT rc; // создаем область
GetClientRect(hWnd, &rc); // получаем координаты клиентского окна
HPEN hPen = CreatePen (PS_SOLID, 3, RGB(255, 0, 0)); // создаемновоеперо
HPEN hOldPen = (HPEN)SelectObject (hDC, hPen); // устанавливаемновоеперосохранивстароев hOldPen
HBRUSH hBrush = CreateSolidBrush(RGB(0, 255, 0)); // создаемновуюкисть
HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, hBrush); //устанавливаемновуюкистьсохраняемстаруюв hOldBrush
MoveToEx(hDC, rc.left, rc.top, NULL); // рисование линии от левого верхнего угла
LineTo(hDC, rc.right, rc.bottom); // к правому нижнему
Ellipse(hDC, 100, 100, 200, 150); // рисование эллипса
Rectangle(hDC, 100, 160, 200, 230); // рисование прямоугольника
SelectObject(hDC, hOldPen); // возвращаем старое перо
SelectObject(hDC, hOldBrush); // возвращаем старую кисть
DeleteObject (hPen); // уничтожаем созданное перо
DeleteObject(hBrush); // уничтожаем созданную кисть
ReleaseDC (hWnd, hDC); // освобождаем контекст устройства
return;
}
void DrawText(void)
{
HDC hDC = GetDC (hWnd); // получаем контекст устройства
RECT rc; // создаем область
GetClientRect(hWnd, &rc); // получаем координаты клиентского окна
HFONT hFont = CreateFont (20,15,FW_DONTCARE,FW_DONTCARE,FW_DONTCARE, // Устанавливаем шрифт
FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_DONTCARE,"MS Sans Serif");
HFONT hOldFont = (HFONT)SelectObject(hDC,hFont); // Устанавливаемновыйшрифтсохранивстарый
TextOut(hDC, 40, 240, "THIS IS TEXT",12); // Функциявыводатекста
SelectObject(hDC, hOldFont); // Возвращаемстарыйшрифт
DeleteObject(hFont); // уничтожаемсозданныйшрифт
ReleaseDC(hWnd, hDC); // освобождаемконтекстустройства
return;
}
Задание для самостоятельной работы:
Номер варианта |
Задание |
1 |
Разработать программу для создания простых изображений. Панель инструментов создать в отдельном окне. |
2 |
Разработать игру «теннис», в которой, используя подвижную платформу, надо не дать упасть двигающемуся кругу. Для передвижения круга и платформы создать отдельные потоки. Добавить поддержку увеличения скорости круга. |
3 |
Разработать программу, в которой две фигуры перемещаются по главной форме произвольным образом так, чтобы при их столкновении они меняли направление аналогично реальным столкнувшимся шарам. Использовать многопоточность.
|
4 |
Разработать игру «Змейка». |
5 |
Разработать программу, в которой можно перетащить мышью простейшую нарисованную фигуру. |
6 |
Разработать программу, в которой с помощью мыши можно менять положение, размеры и наклон нарисованной прямой. |
Содержание отчета:
Титульный лист.
Краткое теоретическое описание.
Задание на лабораторную работу, включающее формулировку задачи.
Результаты выполнения работы.
Контрольные вопросы:
Что такое контекст устройства?
Какие бывают контексты устройства?
Обязательно ли освобождать контекст устройства после его использования? Пояснить ответ.
Лабораторная работа № 4
«Файловая система»
Цель работы: получение навыков работы с функциями файловой системы ОС Windows.
Общие сведения:
С помощью функций, входящих в состав программного интерфейса операционной системы Windows вы можете выполнять над файлами те же операции, что и в среде MS-DOS. Это открытие, закрытие, чтение, запись, позиционирование, удаление и т. п.

.
Значение каждого ряда должно вычисляться
параллельно.
и
.