Глава 12:Расширение возможностей FoxPro средствами языка Си (часть2)

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

Даже экранный ввод/вывод находится под строгим наблюдением FoxPro. Для получения требуемой скорости работы приводимого примера библиотеки мне пришлось использовать функции BIOSа, позволившие мне обойти наблюдение FoxPro за процессами ввода/вывода на экран, и затем выполнить легальный (командой RESTORE SCREEN) вывод на экран, чтобы FoxPro принял изменения. Все это будет объяснено позже.


Хотя, если вы привыкли писать обычные Си-программы, упомянутые выше требования могут создать некоторые неудобства, LCK дает множество новых возможностей. Любой программист, пишущий на Си, позавидует оконной среде FoxPro, а LCK обеспечивает к ней легкий доступ. Аналогично поддерживаются все функции обработки базы данных, поиск по ключу требует всего лишь вызова функции _DBSeek. Организация работы в многопользовательском режиме с вызовом, например, таких функций как _DBLock и _DBUnlock также не представляет сложности.

Пример программы

Создание библиотеки специальных видеоэффектов я начну с простой функции. Функция curtain() открывает экран подобно театральному занавесу, с выводом изображения за ним. Функция принимает два параметра: имя .MEM-файла, содержащее сохраненное изображение экрана, и имя самой экранной переменной. Для создания такого файла выполните следующие действия:

1) Создайте первый экран SAVE SCREEN TO SCR1



2) Создайте еще один экран SAVE SCREEN TO SCR2

3) Сохраните оба экрана в .MEM-файле SAVE ALL LIKE SCR TO MYFILE

Чтобы вызвать функцию библиотеки дайте следующие команды:

SET LIBRARY TO SCREENFX =CURTAIN("MYFILE", "SCR2")

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

Отладка

Возможно, первое что вам бросится в глаза когда вы станете изучать исходный текст программы это то, что она почти наполовину состоит из отладочного кода. Это не замедляет ее исполнение. В оптимизирующем компиляторе, таком как Watcom, при установке DEBUGLEVEL NONE все макроопределения раскрываются до превращения их в константы. Компилятор игнорирует код, который никогда не будет исполнен, так что вся отладочная информация не войдет в исполняемый код. При желании использовать отладочный код настройте DEBUGLEVEL на любую комбинацию уровней отладки. Например, макроопределение:

#define DEBUGLEVEL SUB+LOOP

выведет вам все диалоговые окна в подпрограммах и их циклах, но опустит их в главной функции. Вы можете при необходимости добавить свои уровни, например, для конкретной подпрограммы если вы добавляете ее в библиотеку. Убедитесь только, что вы увеличили значение строки замещения для нового макроопределения в два раза. Это сохранит битовую структуру DEBUGLEVEL. К примеру, вы добавляете в библиотеку функцию explode(), раскрывающую экран от центра наружу, и хотите получить отладочные сообщение только для нее. Вам нужно указать:

#define EXPLODE 16

#define DEBUGLEVEL EXPLODE+LOOP+VARVAL

и вы получите полное управление отладкой внутри вашей новой процедуры. Макроопределение DEBUGMSG принимает два параметра. Первый имеет битовую структуру и указывает какие уровни отладки необходимо подключить, второй параметр - это текст, выводимый в диалоговом окне. Для функции explode()^N вы можете написать следующее сообщение отладки:

DEBUGMSG(EXPLODE+LOOP, "No, I said a BUD light!")

Функции поддержки

В библиотеку включены несколько функций, вызываемых из curtain(). Это setcursor(), writechar(), loadscreen() и strtoupper(). Эти функции вам понадобятся каждый раз, когда вы будете писать программы, создающие те или иные видеоэффекты. Первые две из них заносят требуемые значения в соответствующие регистры и вызывают прерывание int 10h. Описание сотен полезных вызовов BIOSа вы можете найти в любом справочном руководстве по DOS и BIOS.

Последняя функция strtoupper() последовательно перебирает строку, которую вы передаете ей в качестве параметра, и проверяет каждый символ для выяснения того, каким регистром он был набран. Если буква находится в нижнем регистре, уменьшением значения ASCII кода на 32 ее превращают в заглавную.

Перечисленные функции очень просты, тогда как loadscreen() требует большего внимания. Она использует многие внутренние функции FoxPro для выполнения файлового ввода/вывода. Имейте ввиду, что вместо стандартной структуры FILE в качестве идентификатора (handle) файла вам следует использовать структуру FCHAIN FoxPro. Функционально все функции соответствуют стандартным, описанным в заголовочном файле <fcntl.h>. Следует отметить, однако, что я также использую внутренние функции FoxPro обработки строк. _StrCmp() и _StrCpy() тоже функционально соответствуют функциям из файла <string.h>.

Единственная функция, используемая в loadscreen() и требующая внимания, - это _UserError(). Она создает окно с текстом сообщения, полученным как параметр, и прерывает работу библиотечной функции. _UserError() работает очень чисто и сохраняет пользовательский интерфейс FoxPro.

В функции loadscreen() я сначала открываю файл и считываю заголовок первой переменной памяти. Как указано в комментариях к программе, имя переменной - это строка с нулевым символом на конце (ASCIIZ-строка), занимающая первые 11 байт заголовка. Затем я вхожу в цикл, условием окончания которого является нахождение искомой переменной. Если переменная не найдена я вычисляю размер блока данных, с использованием значений 12-го и 13-го байтов, и выполняю смещение на полученную величину с помощью функции _FSeek(). После этого проверяю достиг ли я конца файла. Если да, то указанный экран в файле отсутствует, и программа завершается с выдачей сообщения об ошибке. Если нет, я считываю следующий заголовок и снова вхожу в цикл. Если искомое имя переменной найдено, я немедленно переношу информацию об экране в буфер, указатель на который я передал loadscreen(), и закрываю файл.

Структура заголовка .MEM-файла описана в комментариях к программе. Я писал функцию loadscreen() с прицелом на будущие модификации. Вычисление размера блока может показаться чрезмерным, так как известно, что экраны 80*25 занимают ровно 4000 байт, но никогда нельзя сказать, что принесет нам будущее.

Функция Curtain()

Первая проблема, возникающая сразу после вызова curtain(), - это аргументы функции. Они не похожи на привычные и милые сердцу выражения типа char *. Мы можем получить их с помощью полей структуры Value, но прежде должны выделить им память и заблокировать на время использования. Мы учитываем дополнительные четыре символа при выделении памяти для имени файла, чтобы позже добавить к нему расширение ".MEM". Мы блокируем дескриптор памяти (mhandle), преобразуем его в указатель на память, выделенную данному дескриптору, заканчиваем строку символом ASCII 0, после чего можем использовать ее как обычную строку символов с одним только исключением: при любой обработке строк мы обязаны использовать внутренние функции FoxPro. Вот почему нам следует добавить ".MEM" в имя файла не функцией strcpy(), а _StrCpy(). Имя экрана должно быть переведено в заглавные буквы, потому что оно именно в таком виде хранится в файле и позже нам придется использовать это имя в функции _StrCmp().

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

Часть программы, обеспечивающая вывод изображения на экран, - последнее с чем нам придется иметь дело, и она довольно проста. Два цикла контролируют вертикальный и горизонтальный вывод, причем счетчик цикла, ответственный за горизонтальное перемещение, начинает отсчет со значения 40, т. е. с середины экрана. Массив, описывающий экран, содержит два байта для каждого знакоместа: символ и атрибут. Байты хранятся парами, начиная с левого верхнего символа экрана, и располагаются построчно до нижнего правого угла. Указатель массива определяется по величине счетчика, и после определения значения указателя вызываются функции BIOSа. Такой прием обеспечивает зрительно более приятное заполнение экрана по сравнению с простым выводом изображения на экран.

Вызов команды RESTORE SCREEN в конце функции curtain() требует объяснения. Как упоминалось ранее FoxPro очень внимательно следит за использованием памяти, файлов и экрана. Особенно следует отметить то, что FoxPro имеет специальную область, хранящую копию видеопамяти. Так как нам удалось обойти систему защиты посредством вызовов функций BIOSа для вывода на экран, FoxPro не имеет представления о нашем вторжении, и если вы переместите окно по экрану, то оно по пути сотрет все, что было выведено, и на месте вашего изображения появится старый экран. Так что после того как функция <string.h> сделала свое дело, она вызывает команду FoxPro RESTORE SCREEN для повторного вывода того же изображения и приведения в соответствие содержимого экранной памяти FoxPro и изображения на экране.

Чтобы написать код, приведенный в Листинге 12-1, мне понадобилось много, много времени. Участок программы, ответственный за вывод на экран, был наименее сложной работой. Написанию подобного типа программ обучают на любых курсах по Си, хотя в данном случае я использовал свой собственный алгоритм. Самым трудным было заставить написанное мной работать в LCK. Даже после того как код заработал, потребовалась помощь Дэйва Уоткинса из Fox Software для сглаживания некоторых шероховатостей. Вашу первую API - программу вы будете писать намного дольше чем двадцатую.см. Листинг 12-2

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

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

Код

Листинг 12-3 содержит код программы-примера. Каждый блок кода поясняется комментариями.

Заключение

Эта глава, написанная мом сыном Джоном, работающим в Microsoft, демонстрирует пути расширения возможностей FoxPro. Вы действительно можете получить все, что захотите, если используете LCK. В некотором смысле, написание собственных процедур на Си - это вершина программирования на FoxPro. Если у пакета отсутствуют какие-либо нужные вам возможности, вы можете добавить их.