257 65 2MB
Russian Pages 75
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Министерство образования и науки Российской Федерации Федеральное государственное бюджетное образовательное учреждение высшего профессионального образования «Оренбургский государственный университет» Кафедра вычислительной техники и защиты информации
Р.Р. Галимов, А.И. Сарайкин
ПРОГРАММИРОВАНИЕ В СРЕДЕ VISUAL C++ С ИСПОЛЬЗОВАНИЕМ БИБЛИОТЕКИ MFC
Рекомендовано к изданию Редакционно-издательским советом федерального государственного бюджетного образовательного учреждения высшего профессионального образования «Оренбургский государственный университет» в качестве методических указаний для студентов, обучающихся по программам высшего профессионального образования по направлениям подготовки 230100.62 Информатика и вычислительная техника и 090900.62 Информационная безопасность
Оренбург 2014
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
УДК 004.3(076) ББК 32.973.26-04я7 Г 15
Рецензент - кандидат технических наук, доцент А.C. Цыганков
Г15
Галимов, Р.Р. Программирование в среде Visual C++ c использованием библиотеки MFC: методические указания к лабораторным работам / Р.Р. Галимов, А.И. Сарайкин; Оренбургский гос. ун-т. - Оренбург: ОГУ, 2014. – 75 с.
Методические указания содержат 9 лабораторных работ. Каждая работа включает теоретическое изложение материала, постановку задачи, порядок выполнения и контрольные вопросы для самоподготовки. Методические указания рекомендованы преподавателям как вспомогательный материал в организации и проведении занятий, а также студентам по направлениям подготовки 090900.62 - Информационная безопасность и 230101.62- «Информатика и вычислительная техника» - для аудиторного и самостоятельного освоения лабораторного курса дисциплины «Объектноориентированное программирование».
УДК 004.3(076) ББК 32.973.26-04я7
© Галимов Р.Р., Сарайкин А.И., 2014 ©ОГУ, 2014
2
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Содержание
Введение………………………………………………………………………..…… 4 Обозначения и сокращения………………………………………………………… 5 1 Лабораторная работа № 1. Разработка Windows-приложения на основе программного интерфейса приложений API………………………………………….. 6 2. Лабораторная работа №2. Разработка диалоговых приложений с использованием библиотеки MFC …………………………………………………………... 14 3. Лабораторная работа №3. Изучение классов С++…..…………………………. 23 4. Лабораторная работа №4. Работа с пунктами меню и панелью инструментов 30 5. Лабораторная работа №5. Основы векторной графики в приложениях MFC.. 41 6 Лабораторная работа № 6. Объектно-ориентированный подход при работе с графическими примитивами…………………………………………...…………... 46 7 Лабораторная работа № 7. Изучение механизма сериализации……………….. 55 8 Лабораторная работа № 8 Работа с файлами…………………………………… 61 9 Лабораторная работа №9. Программирование алгоритма сокрытия информации в графических файлах………………………………………………….…… 67 Список использованных источников………..........................................................
73
Приложение А. Листинг программы на основе Win API………………………...
74
3
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Введение Настоящий лабораторный практикум предназначен для получения практических навыков студентами профиля подготовки - «Информационная безопасность» очной формы обучения при изучении дисциплины «Объектно-ориентированное программирование» (ООП). Лабораторный курс содержит 9 лабораторных работ и методические указания к ним. Каждая работа включает теоретическое изложение материала, постановку задачи, порядок выполнения и контрольные вопросы для самоподготовки. Общие методические рекомендации по использованию лабораторных работ и методических указаний: - к выполнению лабораторной работы следует приступать после ознакомления с теоретической частью соответствующего раздела и рекомендациями, приведенными в конкретной работе; - лабораторные работы рекомендуется выполнять в порядке их нумерации в аудиторное время, указанное в описании работы; - рекомендуется для экономии времени отчеты о лабораторных работах оформлять в виде протоколов работы с обязательным указанием даты, номера, темы, цели работы и выводов с краткой характеристикой результата; - дополнительные сведения по лабораторным работам содержатся в прилагаемом списке литературы. Лабораторный курс может быть освоен на индивидуальном компьютере со средними техническими характеристиками. Обязательным для полной реализации курса является наличие комплекта офисных приложений и среды программирования Visual C++, желательно иметь выход в Интернет. Практикум рекомендован преподавателям как вспомогательный материал в организации и проведении занятий, а также студентам - для аудиторного и самостоятельного освоения лабораторной части дисциплины ООП.
4
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Обозначения и сокращения API (Application Program Interface) - общее наименование целого набора базовых функций интерфейсов программирования приложений операционных систем семейств Microsoft Windows MFC (Microsoft Foundation Classes) - библиотека на языке C++, разработанная Microsoft и призванная облегчить разработку GUI-приложений ASCII (American Standard Code for Information Interchange) американская стандартная кодировочная таблица для печатных символов и некоторых специальных кодов BMP (BitMap Picture) - формат хранения растровых изображений GDI( Graphics Device Interface) -интерфейс Windows для представления графических объектов и передачи их на устройства отображения, такие как мониторы и принтеры LSB (Least Significant Bit) – метод в стеганографии, заключающийся в изменении младших битов в контейнере RGB(Red,Green,Blue) – цветовая модель SDI ( Single Document Interface)- приложения однодокументного интерфейса ЛК – левая кнопка мыши ПК –правая кнопка мыши ОС – операционная система
5
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
1 Лабораторная работа № 1. Разработка Windows-приложения на основе программного интерфейса приложений API Цель работы: изучить особенности работы Windows-приложений
1.1 Теоретическая часть
Наиболее часто используемыми средствами для разработки приложений в среде ОС Windows являются использование API и библиотеки MFC. В данной работе рассматривается задача разработки приложений с использованием API, так как она позволяет оценить особенности работы программ для ОС Windows. После запуска Windows-приложение, в большинстве случаев, переходит в режим ожидания сообщений от операционной системы. Windows генерирует большое количество разнообразных сообщений: щелчок кнопки мыши, нажатие клавиши клавиатуры. После получения сообщения приложение выполняет определенные действия и снова переходит в режим ожидания. Разработчиками ОС Windows была создана библиотека функций, при помощи которых происходит взаимодействие приложения с операционной системой, так называемые функции Программного интерфейса приложений (Application Program Interface)[2]. Для разработки минимальной программы с использованием Win API необходимо написать две функции: - WinMain(), с которой начинается выполнение программы и происходит её инициализация; - WndProc(), вызываемая Windows для обработки сообщений приложения[5]. Структура Windows- приложения представлена на рисунке 1.1. Функция WinMain() выполняет четыре основных действия: - сообщает ОС, какого вида окно требуется программе; - создает окно программы; - инициализирует окно программы; - извлекает сообщения Windows, предназначенные программе. 6
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Рисунок 1.1 – Структура Windows-приложения[5] На псевдокоде функцию WinMain() можно представить следующим образом: WinMain(список аргументов) { Подготовить и зарегистрировать класс окна с нужными характеристиками; Создать экземпляр зарегистрированного класса; Пока не произошло необходимое для выхода события { Извлечь очередное сообщение из очереди сообщений; Передать его через Windows оконной функции; } Возврат из программы
} Окно программы, в рамках Windows-приложения, создается на основе шаблона, который определяет параметры окна. Шаблон окна определяется структурой WNDCLASS. В приложении А представлен листинг программы, который создает Application Wizard среды программирования Visual Studio. В WinMain присутствуют функции MyRegisterClass, которая регистрирует шаблон окна, и InitInstance, непосредственно создающая окно программы. Одним из полей структуры WNDCLASS является поле lpfnWndProc, указывающая на оконную функцию WndProc(). Данная функция является «функцией обратного вызова». Такие функции вызываются ОС, а не самой программой. WindowProc() обрабатывает все сообщения, которые поступают окну программы. Ниже приведен листинг примера данной функции: LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
7
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Разобрать выбор в меню: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: добавьте любой код отрисовки... EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }
Слово CALLBACK означает, что это функция обратного вызова. Переменная целого типа message определяет тип сообщения. Данная оконная функция обрабатывает сообщения трех типов: - WM_COMMAND- выбор пункта меню; - WM_PAINT – прорисовка окна программы; - WM_DESROY- команда закрытия программы. Все остальные сообщения обрабатываются по умолчанию функцией DefWindowProc. Рассмотрим классический пример – вывести на экране текст «Здравствуй, МИР!». Для этого добавляем код, который обрабатывает сообщение WM_PAINT: case WM_PAINT: hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd,&rect); DrawText(hdc,L"Здравствуй, МИР!",-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER); EndPaint(hWnd, &ps); break;
Результат выполнения программы представлен на рисунке 1.2. 8
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Рисунок 1.2 – Экранная форма программы Таким образом, при разработке Windows-приложений задача программиста сводится во многом к написанию кода, который будет обрабатывать определенные события. В данной работе будут обрабатываться события нажатия клавиш клавиатуры и кнопок мыши. Сообщения от мыши делятся на две категории: сообщения клиентской области и не клиентской области. Рассмотрим несколько сообщений клиентской области: - WM_LBUTTONDOWN –нажатие левой кнопки мыши; - WM_MBUTTONDOWN –нажатие средней кнопки мыши; - WM_RBUTTONDOWN - –нажатие правой кнопки мыши. Общая структура сообщения WM_LBUTTONDOWN представлена на рисунке 1.3.
Рисунок 1.3 – Структура сообщения WM_LBUTTONDOWN Данное сообщение получает два параметра: - wParam - указывает, находятся ли в нажатом состоянии различные виртуальные клавиши. В таблице 1.1 представлены значения данного параметра; - lParam - младшее слово устанавливает x-координату курсора. Старшее слово устанавливает y-координату курсора. Аналогичную
структуру
имеют
сообщения
WM_MBUTTONDOWN
и 9
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
WM_RBUTTONDOWN. Таблица 1.1 - Значения параметра wParam Значение MK_CONTROL MK_LBUTTON MK_MBUTTON MK_RBUTTON MK_SHIFT MK_XBUTTON1 MK_XBUTTON2
Предназначение Клавиша CTRL находится в нажатом состоянии. Левая кнопка мыши находится в нажатом состоянии. Средняя кнопка мыши находится в нажатом состоянии. Правая кнопка мыши находится в нажатом состоянии. Клавиша SHIFT находится в нажатом состоянии. Windows 2000/XP: Первая X-кнопка находится в нажатом состоянии. Windows 2000/XP: Вторая X-кнопка находится в нажатом состоянии.
Как видно из таблицы, сообщение нажатия кнопки мыши не позволяет определить большинство клавиш клавиатуры, которые при этом удерживалась. Для этого используется функция GetKeyState. Функция GetKeyState извлекает данные о состоянии заданной виртуальной клавиши. Состояние определяет, является ли клавиша нажатой, не нажатой или переключенно[6]. Синтаксис функции: SHORT GetKeyState( int nVirtKey); где nVirtKey- определяет виртуальную клавишу. Если нужная виртуальная клавиша - буква или цифра (от А до Z, от а до z или от 0 до 9),то nVirtKey должен быть установлен в значение ASCII этого символа. Для других клавиш, он должен быть кодом виртуальной клавиши. Величина возвращаемого значения определяет состояние данной виртуальной клавиши как указано ниже: - если старший бит равен 1, клавиша нажата; иначе она отпущена. - если младший бит равен 1, клавиша переключилась. Клавиша, такая как CAPS LOCK, переключается, если она является включенной. Клавиша выключена и не переключается, если младший бит равен 0. Когда клавиша переключается, индикатор переключения клавиши на клавиатуре (если он есть) должен быть включен, и отключен, когда клавиша не переключается. В таблице 1.2 приведены виртуальные коды клавиш, используемых в работе.
10
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Таблица 1.2 – Коды виртуальных клавиш VK_LSHIFT –левый Shift VK_LCONTROL - левый Ctrl VK_LMENU- левый Alt
VK_RSHIFT - правый Shift VK_RCONTROL – правый Ctrl VK_RMENU – правый Alt
Сообщение WM_CHAR. Структура сообщения представлена на рисунке 1.4.
Рисунок 1.4 – Структура сообщения WM_CHAR В работе используется параметр wParam, который определяет код символа клавиши. 1.2 Постановка задачи Разработать Windows-приложение, с использованием API. Приложение должно отображать в окне символы нажатых клавиш клавиатуры. Позиция, в которой выводятся символы, определяется нажатием соответствующих кнопок мыши. Варианты заданий представлены в таблице 1.3. Таблица 1.3 – Варианты заданий № 1 2 3 4 5 6 7 8 9 10
Направление текста Справа Справа Справа Справа Справа Справа Справа Справа Справа Справа
Клавиша правый Shift левый Shift правый Ctrl левый Ctrl правый Alt левый Alt правый Shift левый Shift правый Ctrl левый Ctrl
Кнопка мыши Левая Правая Левая Правая Левая Правая Левая Правая Левая Правая
№ 11 12 13 14 15 16 17 18 19 20
Направление текста Слева Слева Слева Слева Слева Слева Слева Слева Слева Слева
Клавиша правый Alt левый Alt правый Shift левый Shift правый Ctrl левый Ctrl правый Alt левый Alt правый Shift левый Shift
Кнопка мыши Левая Правая Левая Правая Левая Правая Левая Правая Левая Правая
1.3 Порядок выполнения работы
1. Запустите
Microsoft
Visual
Studio
C++.
Выберите
команду 11
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Файл|Создать|Проект. В появившемся окне выберите шаблон «Visual C++|Проект Win32» и введите имя проекта, например, Lr1;
Рисунок 1.5 – Диалоговое окно создание проекта 2. Остальные параметры проекта, которые запрашивает AppWizard, оставьте по умолчанию. Откомпилируйте программу нажатием клавиши «F7»; 3. Запустите программу нажатием клавиш «Ctrl+F5». Убедитесь в работоспособности программы; 4. Закройте программу. В среде Visual Studio в окне классов выберите раздел «Глобальные функции и переменные» и найдите функцию WinMain (_tWinMain). Изучите данную функцию (рисунок 1.6);
Рисунок 1.6 – Окно классов 12
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
5. В начале файла Lr1.cpp, в разделе глобальных переменных, объявите следующие переменные str, x, y: // Глобальные переменные: HINSTANCE hInst; TCHAR szTitle[MAX_LOADSTRING]; TCHAR szWindowClass[MAX_LOADSTRING]; CString str; int x,y;
// текущий экземпляр // Текст строки заголовка // имя класса главного окна
6. Выберите в окне классов функцию WndProc. Добавьте в область оператора switch выбор события нажатия кнопки клавиатуры WM_CHAR. Добавляйте символ в строку str слева или справа в зависимости от варианта: case
WM_CHAR: str+=(TCHAR)wParam; //str=(TCHAR)wParam+str; InvalidateRect(hWnd,NULL,TRUE); break;
7. Добавьте в область оператора switch выбор события перерисовки WM_PAINT: case WM_PAINT: hdc = BeginPaint(hWnd, &ps); TextOut(hdc,x,y,str,str.GetLength()); EndPaint(hWnd, &ps); break;
8. Добавьте в область оператора switch выбор события нажатия определенной кнопки мыши по варианту. Ниже приведен пример для нажатия левой кнопки мыши: case WM_LBUTTONDOWN: //if (wParam&MK_CONTROL) if (GetKeyState(VK_RSHIFT)) { x=LOWORD(lParam); y=HIWORD(lParam); InvalidateRect(hWnd,NULL,TRUE); } break;
9. Откомпилируйте программу и проверьте её работу. 10. Сделайте выводы по работе и составьте отчет по работе.
1.4 Контрольные вопросы
1. Какими особенностями обладают Windows-приложения? 2. Назначение функции WinMain. 13
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
3. Назначение функции WindowProc. 4. Что такое Win API? 5. Что такое сообщение? 6. Назначение функции GetMessage. 7. Назначение функции TranslateMessage. 8. Определите, что за сообщения WM_PAINT, WM_DESTROY, WM_CHAR. 9. Назначение функции GetKeyState.
2 Лабораторная работа № 2. Разработка диалоговых приложений с использованием библиотеки MFC Цель: получить навыки разработки диалоговых приложений 2.1 Теоретическая часть Приложения, основанные на диалоговых окнах с использованием MFC, позволяют разрабатывать интерфейс пользователя, используя редактор ресурсов Visual C++ а, не программируя код. При создании приложения на основе диалоговых окон мастер проектов App Wizard создает три класса, например, CLR2_App, CLR2_Dlg и CAbout
Dlg. Ос-
новными являются два первых класса: класс приложения и класс диалогового окна. Класс приложения конструирует объект "главное окно" приложения, загружает настройки программы из реестра Windows. Класс диалогового окна осуществляет основную работу программы. Описание диалогового окна состоит из двух частей: описания ресурсов диалогового окна и класса диалогового окна. Для формирования ресурсов диалогового окна используется редактор ресурсов. Для этого откройте «Окно ресурсов» и выберите вкладку Dialog. Щелкните на ресурсе IDD_LR2_DIALOG (рисунок 2.1).
14
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Рисунок 2.1 – Окно ресурсов На диалоговом окне можно размещать элементы из панели элементов. В данной работе рассматриваются следующие элементы: Static Text, Edit Control и Check Box.
Рисунок 2.2 – Расположение элементов приложения Редактор ресурсов позволяет разместить элементы на диалоговом окне в нужных позициях и изменять их размеры. Каждый элемент управления содержит набор свойств, которые можно изменить в окне «Свойства». На рисунке 2.3 представлен пример редактирования свойства элемента.
15
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Рисунок 2.3 – Редактирование свойств элемента Некоторые свойства имеют все элементы, некоторые - только определенные. Рекомендуется изменить свойство «ИД» у элементов со стандартных значений на выражения, несущие смысловую нагрузку. Элемент Static Text. Используется для отображения текста, который нельзя редактировать. Основным свойством данного элемента является «Подпись», которое содержит текст, отображающийся на диалоговом окне. Элемент Check Box. Используется в качестве модификатора информации. Может принимать два или три состояния: «истина», «ложь» и «не определено». Элемент Edit Control. Этот элемент управления используется, когда требуется отредактировать одну или несколько строк текстовых данных. 2.1.2 Взаимодействие с элементами управления Для взаимодействия с элементами управления диалогового окна необходимо создать экземпляры классов для каждого элемента управления. Одним из способов создания экземпляра класса объекта является использование мастера Visual Studio Class Wizard. Данный способ позволяет создавать переменные, связанные с элементом управления. Для создания такой переменной необходимо выбрать пункт меню «Проект| Мастер классов». Далее выбираем класс диалогового окна и переходим на 16
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
вкладку «Переменные-члены».
Рисунок 2.4 – Создание переменной Необходимо выбрать идентификатор элемента управления и нажать кнопку «Добавить переменную».
Рисунок 2.5 – Добавление переменной В появившемся диалоговом окне вводим имя переменной, выбираем категорию. При выборе типа «Control» переменная будет связана непосредственно с объектом, при выборе «Value» - со значением определенного типа. Например, для текстового поля IDC_EDIT_LEVEL создадим переменную со значением категории «Value». В качестве типа данных выберем тип «float». В результате «Мастер классов» добавит новую переменную в класс: class CLR2_2Dlg : public CDialogEx { // Создание
17
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
public: . . . . . .
. ..
.
DECLARE_MESSAGE_MAP() private: float m_fProbability; float m_fLevel; }
В методе обмена данных классам DoDataExchange добавится новая строка: void CLR2_2Dlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT_lEVEL, m_fLevel); }
Механизм автоматического обмена данными позволяет привязать к органам управления диалоговой панели переменные или элементы данных класса диалоговой панели. Ряд специальных функций, определенных в библиотеке MFC, вызываются методом DoDataExchange и выполняют обмен данными между органами управления диалоговой панели и соответствующими элементами данных класса диалоговой панели. Такой обмен работает в обоих направлениях. Приложение не должно напрямую вызывать метод DoDataExchange. Он вызывается через метод UpdateData, определенный в классе CWnd. Если необходимо выполнить обмен данными, между органами управления и элементами данных класса диалоговой панели, используется метод UpdateData: BOOL UpdateData(BOOL bSaveAndValidate = TRUE);
Необязательный параметр bSaveAndValidate, определяет, как будет происходить обмен данными. Если метод UpdateData вызывается с параметром FALSE, выполняется инициализация диалоговой панели. Информация из элементов данных класса отображается в органах управления диалоговой панели. Для предотвращения ошибок ввода данных часто требуются сделать определенные элементы управления недоступными. Для этого используется метод EnableWindow класса CWnd: BOOL EnableWindow(
BOOL bEnable = TRUE );
При значении параметра bEnable окно доступно, иначе - недоступно. Ниже приведено часть кода, который делает недоступным строку ввода: CEdit *m_pEdit; m_pEdit=(CEdit*)GetDlgItem(IDC_EDIT_lEVEL);
18
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
m_pEdit->EnableWindow(m_bMeans);
На первом шаге определяем указатель на объект класса CEdit. Метод GetDlgItem определяет адрес объекта с идентификатором IDC_EDIT. В зависимости от значения булевой переменной bMeans текстовое поле становится либо доступным, либо нет.
2.2 Постановка задачи
Разработать приложение Windows на основе диалоговых окон, позволяющее рассчитать возможный стоимостной ущерб информационной системе из-за атак злоумышленников. Расчет ущерба в работе предлагается оценить по следующей формуле: ,
(2.1)
где Z – вероятностное значение ущерба в течение года; P – оценка вероятности реализации атаки; L – уровень снижения вероятности реализации атаки за счет использования средств защиты; U – оценочное значение ущерба при реализации одной атаки; λ – интенсивность потока атак на систему, дни-1. Предусмотреть проверки на отрицательные значения вводимых параметров, например, значения ущерба.
2.3 Порядок выполнения работы
1.Запустите
Microsoft
Файл|Создать|Проект.
В
Visual
Studio
появившемся
окне
C++.
Выберите
выберите
шаблон
команду «Visual
C++|MFC|Приложение MFC» и введите имя проекта, например, Lr2; 2. Выберите тип приложения «На основе диалоговых окон». Остальные параметры на данном окне оставьте по умолчанию.
19
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Рисунок 2.6 – Диалоговое окно выбора типа приложения 3. Скомпилируйте проект (F7) и запустите приложение (F5). 4. Откройте в среде Visual С++ окно ресурсов и выберите диалоговое окно IDD_LR2_DIALOG. 5. Удалите элемент ID_STATIC1 с текстом «TODO: Разместите здесь элементы управления диалоговым окном.». Разместите на диалоговом окне элементы: Static Text, Edit Control, Check Box. Возможное расположение элементов представлено на рисунке 2.4. 6. В окне свойств измените идентификаторы элементов Edit Control и Check Box, чтобы в их названии присутствовала подсказка на их назначение. Например, ID_EDIT_PROBABILITY. 7. Добавьте в класс CLR2_Dlg переменную, связанную со строкой ввода вероятности реализации атаки. Для этого в диалоговом окне выделите элемент ID_EDIT_PROBABILITY и нажмите ПК мыши. В появившемся контекстном меню выберите пункт «Добавить переменную» (рисунок 2.7).
20
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Рисунок 2.7 – Добавление переменной В появившемся диалоговом окне установите категорию в значение «Value», тип переменной – float. Имя переменной вводите на свое усмотрение. Также установите минимальное и максимальное значения, принимаемые данной переменной (рисунок 2.8).
Рисунок 2.8 – Выбор параметров переменной 8.Аналогично добавьте переменные для остальных элементов Edit Control. Для элемента Check Box выберите тип данных BOOL, элемента для вывода результата расчета ID_STATIC_SUMMA значение CString. 9. Для строки ввода уровня повышения безопасности за счет использования средства защиты добавьте еще одну переменную. Для этой переменной в разделе категория выберите значение Control. 10. Измените идентификатор кнопки IDOK на ID_BUTTON_CALCULATE. 21
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
В свойстве «Подпись» данной кнопки введите значение «Рассчитать», а для IDCANCEL – «Выход». 11. Создайте обработчик для кнопки ID_BUTTON_CALCULATE события нажатия BN_CLICED. Для дважды щелкните ЛК мыши на кнопке на диалоговом окне: void CLR2_2Dlg::OnBnClickedButtonCalculate() { // TODO: добавьте свой код обработчика уведомлений }
12. Добавьте код, вычисляющий значение ущерба по формуле 2.1. 13. Добавьте обработчик события BN_CLICED для элемента Check Box. В обработчике события напишите код, который делает недоступным строку ввода уровня повышения безопасности при снятии флажка. Для этого используйте метод EnableWindow класса окна. 14. Откройте окно классов и выберите CLR2_Dlg. Далее выберите метод OnInitDialog. Добавьте в этот метод код, который при запуске программы снимает флажок использования средства защиты и инициализирует начальные значения полей ввода (рисунок 2.9).
Рисунок 2.9 – Добавление кода по инициализации приложения 15. Скомпилируйте программу и проверьте работоспособность приложения. 16. Составьте отчет по работе и ответьте на контрольные вопросы.
22
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
2.4 Контрольные вопросы
1. Дайте понятие переменной, связанной с элементом управления. 2. Какие классы формирует мастер проектов при создании приложения на базе диалоговых окон? 3. Назначение метода UpdateData. 4. Назначением метода DoDataExchange. 5. Назначением метода EnableWindow. 6. Назначение метода GetDlgItem.
3 Лабораторная работа № 3. Изучение классов С++ Цель: получить навыки по созданию классов и работ с массивами данного типа 3.1 Теоретическая часть Класс является абстрактным типом данных, определяемым пользователем, и представляет собой модель реального объекта в виде данных и функций для работы с ними. Данные класса называются полями (по аналогии с полями структуры), а функции класса — методами[3]. Класс описывается следующим образом: class { private: ; protected: ; public: ; };
Компоненты класса, объявленные в секции private, называются внутренними. Они доступны только компонентным функциям того же класса и функциям. Компоненты класса, объявленные в секции protected, называются защищенными. Они доступны компонентным функциям не только данного класса, но и его потомков.
23
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Компоненты класса, объявленные в секции public, называются общими. Они доступны за пределами класса в любом месте программы. Пример класса приведен ниже: class smallobj { private: int somedata; public: void setdata(int c); void showdata(); };
В классе объявлена внутренняя переменная somedata, доступ к которой осуществляется только через методы setdata и showdata. Далее приведена реализация данных методов и пример использования: void smallobj::setdata(int c) { somedata=c; } void smallobj::showdata() { coutSelectObject(pOldPen);
Метод контекста устройства SelectObject устанавливает новое перо и сохраняет адрес старого. После использования нового пера рекомендуется восстановить старок перо. Параметры заполнения замкнутых фигур определяется объектом класса CBrush. Для установки параметров кисти используются методы: BOOL CBrush::CreateSolidBrush(COLORREF crColor); BOOL CBrush:: CreateHatchBrush ( int nIndex, COLORREF crColor);
где crColor – цвет кисти; nIndex - определяет один из возможных вариантов штриховки. Пример использования пера приведен ниже: CBrush aBrush; aBrush.CreateHatchBrush(HS_DIAGCROSS,RGB(0,255,0)); CBrush* pOldBrush=pDC->SelectObject(&aBrush); pDC->Rectangle(30,30,200,200); pDC->SelectObject(pOldBrush);
В результате рисуется прямоугольник, заполненный штриховкой зеленого цвета. Старую кисть после выполнения необходимых действий также рекомендуется восстановить. 44
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
5.2 Постановка задачи
Написать программу, которая рисует элементы с различными цветами. Для замкнутых фигур использовать различные виды штриховки.
5.3 Порядок выполнения работы
1. Создайте новый проект в среде Visual C++ на базе шаблона однодокументного приложения MFC; 2. Выберите класс представления и его метод OnDraw(); 3. В метод OnDraw() добавьте код позволяющий рисовать элементы, указанные в пункте 5.2.; 4. Проверьте правильность работы программы и составьте отчет по работе; 5. Ответьте на контрольные вопросы.
5.4 Контрольные вопросы
1. Когда вызывается метод OnDraw() класса представления MFC? 2. Где будет находиться текущая точка после выполнения команды LineTo? 3. Каким объектом определяется параметры рисуемых линий? 4. Для каких фигур имеет значение объект кисть? 5. Для чего после рисования рекомендуется восстанавливать старое перо?
45
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
6 Лабораторная работа № 6. Объектно-ориентированный подход при работе с графическими примитивами Цель: получить навыки работы с механизмом наследования
6.1 Теоретическая часть
Механизм наследования является одним из основополагающих принципов ООП, который позволяет уменьшить размер разрабатываемого кода. Базовый класс содержит методы и поля общие для производных классов. Создание нового класса на базе существующего позволяет получить всю его функциональность. Рассмотрим механизм наследования на примере задачи рисования графических элементов: линии, кривой, окружности и прямоугольника. Данные объекты обладают схожими чертами, например, свойством «цвет пера» и метод «нарисовать себя» Draw(). Поэтому при разработке программы целесообразно выделить базовый класс CElement и производные классы, которые определяют индивидуальные особенности каждого элемента. Иерархия классов представлена на рисунке 6.1.
Рисунок 6.1 –Иерархия классов
Возможный вид класса CElement представлен ниже: class CElement : public CObject {
46
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
DECLARE_SERIAL(CElement) protected: int m_PenWidth;//толщина пера COLORREF m_Color; //цвет пера CRect m_EnclosingRect; //описывающий прямоугольник CElement(); public: virtual ~CElement(); virtual void Draw(CDC* pDC,CElement* pElement=nullptr); //виртуальный метод прорисовки элемента CRect GetBoundRect(void) const; //определение ограничивающего прямоугольника с учетом толщины пера virtual void Move(const CSize& aSize); virtual void Serialize(CArchive& ar); };
Особый интерес представляют методы с ключевым словом virtial. Виртуальный означает видимый, но не существующий в реальности. Виртуальный метод переопределяется у наследников. Рассмотрим пример. Пусть для хранения графических элементов используется массив указателей на базовый класс: CElement* pShapes[100];
Для того чтобы нарисовать все фигуры достаточно написать следующий код: for (in i=0;iDraw();
Получается, что прорисовка разных графических элементов производится единообразно при помощи вызова одного и того же метода. Достаточно указателю базового класса присвоить адрес нужного объекта. При компиляции программы компилятор не знает, чей метод он должен вызвать для прорисовки фигуры. Данное решение откладывается до запуска программы. При выполнении же программы станет известно адрес какого объекта хранится в указателе и какой метод нужно вызвать. Такой подход называется поздним связыванием. Позднее связывание требует больше ресурсов, но дает выигрыш в возможностях и гибкости[5].
6.2 Постановка задачи
Продолжить программу из работы №5. Измененная программа должна позволять рисовать графические фигуры согласно своему варианту из работы 4. Рисова47
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
ние производится мышью: - нажатие ЛК мыши на клиентской области инициируется процесс рисования геометрической фигуры; - перемещение мыши с удержанием ЛК мыши приводит к изменению размера фигуры; - отпускание ЛК мыши приводит к фиксированию объекта.
6.3 Порядок выполнения работы
1. Откройте проект из работы 5. Удалите написанный ранее код из метода OnDraw объекта представления. 2. Выделите приложение mfc2 в окне классов и нажатием ПК мыши вызовите контекстное меню. В меню выберите пункт «Добавить|Класс». В появившемся окне выберите шаблон «Класс MFC». Нажмите кнопку «Добавить».
Рисунок 6.2 –Выбор шаблона класса 3. Введите имя класса CElement, а в качестве базового класса выберите CObject. Остальные параметры оставьте по умолчанию. В результате появится следующий класс, который описан в файле Element.h, а реализация методов – в Element.cpp: #pragma once // CElement class CElement : public CObject { DECLARE_DYNAMIC(CElement) public: CElement(); virtual ~CElement(); protected: DECLARE_MESSAGE_MAP()
48
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
};
4.
Класс CElement содержит переменные и функции, общие для всех про-
изводных классов, и виртуальные функции, которые заменяются в производных классах. Произведем изменения класса CElemеnt следующим образом: class CElement : public CObject { DECLARE_DYNAMIC(CElement) protected: int m_PenWidth;//толщина пера COLORREF m_Color;//цвет пера CRect m_EnclosingRect;//прямоугольник, описывающий элемент public: CElement(); virtual ~CElement(); virtual void Draw(CDC* pDC){}//виртуальная операция рисования CRect GetBoundRect() const;// получить ограничивающий прямоугольник };
5. Каждый элемент будет хранить информацию об описывающем прямоугольнике в переменной m_EnclosingRect базового класса. Для определения ограничивающего прямоугольника с учетом толщины пера реализуйте метод GetBoundRect в файле Element.cpp: CRect CElement::GetBoundRect() const { CRect boundingRect(m_EnclosingRect);// переменная для хранения ограничивающего прямоугольника boundingRect.InflateRect(m_PenWidth,m_PenWidth);//расширить прямоугольник на ширину пера return boundingRect; }
6. Выделите приложение mfc2 в окне классов и нажатием ПК мыши вызовите контекстное меню. В меню выберите пункт «Добавить|Класс». В появившемся окне выберите шаблон «Класс C++». Нажмите кнопку «Добавить». 7. Введите имя класса CLine, в поле базовый класс – CElement.В поля Файл.h и Файл.cpp выберите соответственно Element.h и Element.cpp. 8. В описание класса CLine добавьте следующий код: class CLine : public CElement { public: CLine(void); ~CLine(void); virtual void Draw(CDC* pDC);//Функция отображения линии CLine (const CPoint& start,const CPoint& end,COLORREF aColor);//конструктор объекта линии protected: CPoint m_StartPoint; CPoint m_EndPoint; };
9. В файле Element.cpp реализуйте конструктор класс CLine: CLine::CLine(const CPoint& start,const CPoint& end,COLORREF aColor):m_StartPoint(start),m_EndPoint(end) {
49
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
m_PenWidth=1; m_Color=aColor; //Определение описывающего прямоугольника m_EnclosingRect=CRect(start,end); m_EnclosingRect.NormalizeRect(); }
10. Реализуйте метод Draw класса CLine. Ниже приведен программный код: void CLine::Draw(CDC* pDC) { CPen aPen; if (!aPen.CreatePen(PS_SOLID,m_PenWidth,m_Color)) { AfxMessageBox(_T("Не удалось создать перо!"),MB_OK); AfxAbort(); } CPen* pOldPen=pDC->SelectObject(&aPen); pDC->MoveTo(m_StartPoint); pDC->LineTo(m_EndPoint); pDC->SelectObject(pOldPen); }
11.
Добавьте в проект класс CRectangle аналогично, как CLine. Описание
класса представлено ниже: class CRectangle : public CElement { public: CRectangle(void); ~CRectangle(void); virtual void Draw(CDC* pDC);//Функция отображения линии CRectangle (const CPoint& start,const CPoint& end,COLORREF aColor);//конструктор объекта прямоугольник };
12. Реализуйте конструктор класса CRectangle: CRectangle::CRectangle(const CPoint& start,const CPoint& end,COLORREF aColor) { m_PenWidth=1; m_Color=aColor; //Определение описывающего прямоугольника m_EnclosingRect=CRect(start,end); m_EnclosingRect.NormalizeRect(); }
13. Напишите программный код метода Draw() класса CRectangle. Будем рисовать только контур прямоугольника, поэтому в качестве кисти будем использовать стандартную кисть NULL_BRUSH. Ниже представлен код: void CRectangle::Draw(CDC* pDC) { CPen aPen; if (!aPen.CreatePen(PS_SOLID,m_PenWidth,m_Color)) { AfxMessageBox(_T("Не удалось создать перо!"),MB_OK); AfxAbort(); } CPen* pOldPen=pDC->SelectObject(&aPen); CBrush* pOldBrush=(CBrush*)pDC->SelectStockObject(NULL_BRUSH); pDC->Rectangle(m_EnclosingRect); pDC->SelectObject(pOldPen); pDC->SelectObject(pOldBrush);
50
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
}
14. Добавьте в проект класс CCircle, аналогично, как класс CRectangle в пункте 11. 15. Напишите конструктор класса CCircle, как приведено ниже: CCircle::CCircle (const CPoint& start,const CPoint& end,COLORREF aColor) { long radius=static_cast(sqrt(static_cast((end.x-start.x)*(end.xstart.x)+(end.y-start.y)*(end.y-start.y)))); m_EnclosingRect=CRect(start.x-radius,start.y-radius,start.x+radius,start.y+radius); m_EnclosingRect.NormalizeRect(); m_Color=aColor; m_PenWidth=1; }
16. Для использования функции sqrt() необходимо в файл Element.cpp библиотеку cmath. 17. Реализуйте код рисования окружности для класса CCircle: void CCircle::Draw(CDC* pDC) { CPen aPen; if (!aPen.CreatePen(PS_SOLID,m_PenWidth,m_Color)) { AfxMessageBox(_T("Не удалось создать перо!"),MB_OK); AfxAbort(); } CPen* pOldPen=pDC->SelectObject(&aPen); CBrush* pOldBrush=(CBrush*)pDC->SelectStockObject(NULL_BRUSH); pDC->Ellipse(m_EnclosingRect);//рисование окружности pDC->SelectObject(pOldPen); pDC->SelectObject(pOldBrush); }
18. Добавьте в проект класс CCurve: class CCurve : public CElement { public: CCurve(void); ~CCurve(void); virtual void Draw(CDC* pDC);//Функция отображения кривой CCurve (const CPoint& first,const CPoint& second,COLORREF aColor);//конструктор объекта кривой void AddSegment(const CPoint& point);//добавить сегмент кривой protected: std::vector m_Points;//множество точек, определяющих кривую };
19. Подключите к файлу Element.h библиотеку vector. 20. Добавьте определение конструктора класса CCurve: CCurve::CCurve (const CPoint& first,const CPoint& second,COLORREF aColor) { m_Points.push_back(first); m_Points.push_back(second); m_EnclosingRect=CRect(min(first.x,second.x), min(first.y,second.y), max(first.x,second.x), max(first.y,second.y));
51
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
m_Color=aColor; m_PenWidth=1; }
21. Напишите метод прорисовки кривой класса CCurve: void CCurve::Draw(CDC* pDC) { CPen aPen; if (!aPen.CreatePen(PS_SOLID,m_PenWidth,m_Color)) { AfxMessageBox(_T("Не удалось создать перо!"),MB_OK); AfxAbort(); } CPen* pOldPen=pDC->SelectObject(&aPen); pDC->MoveTo(m_Points[0]); for (size_t i=1;iLineTo(m_Points[i]); pDC->SelectObject(pOldPen); }
22. Добавьте метод добавления новой точки к кривой: void CCurve::AddSegment(const CPoint& point) { m_Points.push_back(point); m_EnclosingRect=CRect(min(point.x,m_EnclosingRect.left), min(point.y,m_EnclosingRect.top), max(point.x,m_EnclosingRect.right), min(point.y,m_EnclosingRect.bottom)); }
23. Добавьте в класс документа методы, которые позволяют получить тип выбранного пользователем элемент и цвет пера, которые были определены в работе №4: unsigned int Cmfc2Doc::GetElementType(void) { return m_Element; } COLORREF Cmfc2Doc::GetColorElement(void) { return m_Color; }
24. Добавьте метод CreateElement в класс представления: CElement* Cmfc2View::CreateElement(void) const { Cmfc2Doc* pDoc=GetDocument(); ASSERT_VALID(pDoc); switch (pDoc->GetElementType()){ case RECTANGLE: return new CRectangle(m_FirstPoint,m_SecondPoint,pDoc->GetColorElement); case LINE: return new CLine(m_FirstPoint,m_SecondPoint,pDoc->GetColorElement()); case CIRCLE: return new CCircle(m_FirstPoint,m_SecondPoint,pDoc->GetColorElement()); case CURVE: return new CCurve(m_FirstPoint,m_SecondPoint,pDoc->GetColorElement()); default: AfxMessageBox(_T("Неверный код элемента"),MB_OK); AfxAbort(); return nullptr; } return NULL;
52
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
}
25. В файл Cmfc2View.h включите библиотеку Element.h. 26. Добавьте
в
класс
представления
переменную
m_FirstPoint
и
m_SecondPoint типа CPoint и инициализируйте их в конструкторе класса значением CPoint(0,0). 27.
Добавьте в класс представления указатель m_pTempElement и инициа-
лизируйте его в конструкторе класса значением nullptr. 28. Создайте обработчик сообщения нажатия левой кнопки мыши в классе представления. Для этого в обозревателе классов выделите Cmfc2View и в окне свойств
нажмите
кнопку
«Сообщения».
Найдите
сообщение
WM_LBUTTON_DOWN, нажмите на стрелку и выберите пункт «Add».
Рисунок 6.3 –Создание обработчика события мыши 29. В обработчике события нажатия ЛК мыши сохраните координаты: void Cmfc2View::OnLButtonDown(UINT nFlags, CPoint point) { m_FirstPoint=point; }
30. Добавьте обработчик события перемещения мыши в классе представления: void Cmfc2View::OnMouseMove(UINT nFlags, CPoint point) { // TODO: добавьте свой код обработчика сообщений или вызов стандартного CClientDC aDC(this);//контекст устройства для данного представления if (nFlags&&MK_LBUTTON)//проверить, что нажата левая кнопка
53
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
{ m_SecondPoint=point;//сохранить координаты мыши if (m_pTempElement) { if (CURVE==GetDocument()->GetElementType())//если это кривая { static_cast(m_pTempElement)->AddSegment(m_SecondPoint); m_pTempElement->Draw(&aDC); return; } //перерисовываем старый элемент, чтобы он исчез aDC.SetROP2(R2_NOTXORPEN); m_pTempElement->Draw(&aDC); delete m_pTempElement; m_pTempElement=nullptr; } m_pTempElement=CreateElement();//создать новый элемент m_pTempElement->Draw(&aDC);//нарисовать элемент } }
31. Создайте обработчик события отпускания кнопки мыши в классе представления: void Cmfc2View::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: добавьте свой код обработчика сообщений или вызов стандартного if (m_pTempElement) { delete m_pTempElement; m_pTempElement=nullptr; }
32. Для того чтобы обрабатывать сообщения мыши при выходе за пределы клиентской области добавьте в обработчике следующие строчки: void Cmfc2View::OnLButtonDown(UINT nFlags, CPoint point) { m_FirstPoint=point; SetCapture();//перехватывать все последующие сообщения мыши } void Cmfc2View::OnLButtonUp(UINT nFlags, CPoint point) { if (this==GetCapture()) ReleaseCapture();//прекратить перехват сообщений от мыши if (m_pTempElement) { delete m_pTempElement; m_pTempElement=nullptr; }
33. Проверьте работу программы. 34. Ответьте на контрольные вопросы и подготовьте отчет по работе.
6.4 Контрольные вопросы
1. Назначение функции SETROP2. 54
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
2. Назначение параметра nFlags обработчика события WM_MOUSE_MOVE. 3. Объясните механизм позднего связывания 4. В каких случаях имеет смысл использовать виртуальные методы? 5. Как в данной работе производится удаление старых элементов? 6. Назначение метода InflateRect класса CRect. 7. Назначение метода NormalizeRect класса CRect. 8. Назначение функции GetCapture.
7 Лабораторная работа № 7. Изучение механизма сериализации Цель: получить навыки работы с механизмом сериализации
7.1 Теоретическая часть
Документ программы на базе MFC представляет собой сложный объект, который может содержать большое многообразие объектов, каждый из которых в свою очередь другие. Задача сохранения объекта в файл на внешнем носителе является более сложной, чем сохранение обычных типов данных, таких как int. При сохранении объекта в файл записываемая информация должна содержать полную спецификацию класса. Процесс чтения должен синтезировать объект на основе данной спецификации. Библиотека MFC предоставляет удобный механизм ввода-вывода на внешний носитель объектов, называемый сериализацией. Данный механизм предполагает, что каждый класс сам отвечает за сохранение и восстановление. Каждый сериализуемый класс должен содержать виртуальную функцию Serialize(): void Cmfc2Doc::Serialize(CArchive& ar) { if (ar.IsStoring()) { //добавить сюда код сохранения } else { //добавить сюда код загрузки
55
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
} }
Метод IsStoring определяет, выполняется сохранение объекта или его восстановление. Единственным параметром данной функции является указатель на объект класса CArchive. Данный класс предоставляет аналог библиотеки MFC потоковых операций С++. Объект данного класса позволяет направлять объекты документа в файл, либо их восстанавливать. Объект класса CArchive включает объект типа CFile, который осуществляет работу непосредственно с физическим файлом. В большинстве случаев программисту нет необходимости работать с объектом CFile. Класс CArchive определяет логику структурирования объектов документа при их записи и восстановлении. Процесс сериализации упрощенно представлен на рисунке 7.1[5]
Риусунок 7.1 – Обобщенная схема процесса сериализации объекта документа В ответ на сообщение о сериализации объект класса документа инициирует процесс сериализации для базовых типов и своих внутренних объектов. При этом как будет производиться сохранение/восстановление внутренних объектов класс документа не знает. Внутренние объекты в свою очередь сериализуют свои базовые типы и инициируют процесс сериализации для своих объектов. 7.2 Постановка задачи 56
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Добавьте в программу, из предыдущей работы, возможность хранения графических элементов в динамическом массиве на случай восстановления изображения при перерисовке экрана. Также программ должна уметь сохранять рисунок в файл и восстанавливать его из файла на основе механизма сериализции.
7.3 Порядок выполнения работы
1.Откройте в среде Visual C++ проект, реализованный в 6 работе. 2. Добавьте в класс документа переменную типа контейнера list для хранения списка графических элементов в разделе protected: std::list m_ElementList;
3. Подключите библиотеку list в файл mfc2Doc.h: #include
4. Добавьте в класс документа метод AddElement, который включает в список новый элемент: void AddElement(CElement* pElement) { m_ElementList.push_back(pElement); SetModifiedFlag(); };
7. Измените деструктор класса документа таким образом, чтобы пробежаться по списку элементов и удалить каждый из них: Cmfc2Doc::~Cmfc2Doc() { for (auto iter=m_ElementList.begin();iter!=m_ElementList.end();++iter) delete *iter; m_ElementList.clear(); }
8. Прорисовка элементов производится в классе представления, а список элементов хранится в классе документа в разделе protected. Поэтому в классе документа требуются методы, которые позволят получить доступ к списку из других классов: std::list::const_iterator begin()const { return m_ElementList.begin(); } std::list::const_iterator end()const { return m_ElementList.end(); }
57
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
Данные методы возвращают указатели на начало и конец списка. 9. В метод OnDraw класса представления добавьте следующий код: void Cmfc2View::OnDraw(CDC* pDC) { Cmfc2Doc* pDoc=GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; CElement* pElement(nullptr); for (auto iter=pDoc->begin();iter!=pDoc->end();++iter) { pElement=*iter; if (pDC->RectVisible(pElement->GetBoundRect())) pElement->Draw(pDC); } }
10. В обработчике события OnLButtonUp класса представления реализуйте код, который добавляет новый элемент в список: void Cmfc2View::OnLButtonUp(UINT nFlags, CPoint point) { if (m_pTempElement) { GetDocument()->AddElement(m_pTempElement); InvalidateRect(nullptr); m_pTempElement=nullptr; } }
11. Откомпилируйте проект и убедитесь в работоспособности программы. 12. Добавим в проект механизм сериализации для ввода-вывода рисунка. Для этого добавьте в определение класса CElement макрос DECLARE_SERIAL: class CElement : public CObject { DECLARE_SERIAL(CElement) protected: int m_PenWidth;
………………………….. 13. Добавьте
в
файл
реализации
класса
CElement
макрос
IMPLEMENT_SERIAL сразу после директивы #include: // Element.cpp: файл реализации #include "stdafx.h" #include #include "mfc2.h" #include "Element.h" IMPLEMENT_SERIAL(CElement,CObject,VERSION_NUMBER)
14. Добавьте код для сериализации объекта класса документа: void Cmfc2Doc::Serialize(CArchive& ar) { if (ar.IsStoring()) { arm_hDC,0,0,cr.Width(),cr.Height(),0,0,SRCCOPY);//копирование //изображения
Для получения интенсивности цвета красного, зеленого и синего каналов используются соответственно следующие макросы: GetRValue, GetGValue и GetBValue. Формат GetRValue приведен ниже: BYTE GetRValue (
DWORD RGB);
Функция возвращает интенсивность красного компонента цвета, представленного в модели RGB. Аналогичный формат имеют и два других макроса.
9.2 Постановка задачи
Разработать приложение на основе диалоговых окон, позволяющее спрятать в графическом рисунке формата BMP информацию из файла.
69
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
9.3 Порядок выполнения работы 1. Создайте проект Lr9 на основе диалогового окна с использованием MFC. 2. Разместите на диалоговом окне следующие элементы: Picture Control и 3 кнопки. Первая кнопка будет использовать для открытия файла с изображением, вторая кнопка для открытия файла источника данных, а третья – для чтения скрытой информации из изображения с последующим сохранением. Расположение элементов представлено на рисунке 9.3.
Рисунок 9.3 – Экранная форма программы 3. Измените свойство Type элемента Picture Control на значение Bitmap . 4. Добавьте переменную управления m_picture в класс CLr9Dlg, связанную с элементом Picture Control. 5. Добавьте переменную m_Image типа CImage в класс CLr9Dlg. 6. Напишите обработчик события нажатия первой кнопки, который открывает графический файл формата BMP при помощи переменной m_Iimage и отображает в элементе Picture Control: TCHAR szFilters[]= _T("BitMap (*.bmp)|*.bmp||"); CFileDialog dlg(true,_T("BitMap"), _T("*.bmp"), OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilters); if (dlg.DoModal()==IDOK) { CDC* pDC=m_picture.GetDC(); if (!pImage.IsNull()) pImage.Detach(); pImage.Load(dlg.GetPathName()); CRect cr; m_picture.GetClientRect(&cr); pImage.BitBlt(pDC->m_hDC,0,0,cr.Width(),cr.Height(),0,0,SRCCOPY); }
7. Напишите обработчик события второй кнопки, который открывает файл с 70
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
информацией и скрывает его в графическом файле. Предусмотреть проверки, что файл-контейнер выбран и что размер рисунка достаточен для сохранения информации: unsigned char buff[100]; TCHAR szFilters[]= _T("All (*.*)|*.*||"); CFileDialog dlg(true,_T("All"), _T("*.*"), OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilters); char szFiltersSave[]="BitMap (*.bmp)|*.bmp||"; CFileDialog dlgsave(false,"BitMap", "*.bmp", OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFiltersSave); if (dlg.DoModal()==IDOK)//выбор файла с информацией { CFile file; if (!file.Open(dlg.GetPathName(),CFile::modeRead|CFile::typeBinary)) { AfxMessageBox("Не удалось открыть файл"); return; } CString str; int count=0; bool bOk=true; if (file.GetLength()>((pImage.GetWidth()*pImage.GetHeight()-5))) { AfxMessageBox("Не досточный размер контейнера для скрытия информации!!!"); goto lClose; } InitStep();//инициализирует переменные для хранения строки и столбца рисунка WriteFirstChar();// скрывает в первый пиксель метку int len=file.GetLength(); WriteLength(&len);//записываем в 4 пикселя размер файла информации do { count=file.Read(buff,sizeof(buff));//считываем из файла с //информацией 100 байтов if (!WriteStrToBmp(buff,count))//функция скрывает данные из буфер в //рисунок { bOk=false; break; } } while (count>0); if (dlgsave.DoModal()==IDOK)// сохранение в файл рисунка со скрытой //информацией { pImage.Save(dlgsave.GetPathName()); } if (!bOk) AfxMessageBox("Произошли ошибки при сохранении информации"); lClose: file.Close();
8. Напишите обработчик события третьей кнопки, который считывает скрытую информацию из графического рисунка и сохраняет на диск: InitStep(); unsigned char ch=ReadByte(); if (ch!=10)//проверка наличии метки {
71
Copyright ОАО «ЦКБ «БИБКОМ» & ООО «Aгентство Kнига-Cервис»
AfxMessageBox("Нет данных в изображении!!!"); return ; } if (!NextStep())//переход к следующему пикселю { AfxMessageBox("Ошибка!!!"); return ; } int cnt=ReadLen();//извлечение количества байтов информации if (cnt==-1) { AfxMessageBox("Ошибка!!!"); return ; } TCHAR szFilters[]= _T("Text (*.txt)|*.txt||"); CFileDialog dlg(false,_T("Text"), _T("*.txt"), OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilters); if (dlg.DoModal()==IDOK)//выбор файла для сохранения информации { CFile file; if (!file.Open(dlg.GetPathName(),CFile::modeCreate| CFile::modeWrite|CFile::typeBinary)) { AfxMessageBox("Не удалось создать файл, для сохранения результата"); return; } int cntwr; for (int i=0;i