Использование буфера записей BDE

{codecitation class=»brush: pascal; gutter: false;» width=»600px»}

Оформил: DeeCo

Автор: Александр Шпихернюк

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

Но все наработанное с использованием BDE сразу не перепишешь и не выбросишь. Компоненты прямого доступа существенно расширяют возможности разработчика.

Недавно понадобилось напрямую работать с буфером записей запроса (TQuery), если бы можно было использовать IBQuery проблем бы с этим не возникло, но буфер записей BDE закрыт и просто до него не достучаться.

Задача стояла следующая: в БД (Interbase) при работе с достаточно большой таблицей появилась необходимость при навигации в ReadOnly DBGrid и нажатию короткой клавиши отмечать записи для отложенной печати (поле SOST := 1).

Данная задача решается несколькими способами:

Перевести Query в режим редактирования установить поле в необходимое значение и вызвать метод Query.Post;

C использованием другого Query выполнить Update записи, затем переоткрыть Query.

C использованием другого Query выполнить Update записи, затем в буфере записей выставить значение нужного поля.

Первый метод не подходит по понятным соображениям, к тому же в нашем случае Query не редактируемый (RequestLive = false).

Второй слишком долгий и ведет к увеличению сетевого трафика.

Третий метод возможно реализовать только с использованием IBX или ClientDataSet, что в этом конкретном случае не приемлемо.

Поэтому для решения задачи третьим методом пришлось искать где BDE хранит полученные от IB сервера данные, вот что из этого получилось:

unit Unit1;

interface

uses

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

StdCtrls, Db, DBTables, Grids, DBGrids, BDE, Menus;

type

TForm1 = class(TForm)

DataSource: TDataSource;

Query: TQuery;

DBGrid: TDBGrid;

Database: TDatabase;

SetFldQ: TQuery;

PopupMenu: TPopupMenu;

Sost1: TMenuItem;

Sost0: TMenuItem;

procedure FormCreate(Sender: TObject);

procedure Sost1Click(Sender: TObject);

procedure Sost0Click(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

procedure SetSost(AValue: Integer);

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

function GetBDERecBuff(ACursor: TQuery): Pointer; {cursor}

var

P, P1: Pointer;

CurNo, RecNo, RecSize: Integer;

begin

//Вызов этого метода синхронизирует положение курсора

//DataSet и BDE

ACursor.UpdateCursorPos;

P := ACursor.Handle;

Inc(PChar(P), $1E);

P := Pointer(P^);

Inc(PChar(P), $7E);

P := Pointer(P^);

Inc(PChar(P), $14);

P := Pointer(P^);

Inc(PChar(P), $36);

P := Pointer(P^);

// Получаем внутренний BDE-шный номер текущей записи

P1 := P;

Inc(PChar(P1), $A);

Inc(PChar(P1), $2);

RecNo := Integer(P1^) — 1;

Inc(PChar(P), $4);

P := Pointer(P^);

// Получаем внутренний BDE-шный номер курсора

P1 := P;

Inc(PChar(P1), $11F);

P1 := Pointer(P1^);

CurNo := Word(P1^);

// Получаем размер записи

P1 := P;

Inc(PChar(P1), $113);

RecSize := Word(P1^);

// Получаем указатель на массив где хранятся указатели на

// буфера всех BDE курсоров

Inc(PChar(P), $4);

P := Pointer(P^);

Inc(PChar(P), $68);

P := Pointer(P^);

// Выбираем из массива нужный нам указатель

Inc(PChar(P), 4 * (CurNo — 1));

P := Pointer(P^);

// Получаем указатель на текущую запись

Inc(PChar(P), RecNo * RecSize);

Result := P;

end;

procedure PutFldToBDEBuf(ACursor: TQuery; AField: TField; pValue: Pointer);

var

P: Pointer;

begin

// Получаем указатель на текущую запись

P := GetBDERecBuff(ACursor);

//складываем нужное значение в буфер BDE

Check(DbiPutField(ACursor.Handle, AField.FieldNo, P, pValue));

//Вызов Resync для пересчета Calc-полей и немедленного отображений изменении на

экране

ACursor.Resync([]);

end;

procedure TForm1.FormCreate(Sender: TObject);

begin

Database.Open;

Query.DataBaseName := Database.DatabaseName;

SetFldQ.DataBaseName := Database.DatabaseName;

DBGrid.PopupMenu := PopupMenu;

Sost1.ShortCut := TextToShortCut(‘Ctrl A’);

Sost0.ShortCut := TextToShortCut(‘Ctrl S’);

Query.SQL.Text := ‘SELECT * FROM AKODIF ORDER BY CODE’;

Query.Open;

SetFldQ.SQL.Text := ‘UPDATE AKODIF SET SOST = :SOST WHERE CODE = :CODE’;

SetFldQ.Prepare;

end;

procedure TForm1.SetSost(AValue: Integer);

begin

SetFldQ.ParamByName(‘SOST’).AsInteger := AValue;

SetFldQ.ParamByName(‘CODE’).AsInteger := Query.FieldByName(‘CODE’).AsInteger;

SetFldQ.ExecSQL;

PutFldToBDEBuf(Query, Query.FieldByName(‘SOST’), @AValue);

end;

procedure TForm1.Sost1Click(Sender: TObject);

begin

SetSost(1);

end;

procedure TForm1.Sost0Click(Sender: TObject);

begin

SetSost(0);

end;

end.

Все описанное выше работает в Delphi 3, Delphi 4, Delphi 5. С BDE 5.01, idapi32.dll от 12.11.1999 размер 589 312. С другими версиями BDE скорее всего работать не будет!

Все, вышеописанное есть некий частный результат и автор желал бы получить отклик от тех, кого интересует эта тема.

{/codecitation}

Добавить комментарий