Содержание
1. Задание
2. Постановка задачи и уточнение
3.Спецификация программ
3.1. Описание программ с точки зрения пользователя
3.1.1. Инструкция по работе с Windows - приложением
3.1.2. Инструкция по работе с DOS - утилитой
3.2. Внутреннее представление программ
(Взаимодействие программ с ОС)
3.2.1. Windows приложение
3.2.1. Приложение под DOS 7.0.
4. Отладка
5. Используемая литература
6. Приложение .
1. Задание
Разработать две программы.
Первая программа - приложение под Windows 9x, созданное в среде VC++ 6.0, написанное с использованием API, которое должна содержать работу с:
1. графическими примитивами,
2. таймером,
3. стандартным диалогами,
4. файлами (создание - открытие, чтение -запись ),
5. диалоговыми окнами
6. реестром (создание - открытие разделов, чтение - запись параметров)
Вторая программа - приложение под DOS 7.0. (поддержка длинных имен) на ассемблере, которое осуществляет обработку файлов созданных первым приложением.
2. Постановка задачи и уточнение
1. В среде VC ++ 6.0 разработать программу-игру. Это Windows 9x - приложение, в главном окне которого отображается каждый раз новый карточный расклад . Согласно правилам игры можно открывать следующую карту только если она совпадает с предыдущей изображением. Если не совпадает, то предыдущая карта закрывается. Всего 8 разновидностей карт. Игра закончена когда открыты все карты. Таймер фиксирует (в секундах ) за сколько времени пройдена игра. Графика прокручивается бегунком.
Главное окно также включает в себя меню: New - создание нового текстового файла для сохранения информации об игре; "Load" - вызов стандартного диалога открытия файла - загрузка данных об игре из текстового файла в панель редактирования Edit box, которая также расположена в главном окне, "Save" - вызов стандартного диалога сохранения файла - сохранение данных об игре в файле в формате :
Матрица открытых-закрытых карт;
Количество секунд, за которые пройдена игра;
Exit - выход из программы с сохранением.
2. Разработать программу (утилиту) на ассемблере (компиляторы TASM или MASM) под DOS 7.0., которое читает данные из файлов (имя файла вводится с командной строки), которые созданны вышеуказанным приложением под Windows 9x и выводит их на экран в следующем формате:
Количество открытых карт: N
Открыты следующие карты:
Красный квадрат
Синий треугольник
Красный квадрат
Зеленый прямоугольник и т.д……
Игра пройдена за : N секунд
3.Спецификация программ
3.1. Описание программ с точки зрения пользователя
3.1.1. Инструкция по работе с Windows - приложением
Описание меню (всех пунктов), всех элементов управления, горячих клавиш, нажатий на клавиши мыши, диалогов (включая стандартные), скриншоты всех окон (включая стандартные диалоги).
Пользователь, запустив программу видит перед собой главное окно стнадартного Windows приложения.
Главное окно содержит:
1) Графика, которая перирисовывается если пользватель нажал на определенной прямомоугольной области мышкой или прокрутил бегунок.
2)Панель редактирования Edit box, в которую выводится содержимое текстового файла с информацией об игрe
3) меню из 4х пунктов:
New
Load
Save
Exit
При нажатии на один из этих пунктов появлются стандартные диалоги:
New начинает новую игру
Load загружает уже созданный файл:
Save записывает информацию в указанный файл:
Если для записи выбирается уже существующий файл, то появляется предупреждение о замене:
Exit завершает работу приложения, предварительно выводя сообщение :
Если нажато Да то выводится стандартый диалог сохранения, после чего программа завершается.
5)Бегунок, реагирует на события: перетаскивание бегунка вручную, нажатие над\под бегунком, нажатие стрелки вниз\вверх.
3.1.2. Инструкция по работе с DOS - утилитой
Запуск утилиты.
Запуск программы (файл viewer.exe) можно выполнить либо из программы Far либо из командной строки MS DOS, введя
viewer.exe my_file.sav
Открываемый текстовый файл вводится справа от имени запускаемой программы в качестве параметра командной строки.
Входные данные
Входные данные программы находятся в файле “my_file.sav”. Файл содержит строку пойманных карт (16 символов (0 если не поймана 1- поймана)), строку открытых\закрытых карт (16 символов (0-если закрыта, 1-открыта)), номера соответствующих каждой карте рисунков (16 символов от 1 до 8) , количество секунд, за кот. пройдена игра (30 символов) , (1 символ + символ перехода на новую строку и символ возврата каретки = 3 символа)., итого длина файла =81
010101100100000101010110110000013670165210274435your time is: 6 seconds
| 16 | 16 | 16 | 30 |3 |
Выходныеданные
=================================================================
** Kol-vo poymannih kart: 02
** Kol-vo otkritih kart: 02
** OTKRITIE KARTI:
ZELENIJ KVADRAT
ZELENIJ KVADRAT
** IGRAPROYDENAZA (SEC): 9
===============================================
3.2. Внутреннее представление программ
(Взаимодействие программ с ОС)
3.2.1. Windows приложение
3.2.1.1. Общий алгоритм работы программы
Данные об игре хранятся в 8 массивах: х1,х0,у0,у1,opened,n,names,catched
Рабочее поле поделено на 16 прямоугольных областей, если мышка попадает и щелкает 1 раз в одну из этих областей, то карта открыта, 2 раза - карта снова закрывается.
x0 , y0 –координаты верхних левых углов карт
x1, y1 – координаты нижних правых углов карт
Если карта открыта то в предварительно обнуленный массив opened записывается единичка.
Чтобы расклад карт каждый раз был случайным есть массив n, в каждую ячейку которого случайным образом записывается число от 0 до 4 (каждое из чисел четыре раза записывается в случайный номер ячейки, если эта ячейка уже что-то содержит, то снова генерируется случ. число пока не заполнится весь массив n). В массиве names (массив строк) хранятся имена загружаемых изображений. Номера ячеек, в которых хранятся изображения, соответствуют числам, которые хранятся в массиве n, поэтому чтобы посмотреть, совпадают ли изображения , мы обращаемся к изображению так: names[n[i]].
Согласно правилам игры можно открывать следующую карту только если она совпадает с предыдущей изображением. Если не совпадает, то предыдущая карта закрывается. Этот механизм проверяется следующим условием: происходит обращение по индексу двух сравниваемых карт, и если в массиве opened по этим индексам стоят единички, а в массиве n по этим индексам стоят одинаковые числа, то в массив catched в эти индексы записывается единичка, изображения становятся «пойманными» и не реагируют на нажатие мыши.
3.2.1.2. Описание окон, сообщений, и относящихся к ним данных и функций (сверху - вниз, начиная с наиболее крупных, заканчивая вспомогательными).
Программа состоит из главной функции WinMain, в которую входит обработчик событий switch, функции WindowFunc, DrawCards. Так же есть дополнительные функции FillCoords, Catch. Обработчик событий switch содержит следующие ветвления:
- WM_DESTROY: /* завершение программы */
- WM_VSCROLL:/*прокрутка, содержит свой обработчик событий*/
-SB_LINEDOWN://была нажата стрелка вниз
- SB_LINEUP: // вверх
- SB_PAGEUP: //было нажато над скролом
- SB_PAGEDOWN: //было нажато под скролом
- SB_THUMBTRACK: //бегунок был перетащен вручную
- WM_LBUTTONDOWN:/*нажата левая кнопка мышки*/
- WM_COMMAND:/*выбор пункта меню*/
- WM_TIMER: /* сообщение от таймера*/
- WM_PAINT: /*отрисовка клиентской области, вызывает функцию DrawCards*/
- WM_CREATE:/*была вызвана функция создания окна, здесь созданы элементы управления – поле редактора Edit box и Check Box */
Функция DrawCards:
Объявление
void DrawCards(HWND hwnd,int x,int y,char *msg, int i, int j,float sdvig);
Вызов
DrawCards(hwnd,100,10,"Откройте любую карту .",i,j,-cyChar*iVscrollInc);
hwnd- дескриптор окна;
2-й, 3-й параметры – координаты для вывода сообщения;
4-й – текст сообщения;
5-й, 6-й – индексы для доступа к каждой отдельной карте;
7-й – праметр сдвига перерисовки при прокрутке
Функция DrawCards рисует прямоугольники с помощью функции Rectangle в цикле:
for (i=0;i Rectangle(hDC,x0[i],y0[i]=(y0[i]+sdvig*100),x1[i],y1[i]=(y1[i]+sdvig*100));
Sdvig- это расстояние, на которое карты должны перерисоваться с учетом скроллинга, функция DrawCards вызывается во время скроллинга, и в кач-ве параметра сдвига ей передается выражение: -cyChar*iVscrollInc где cyChar, iVscrollInc – числовые коэффициенты, положительные\отрицательные в зависимости от напрвления прокрутки.
Перерисовка при нажатии мышки происходит в ветвлении WM_LBUTTONDOWN с помощью функции BitBlt.
ФункцияFillCoords:
Текст функции
void FillCoords(int i, int X0, int Y0, int X1, int Y1)
{
x0[i] = X0;
y0[i] = Y0;
x1[i] = X1;
y1[i] = Y1;
return;
}
Заполняет массивы х0, у0, х1, у1 необходимыми координатами;
Вызов: FillCoords(0,113,157,247,322);
Функция Catch:
void Catch(int i, int j)
{
catched[i] = catched[j] = true;
}
Выполняется, если две карты оказываюся открытыми и с одинаковыми изображениями.
Сообщения программы
Программа посылает сообщения с помощью функции SendMessage в следующих местах:
1)При загрузке информации из текстового файла в поле редактора:
SendMessage(EdtHwnd, EM_REPLACESEL, FALSE, (LPARAM)(LPCTSTR)pBuffer);
EdtHwnd//дескриптор редактора- получателя
EM_REPLACESEL// позволяет к концу одной записи прибавлять другую, выводить по несколько файлов
WPARAM wParam, // первый параметр сообщения , равен нулю
pBuffer // буфер для текста
2)При завершении работы приложения, этим сообщением вызывается стандартный диалог сохранения файла
SendMessage(hwnd,WM_COMMAND,IDM_SAVE2,0);
hwnd//дескриптор окна- получателя
WM_COMMAND// посылаемое сообщение
3.2.1. Приложение под DOS 7.0.
3.2.1.1. Общий алгоритм работы программы
При загрузке программы (не важно, СОМ или ЕХЕ) перед образом программы в памяти находится область PSP (префикс программного сегмента) размером 256 байт, последние 128 байт которой называется DTA и является файловым буфером по умолчанию для устаревших файловых функций , так же она используется для хранения строки параметров командной строки. Содержимое командной строки, следующее за именем программы при ее вызове помещается в PSP, DTA со смещением 80h и максимальным размером 128. 1ый байт содержит кол-во символов параметров комстроки (длину), со второго (смещение 81h) начинаются символы параметров. Последний символ – всегда 0dh. Программа сначала копирует имя открываемого файла из PSP в сегмент данных ,а потом настраивает регистр ds на сегмент данных, т.к при старте программы загрузчик устанавливает регистры ds и es равные сегментному адресу PSP.
mov ax,@data;
mov es,ax
Ассемблер не может определить адрес data, он пределится лишь когда объектная программа будет скомпанована и загружена для выполнения. Поскольку згрузчик может расположить программу в любом месте памяти, ассемблер оставляет данный адрес открытым, компоновщик должен подставить вместо @data действительный адрес.
mov di,81h ;начало комстроки
mov al," "
mov cx,128 ;макс длина
cld ; сбрасывает флаг DF, это необходимо, чтобы на каждом шаге
; копирования происходило увеличение
; (а не уменьшение) содержимого DI на единицу.
repe scasb ;пропускает пробелы, пока не найдет символ «не пробел»,
; причем даже когда будет найден такой символ
;di увеличится на 1
dec di ;вернет значение di, так что он будет являтся смещением 1-го
;отличного от пробела символа ES:[DI]
push di
pop si ;его адрес- в si siß---diß---ES
mov ax,@data;вручную настраиваем es на сегмент данных
mov es,ax ;ax в es т.к нет команд для непосредственной пересылки
; данных из памяти в регистр es
mov cl,ds:[80h]; кол-во символов в комстроке
dec cl ; избавляемся от Enter’а , введенного пользователем
lea di,FILE ;
rep movsb ; скопировать комстроку в FILE по адресу di
push es ;было esß---@data, настроим ds на сегмент данных
pop ds ; теперь dsß---es а значит dsß-@data
Далее программа открывает файл с помощью функции 3Dh , режим доступа –только чтение. Далее создается дескриптор файла. После этого файл читается функцией 3fh, дескриптор файла заносится в регистр bx, в регистр сх- число байтов, которое нужно прочитать, в регистр dx адрес области ввода. ( сх совпадает с размером буфера для чтения). Правильно выполненная операция считывает запись в память, сбрасывает флаг CF и устанавливает в регистре ах число дейсвительно прочитанных байтов. Нулевое значение в ах означает попытку чтения после конца файла. Мы каждый раз сравниваем значение в регистрах сх и ах и , если значения совпадают, закрываем файл с помощью функции 3Еh , предварительно занеся в регистр bx дескриптор файла.
Далее мы обрабатываем массив с файла. В регистре сх записываем, сколько нам необходимо прочитать, обращение к символу в строке происходит через регистр обработки цепочечных операций si: BUFER[si]. Мы сравниваем символ и если он совпадает с нужным нам символом мы увеличиваем счетчик и продвигаемся по строке дальше, увеличивая si. Поскольку значение счетчика –число, то для вывода на экран его необходимо преобразовать в строку (ASCII-формат).В коде ASCII на каждый символ требуется 1 байт. Например, число 25 в ASCII-коде имеет внутреннее представление 3235. Значит, чтобы преобразовать двоичное число, его нужно делить на 10 пока результат не станет меньше 10. Остатки, которые лежат в границах от 0 до 9 образуют число в ASCII-формате. Последнее частное и все остатки от деления (снизу вверх) должны записываться в память с тройками:
xor ax,ax; обнуляем частное
mov al,bl; заносим наше число
div ten
or ah, 30h ;дописываем 3 в 4 старших бита, в AH ascii-код числа
;старший байт при выводе на экран будет второй цифрой
; (формат выводимого числа AL:AH)
mov count_otrit[1],ah
or al, 30h
mov count_otrit[0],al
Поскольку у нас числа только до 16 то нет необходимости проверять доп условия.
4. Отладка
Описание проблем и их решений при разработке пограмм.
При написании Windows-приложения возникали ошибки в фрагменте кода, где выбирался пункт меню Save:
filename=new char[100];
Память для имени сохраняемого файла выделялась, но при попытки освободить память функцией delete[] filename; программа завершала свою работу.
1 Способ – выделить память с помощью функции CString
CString filename("");
2 Способ – заменить char *filename; на char filename[280];
заменить filename=new char[100]; на
*filename=0;
lstrcpy(filename,"");
5. Используемая литература
1. Захарова З.Х. «Операционные системы. Учебное пособие»
2. Питер Абель «АССЕМБЛЕР И ПРОГРАММИРОВАНИЕ ДЛЯ IBM PC»
6. Приложение .
Файлы проекта.
1) Windows-приложение:
ФайлС++
#include
#include "resource.h"
#include
#include
#include "stdio.h"
#include "stdlib.h"
#include
/*массивы координат верхней левой и нижней правой точек прямоуголиников*/
int x0[16],
y0[16],
x1[16],
y1[16],
n[16]; // хранит код загружаемой картинки
char names[4][80]; //массив для хранения имен картинок которые будут рисоваться на картах
int opened[16]; /*массив открытых карт */
bool catched[16]; /*массив «пойманных» карт*/
// объявление сторк для названий картинок, которые отображаются на картах
#define ex0 "example2.bmp"
#define ex1 "example3.bmp"
#define ex2 "example4.bmp"
#define ex3 "example5.bmp"
#define cl "example1.bmp" //обратная сторона карты
int result = 0 ; //флаг
LRESULT CALLBACK WindowFunc(HWND,UINT,WPARAM,LPARAM);
void DrawCards(HWND hwnd,int x,int y,char *msg, int i, int j,float sdvig);
/* дескриптор приложения */
HINSTANCE hInstance;
/* редактор (поле ввода)*/
HWND EdtHwnd;
/* структура - диалог для открытия и сохранения файла */
OPENFILENAME ofn;
/* для операций с файлами */
HANDLE hSrc,hDest;
// буфер для хранения данных из файла
TCHAR pBuffer[100];
TCHAR bBuffer[70];
char szWinName[] ="MyWin"; /* имя класса окна */
void FillCoords(int i, int X0, int Y0, int X1, int Y1)
{
x0[i] = X0;
y0[i] = Y0;
x1[i] = X1;
y1[i] = Y1;
return;
}
void Catch(int i, int j)
{
catched[i] = catched[j] = true;
}
/* главная функция */
int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst,
LPSTR lpszArgs, int nWinMode )
{
HWND hwnd;
MSG msg;
WNDCLASSEX wcl;
int i, j, r0, r1;
/* определение класса окна */
wcl.hInstance=hThisInst; /* дескриптор данного экземпляра */
wcl.lpszClassName=szWinName; /* имя класса окна */
wcl.lpfnWndProc=WindowFunc; /* функция окна */
wcl.>