Delphi World - Использование буфера записей BDE
Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Использование буфера записей BDE

Оформил: DeeCo

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

Все идет к тому, что BDE в ближайшее время окончательно сдаст позиции компонентам прямого доступа к данным (IBX, dbExpress).
Но все наработанное с использованием BDE сразу не перепишешь и не выбросишь. Компоненты прямого доступа существенно расширяют возможности разработчика.
Недавно понадобилось напрямую работать с буфером записей запроса (TQuery), если бы можно было использовать IBQuery проблем бы с этим не возникло, но буфер записей BDE закрыт и просто до него не достучаться.
Задача стояла следующая: в БД (Interbase) при работе с достаточно большой таблицей появилась необходимость при навигации в ReadOnly DBGrid и нажатию короткой клавиши отмечать записи для отложенной печати (поле SOST := 1).

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

  1. Перевести Query в режим редактирования установить поле в необходимое значение и вызвать метод Query.Post;
  2. C использованием другого Query выполнить Update записи, затем переоткрыть Query.
  3. 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 скорее всего работать не будет!

Все, вышеописанное есть некий частный результат и автор желал бы получить отклик от тех, кого интересует эта тема.
Проект Delphi World © Выпуск 2002 - 2017
Автор проекта: Эксклюзивные курсы программирования