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

 FoxPro - это мощный язык и его средствами вы можете решить почти любую задачу. Почти, но не любую. С помощью средств, предоставляемых пакетом Library Construction Kit (LCK), FoxPro может делать абсолютно все, что доступно для любых других языков программирования.

Раньше, с возможностью делать на компьютере абсолютно все ассоциировалось знание ассемблера. Однако теперь, благодаря Кернигану и Ричи, обычно хватает средств, предоставляемых Си, и проблемы решаются меньшей кровью. LCK дает возможность использовать написанные на Си (или, если вы настаиваете, на ассемблере) функции из программ на FoxPro так, как если бы они являлись неотъемлимой частью языка пакета.


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

Как это работает

LCK позволяет вам создавать библиотечные файлы, т. е. файлы с расширением .PLB. Для подключения, скажем, библиотеки SCREENFX.PLB, в вашей программе должна присутствовать команда:

SET LIBRARY TO SCREENFX



После чего вы можете вызывать любую функцию в библиотеке SCREENFX.PLB так, как если бы она была собственной функцией FoxPro. В том числе и для выполнения того, что FoxPro сделать "не может" - например, работы в графическом режиме. FoxGraph весьма неплох и может полностью удовлетворять ваши потребности в графиках и круговых диаграммах, но как насчет того, чтобы вывести на дисплей графические изображения из базы данных? Как насчет программы на FoxPro, снабженной графическим интерфейсом? Как насчет анимации? Что вы думаете по поводу иных чем у FoxPro форматов данных? Использование собственных или разработанных другими функций, написанных на Си, решает все эти проблемы.

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

Всем нам приходилось видеть программы, похожие на слайд-шоу. Сменяющие друг друга изображения спускаются сверху, выталкивая ранее присутствовавшие на экране. Или экран раскрывается подобно занавесу, открывая расположенное под ним изображение. Я писал подобные функции на FoxPro, но они работают слишком медленно для получения желаемого эффекта. FoxPro демонстрирует высокую скорость, когда речь идет о поиске в базе данных в миллион записей, но также неповоротлив как любая другая СУБД, если дело касается экранного ввода/вывода. Так что вывод изображений на экран кажется прекрасным кандидатом для нашего примера по созданию библиотеки средствами Си.

Кажется дело простое, не так ли? Вы просто пишете функцию, принимающую переменную с данными об изображении на экране как параметр (переменная типа S, полученная командой SAVE SCREEN TO <имя переменной>, - прим. пер.), потом пару вложенных циклов, выводящих на экран символы в нужном порядке, так? Нет. Нужно учесть еще кое-что, и кроме того LCK имеет некоторые особенности, требующие внимания.

Несколько простых требований...

У Стива Мартина есть реприза об угонщике самолетов, желавшем иметь миллион долларов, машину, чтобы скрыться от преследования, и власть убрать букву `М' из английского языка. Вот и LCK предъявляет к разработчику определенные требования, которые, будем надеяться, не такая экзотика как спортивный автомобиль...

Наиболее серьезным требованием является обязательное использование компилятора Watcom C при написании библиотек. Должен признать, что меня избаловали удобства, предлагаемые средой разработки типа FoxPro: контекстно-зависимая помощь, компиляция, выполняемая по единственному нажатию клавиши, и мне вовсе не улыбалась перспектива возврата к заданию параметров компиляции из командной строки и к созданию программы без привычных средств интегрированной среды разработки (IDE). При работе с Watcom C я потратил немало времени только на то, чтобы установить переменные окружения, проложить пути доступа и организовать каталоги так, чтобы редактор связей был в состоянии отыскать все необходимое. Но в Fox Software используют как раз Watcom C для разработки самого FoxPro, потому что это очень быстрый компилятор, создающий к тому же такой компактный код.

Отладка

Другая проблема, возникающая при компиляции библиотек для FoxPro, - это отладка. Я обычно работаю с отладчиком Turbo Debugger (TD) фирмы Borland. В руководстве по LCK подробно описано как транслировать файл карты памяти формата Watcom (.WAT-файл) в .MAP-файл формата Microsoft, как преобразовать .MAP-файл в таблицу символических имен отладчика (.TDS-файл), как загрузить полученную таблицу в TD, что нужно сделать, чтобы оставить отладчик резидентным в памяти, каким образом можно настроить таблицу символических имен при достижении точки останова (точки останова должны быть скомпилированы как часть кода программы), также объяснено, что нужно увеличить содержимое счетчика команд (регистр IP) для того, чтобы пройти точку останова, но, по правде говоря, все это уже слишком для большинства несложных ошибок.

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

Дополнительные усилия

LCK требует некоторых усилий при написании любой функции. На самом деле это не так уж страшно. Вам необходимо включить файл заголовка:

#include (pro_ext.h)

Затем в программе вам нужно сообщить FoxPro следующее:

1) какое имя функции вы собираетесь использовать;

2) какая функция Си ей соответствует;

3) используются ли какие-либо параметры;

4) как они передаются.

Все это указывается в структурах FoxInfo и FoxTable. Вам необходимо заполнить их информацией о собственных функциях, как это указано ниже:

FoxInfo myFoxinfo[] = { {"MYFUNC", inverse, 1, "N"}, {"YOURFUNC",truncate,2,"C,.I"} };

FoxTable_FoxTable = {(FoxTable FAR *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo),MyFoxInfo };

Эти структуры определяют две функции. Одна называется inverse, и будет вызываться в FoxPro как MYFUNC: она принимает один параметр (число) и возвращает его с противоположным знаком. Хотя необходимо использовать различные функции для получения возвращаемого значения, в данном определении вам не нужно указывать его тип. Об этом мы еще поговорим позже.

Вторая функция добавляет команду FoxPro YOURFUNC, вызывающую функцию Си truncate. Она принимает два параметра: символьный (в том числе символьные строки) и необязательный целочисленный параметр.

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

Структура FoxTable для любой вашей библиотеки будет неизменной в случае если структуре FoxInfo присвоено имя myFoxInfo. Если вы дадите этой структуре другое имя, например, yourFoxInfo, вам придется заменить все myFoxInfo на yourFoxInfo.

Функции Setup и Cleanup в вашей API-программе

Любой участок кода может быть отмечен как "Вызвать при загрузке" (Call on load) или "Вызвать при выгрузке" (Call on unload), что указывает на необходимость автоматического выполнения этой части программы при загрузке или выгрузке библиотеки. Это позволяет использовать установочные и закрывающие процедуры, такие как перевод в графический режим или загрузка системных переменных, необходимых для функций библиотеки. Объявление таких участков производится заменой третьего параметра в структуре FoxInfo на callonload или callonunload (вместо указания числа принимаемых функцией параметров). Установочный/закрывающий код не может принимать параметры, поэтому это место в структуре в любом случае не будет использовано. Ниже приводится пример.

FoxInfo myFoxInfo[]={  {"SETUP", setup, CALLONLOAD, ""}  {"VIEW", viewpic, 1, "M"}  {"CLEANUP", cleanup, CALLONUNLOAD, ""} };

Передача параметров

Все ваши функции, принимающие параметры, должны получить FAR- указатель на структуру (ParamBlk FAR *parm) вместо списка параметров. Структура блока параметров имеет два поля: число параметров и массив объединений (unions) типа Parameter.

В объединение Parameter входят структура Value и структура Locator. Структура Locator используется в том случае, когда параметром является массив или поле базы данных. Во всех остальных случаях используется структура Value.

Обработка параметров, передаваемых функции, выполняется аналогично обработке аргументов командной строки DOS (argc, argv). Приводимый пример библиотеки показывает, что имя файла, передаваемое как параметр, обрабатывается именно так.

Возврат значений

При возврате значений вы не можете использовать стандартную функцию Си return() и описать ее как возвращающую какое-либо значение. Все функции, возвращающие значение в FoxPro, определяются как void FAR. Внутренние функции описываются и работают обычным образом.

При возврате значений в FoxPro вы используете одну из функций возврата, в зависимости от типа возвращаемого значения. Таких функций шесть: _RetChar, _RetInt, _RetFloat, _RetDateStr, _RetLogical и _RetVal. _RetVal имеет наибольшую гибкость и позволяет возвращать данные любого типа, но помимо данных возвращает также структуру Value, так что вам придется дополнительно обрабатывать и ее.