Использование буфера записей BDE
Оформил: 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 скорее
всего работать не будет!
Все, вышеописанное есть некий частный результат и автор желал бы получить
отклик от тех, кого интересует эта тема.
|