Delphi для профессионалов



 

Интерфейс прикладного программирования ВDЕ


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

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

Итак, для создания приложения на основе вызовов функций BDE необходимо выполнить следующие операции:

1. Инициализация BDE (функция DbiInit).

2. Открытие объекта базы данных (функция DbiOpenDatabase).

3. Определение рабочего каталога (функция obiSetDirectory), если на предыдущем этапе не задается псевдоним БД.

4. Определение временного каталога (функция DbiSetPrivateoir).

5. Открытие набора данных и создание курсора (функции DbOреnТаblе, DbiQExec и пр.; дескриптор курсора hDBICur).

6. Заполнение структуры cuRProps, содержащей данные о курсоре и наборе данных (функция DbiGetCursorProps).

7. Выделение памяти для буфера записи.

8. Навигация набору данных (функции DbiSetToBegin, DbiSetToEnd, DbiSetToCursor и пр.)

9. Чтение необходимой записи (функции DbiGetRelativeRecord,  DbiGetNextRecord, DbiGetRecord, DbiGetPriorRecord и др.).

10. Чтение или обновление необходимого поля (функции DMGetFieid, DbiPutField).

11. Освобождение всех ресурсов (освобождение буфера записи, закрытие курсора, таблицы, BDE).

При использовании в программе функций из API BDE необходимо включить в секцию uses модуль BDE.

В прикладном программировании задачи, которые требовали бы выполнения всех описанных выше операций, практически не встречаются. Между тем, включение в программный код отдельных функций API BDE оправдано. В качестве примера рассмотрим приложение, которое позволяет очистить таблицу базы данных (рис. 16.4).

Дело в том, что при удалении записи из таблицы локальной СУБД (например Paradox) размер файла таблицы остается прежним, даже если удалить все записи. То есть на самом деле запись не удаляется, а только становится недоступной. При интенсивном использовании базы данных файлы таблиц могут занимать значительные объемы дискового пространства при довольно умеренном числе записей.

Рис. 16.4. Главная форма проекта BDEEmptyTable

Полная очистка таблиц базы данных осуществляется функцией DbiErr.ptyTable из API BDE. Именно она используется в демонстрационном приложении для радикального уменьшения размера таблиц.

Примечание 

Функция DbiEmptyTable используется в методе EmptyTable компонентов доступа к данным (см. гл. 17).

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

 Листинг 16.1. Модуль главной формы приложения BDEEmptyTable 

unit Main; 

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 

StdCtrls, BDE, ExtCtrls, DBCtrls, Grids, DBGrids, Db, DBTables, Buttons;

type

TMainForm = class(TForm)

AliasesList: TComboBox;

TablesList: TComboBox;

EmptyBtn: TBitBtn;

Labell: TLabel;

Label2: TLabel;

procedure FormClose(Sender: TObject; var Action: TCloseAction);

procedure FormShow(Sender: TObject);

procedure AliasesListChange(Sender: TObject);

procedure EmptyBtnClick(Sender: TObject); private

hDB: hDBIDB;

hCursor: hDBICur;

DBDesc: DBDesc;

TblDesc : TBLBaseDesc;

 public

{ Public declarations } 

end;

var

MainForm: TMainForm;

implementation 

{$R *.DFM}

procedure TMainForm.FormShow(Sender: TObject); 

var Rslt: DBIResult;

 begin

AliasesList.Items.Clear;

 TablesList.Items.Clear; hDB := Nil; 

try

DbiInit(Nil); // Инициалы BDE

 BDE DbiOpenDatabaseList(hCursor) ;

 repeat

Rslt:= DbiGetNextRecord(hCursor, dbiNOLOCK, @DBDesc, nil) ;

 if (Rslt 0 DBIERR_EOF) then

AliasesList.Items.Add(StrPas(DBDesc.szName));

 until (rslt <> DBIERR_NONE); DbiCloseCursor(hCursor) ;

 except

on E:EDBEngineError do ShowMessage ('OiiMSKa MHMunajiM3auMM BDE');

end; 

end;

procedure TMainForm.FormClose(Sender: TObject;

 var Action: TCloseAction); 

begin

 try

finally

if hDB <> Nil then DbiCloseDatabase(hDB);// Закрытие базы данных

 DbiExit; // Закрытие сеанса работы с ВОЕ 

end

 end;

procedure TMainForm.AliasesListChange(Sender: TObject); 

begin

 try

if hDB <> Nil

then DbiCloseDatabase(hDB);// Закрытие базы данных 

DbiOpenDatabase // Открытие базы данных 

(

PChar(AliasesList.Text), // Псевдоним базы данных 

Nil, // Тип базы данных 

dbiReadWrite, // Режим редактирования данных

 dbiOpenShared, // Режим разделения данных

 Nil, // Пароль

0, // Число дополнительных параметров 

Nil, // Перечень полей для доп. параметров Nil, 

// Список доп. параметров hDB 

// Дескриптор базы данных 

);

DbiSetPrivateDir('с:\temp');// Определение временного каталога

DbiOpenTableList(hDb, False, False, '*.DB', hCursor);

TablesList.Items.Clear;

TablesList.Clear;

while DbiGetNextRecord(hCursor, dbiNOLOCK, @TblDesc, nil) = dbiErr_None

do TablesList.Items.Add(TblDesc.szName);

 DbiCloseCursor(hCursor);

 except

on E:EDBEngineError do ShowMessage('Ошибка открытия базы данных');

 end;

 end;

procedure TMainForm.EmptyBtnClick(Sender: TObject); 

begin

try

DbiEmptyTable(hDB, Nil, PChar(TablesList.Text), '');

except

on E:EDBEngineError do ShowMessage('Неверно задана таблица'}; 

end; 

end;

end.

При открытии главной формы (метод-обработчик FormShow) функция Dbiinit осуществляет инициализацию BDE. Затем функция DbiOpenDatabaseList создает в памяти временную таблицу, в которую записываются характеристики каждой зарегистрированной базы данных. Для этого применяется структура DBDesc. Курсор hcursor обеспечивает доступ к записям о базах данных.

После этого функция DbiGetNextRecord позволяет осуществить последовательное считывание имен псевдонимов баз данных (для этого в параметре передается указатель на структуру DBDesс) и их запись в список компонента AliasesList типа TComboBox.

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

Функция DbiopenTableList создает временную таблицу в памяти, в которую помещаются данные о таблицах выбранной базы данных в соответствии с форматом структуры TBLBaseDesс. Функция DbiGetNextRecord позволяет передать эту информацию в список компонента TablesList типа TCombоВох.

При щелчке на кнопке EmptyBtn в методе-обработчике EmptyBtndick работает функция DbiEmptyTabie, которая очищает выбранную ранее в компоненте TablesList таблицу.

Теперь рассмотрим пример простейшего приложения, которое может отображать два поля из таблицы COUNTRY. DВ в демонстрационной базе данных DBDEMOS. Эта база данных поставляется в комплекте Delphi. В примере использованы только функции API BDE.

Проект называется DirectBDE и имеет только одну форму, в которой отображаются сведения из таблицы COUNTRY. DB о государствах и их столицах (рис. 16.5). Кнопки в нижней части формы позволяют перемещаться по набору данных.

 Листинг 16.2. Модуль главной формы приложения DirectBDE 

unit Unitl; 

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

 StdCtrls, Buttons, BDE, ExtCtrls;

type

TMainForm = class(TForm)

PriorBtn: TBitBtn;

Panel2: ТPanel;:

NextBtn: TBitBtn;

Label3: ТLabel;

CountryEdit: TEdit;

Label1: TLabel;

CapitalEdit: TEdit;

procedure PriorBtnClick(Sender: TObject);

procedure FormShow(Sender: TObject};

procedure FormClose(Sender: TObject;

 var Action: TCloseAction);

procedure NextBtnClick(Sender: TObject); 

private

hDB: hDBIDB;

hCur: hDBICur;

CursProps: CurProps;

RecBuf: pByte;

FValue: array [0 .. 255] of Char;

IsEmpty: Bool;

procedure OnBDEError;

 public

 end;

var

MainForm: TMainForm;

implementation ($R *.DFM}

procedure TMainForm.OnBDEError;

var ErrInfo: dbiErrlnfo; // Структура, содержащая информацию об ошибках

AStr: String; 

begin

DbiGetErrorlnfo(True, Errlnfo);// Функция возвращает информацию об ошибке c

ase Errlnfo.iError of

9733: AStr := 'Для создания записи недостаточно параметров'; 

10024: AStr := 'Ошибка доступа к данным'; 

10245: AStr := 'База данных занята другим пользователем';

 10038: AStr := 'Значение поля задано неверно';

11871: AStr := 'Несоответствие типов';

11959: AStr := 'В выражении отсутствует оператор GROUP BY';

 else

AStr := 'Ошибочная операция с данными'; 

end;

ShowMessage(AStr) ; 

end;

procedure TMainForm.FormShow(Sender: TObject);

 begin

hDB := Nil; hCur := Nil;

Dbilnit(Nil); // Инициализация системы 

BDE DbiOpenDatabase // Открытие базы данных 

(

'DBDEMOS', // Псевдоним базы данных

Nil, // Тип базы данных

dbiReadWrite, // Режим редактирования данных

dbiOpenShared, // Режим разделения данных

Nil, // Пароль

0, // Число дополнительных параметров

Nil, // Перечень полей для доп. параметров

Nil, // Список доп. параметров

hDB // Дескриптор базы данных

);

DbiSetPrivateDir('с:\temp'); // Определение временного каталога

DbiOpenTable // Открытие таблицы

(

hDB, // Дескриптор базы данных 

PChar('COUNTRY'), // Название таблицы

PChar(szParadox), // Тип таблицы (только для локальных БД)

 Nil, // Название индекса (необязательный)

 Nil, // IndexTagName — только для dBASE 

0, // 0 — использовать первичный индекс 

dbiReadWrite, // Режим редактирования данных

 dbiOpenShared, // Режим разделения данных

 xltField, // Режим трансляции данных

False, // Признак одностороннего перемещения курсора

 Nil, // Дополнительные параметры

 hCur // Дескриптор курсора таблицы );

DbiGetCursorProps // Определение параметров курсора 

hCur, // Дескриптор курсора таблицы

CursProps // Структура параметров курсора

 );

GetMem // Вьщеление памяти под буфер записи 

(

RecBuf,

CursProps.iRecbufSize*SizeOf(Byte) );

DbiSetToBegin(hCur); // Установка курсора в начало набора данных

 DbiGetNextRecord // Перемещение на первую запись

 (

hCur, // Дескриптор курсора таблицы 

dbiNoLock, // Режим ограничения доступа

 RecBuf, // Буфер записи 

Nil // Параметры записи );

DbiGetField // Получение значения поля

 (

hCur, // Дескриптор курсора таблицы 

1, // Номер поля в структуре таблицы 

RecBuf, // Буфер записи

@FValue, // Переменная, в которую передается значение

 IsEmpty // Признак пустой ячейки );

MainForm.CountryEdit.Text := FValue;

 DbiGetField(hCur, 2, RecBuf, @FValue, IsEmpty);

 MainForm.CapitalEdit.Text := FValue;

 end;

procedure TMainForm.FormClose(Sender: TObject; 

var Action: TCloseAction);

 begin try

finally

FreeMem(RecBuf); // Освобождение памяти буфера записи DbiCloseCursor(hCur); // Закрытие курсора 

DbiCloseDatabase(hDB); // Закрытие базы данных

 DbiExit; // Закрытие сеанса работы с ВОЕ

end 

end;

procedure TMainForm.PriorBtnClick(Sender: TObject);

begin

try

if DbiGetPriorRecord(hCur, dbiNoLock, RecBuf, Nil) = DBIERR_BOF

then PriorBtn.Enabled := False

else 

begin

if Not NextBtn.Enabled then NextBtn.Enabled := True;

 DbiGetField{hCur, 1, RecBuf, SFValue, IsEmpty); 

MainForm.CountryEdit.Text := FValue;

 DbiGetField(hCur, 2, RecBuf, @FValue, IsEmpty);

 MainForm.CapitalEdit.Text := FValue; 

end; 

except

OnBDEError; 

end;

 end;

procedure TMainForm.NextBtnClick(Sender: TObject);

 begin 

try

if DbiGetNextRecord(hCur, dbiNoLock, RecBuf, Nil)=DBIERR_EOF

 then NextBtn.Enabled := False

 else

begin

if Not PriorBtn.Enabled then PriorBtn.Enabled := True;

 DbiGetFieldfhCur, 1, RecBuf, @FValue, IsEmpty); 

MainForm.CountryEdit.Text := FValue;

DbiGetField(hCur, 2, RecBuf, @FValue, IsEmpty);

 MainForm.CapitalEdit.Text := FValue;

 end; 

except

OnBDEError; 

end;

 end;

end.

Рис. 16.5. Главная форма проекта DirectBDE

При показе главной формы приложения в процедуре Formshow проводится инициализация BDE, открытие базы данных и таблицы. При этом создаются дескрипторы базы данных hDB и курсора таблицы hour, которые играют в дальнейшей работе приложения важную роль. Если при создании базы данных не указывать псевдоним БД, то обязательно нужно определить рабочий каталог базы данных с помощью функции DbiSetoirectory.

После этого отводится память под буфер записи, в который будут передаваться значения полей текущей строки таблицы. Размер буфера определяется при помощи Структуры CURPropS.

Затем курсор устанавливается на начало набора данных и на первую запись и осуществляется чтение значений двух полей таблицы.

Навигация по набору данных реализована в методах-обработчиках на нажатие кнопок формы. Их действие аналогично за исключением направления перемещения. При щелчке на кнопке выполняется переход на следующую или предыдущую запись, данные из новой записи помещаются в буфер записи RecBuf. Оттуда при помощи функции DMGetFieid осуществляется чтение значений полей. При достижении начала или конца набора данных кнопка деактивируется.

При закрытии формы проводятся операции по освобождению памяти буфера записи, закрытию базы данных и BDE.