3105:Synchronize a DLL to an Open Dataset
KEYWORDS: Synchronize DLL Dataset DbiSetToCursor DbiSetToCursor AREA: Database
Synchronize a DLL to an Open Dataset
This document demonstrates how to use Object Pascal to link a
DLL dynamically, on-the-fly, into an active Database, thus, giving the
Developer the ability to 'Modularize' features. (Whether at Run-Time or
Design-Time)
The technique of linking a DLL Dynamically into an EXE is useful in
many cases. Examples include, an Accounting package which offers several
'plug-in' modules (A/R, A/P, General Ledger, etc.) or a Point of Sale
package with Current Stock, FIFO/LIFO Ordering, Vendor Tracking, etc.,
modules.
This TI will provide a solid example of how to do this with one
dll, 'Editdll.dll', but will give the Developer the essentials leading
to extensive modularation in projects.
Prerequisites:
--------------
Familiarity with the TTable component, DLL usage, BDE API, and
BDE hCursor knowledge. *WIN API for Dynamical loading of any DLL's.
SAMPLE APPLICATION
The following form, EditForm, is based on the COUNTRY table in the
DBDEMO's directory. When the user presses the 'Edit' button or
double-clicks a record (row), a dialog box appears, from 'EditDll.dll',
displaying record specific information. At this point, the DLL has
synchronized itself not only with the dataset (& session) but also with
the current record. This mean's, the user can now modify the same data
EditForm is viewing! So, with no ado, let's delve into the sample code.
(For best results, simply cut and paste into appropriate files)
>Main Form Project<
-------------------
{ MAINDB.DPR }
program maindb;
uses
Forms,
mainform in 'mainform.pas' {DBMainForm};
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TDBMainForm, DBMainForm);
Application.Run;
end.
>>
{ MAINFORM.PAS }
unit mainform;
interface
uses
SysUtils, Windows, Messages, Classes, Graphics, Controls,
StdCtrls, Forms, DBCtrls, DB, DBGrids, DBTables, Grids, ExtCtrls,
BDE;
type
TDBMainForm = class(TForm)
Table1Name: TStringField;
Table1Capital: TStringField;
Table1Continent: TStringField;
Table1Area: TFloatField;
Table1Population: TFloatField;
DBGrid1: TDBGrid;
DBNavigator: TDBNavigator;
Panel1: TPanel;
DataSource1: TDataSource;
Panel2: TPanel;
Table1: TTable;
EditButton: TButton;
procedure FormCreate(Sender: TObject);
procedure EditButtonClick(Sender: TObject);
procedure DBGrid1DblClick(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
var
DBMainForm: TDBMainForm;
implementation
{$R *.DFM}
procedure TDBMainForm.FormCreate(Sender: TObject);
begin
Table1.Open;
end;
// {NOTES: DBHandle is a Handle to the Database & DSHandle is a cursor
// to the record being viewed. Also, if the purpose is to dynamically
// load a DLL at run-time, use the LoadLibrary, GetProcAddress, and
// FreeLibrary API calls in place of the implicit load calls at startup.
// An Example of the APIcalling convention is: }
// Type
// {For GetProcAddress}
// BDEDataSync =
// function(const DBHandle: HDBIDB; const DSHandle: HDBICur): Boolean;
// stdcall;
// {For the trapping of DLL load error's}
// EDLLLoadError = class(Exception);
// var h: hwnd;
// p: BDEDataSync;
// LastError: DWord;
// begin
// UpdateCursorPos;
// Try
// h := loadLibrary('EDITDLL.DLL');
// {NOTE to Delphi 1.0 users: Whereas Win32 LoadLibrary returns a
// NULL if the DLL load was unsuccessful, thus requiring a call to
// GetLastError to find the error, Win16 LoadLibrary returns
// an error value (less than HINSTANCE_ERROR) which can be checked
// in the Win16API SDK as to the reason for the failure.}
// if h = 0 then begin
// LastError := GetLastError;
// Raise EDLLLoadError.create(IntToStr(LastError) +
// ': Unable to load DLL');
// end;
// try
// p := getProcAddress(h, 'EditData');
// if p(DBHandle, Handle) then Resync([]);
// finally
// freeLibrary(h);
// end;
// Except
// On E: EDLLLoadError do
// MessageDLG(E.Message, mtInformation, [mbOk],0);
// end;
// end;
// {or}
function EditData(const DBHandle: HDBIDB; const DSHandle: HDBICur):
Boolean; stdcall external 'EDITDLL.DLL' name 'EditData';
procedure TDBMainForm.EditButtonClick(Sender: TObject);
begin
with Table1 do
begin
UpdateCursorPos;
// Call the EditData Procedure from EditDll.dll.
if EditData(DBHandle, Handle) then Resync([]);
end;
end;
procedure TDBMainForm.DBGrid1DblClick(Sender: TObject);
begin
EditButton.Click;
end;
end.
>>
>EDIT DLL PROJECT<
------------------
{ EDITDLL.DPR }
library editdll;
uses
SysUtils,
Classes,
editform in 'editform.pas' {DBEditForm};
exports
EditData;
begin
end.
>>
{ EDITFORM.PAS }
unit editform;
interface
uses
SysUtils, Windows, Messages, Classes, Graphics, Controls,
StdCtrls, Forms, DBCtrls, DB, DBTables, Mask, ExtCtrls, BDE;
type
TTableClone = class;
TDBEditForm = class(TForm)
ScrollBox: TScrollBox;
Label1: TLabel;
EditName: TDBEdit;
Label2: TLabel;
EditCapital: TDBEdit;
Label3: TLabel;
EditContinent: TDBEdit;
Label4: TLabel;
EditArea: TDBEdit;
Label5: TLabel;
EditPopulation: TDBEdit;
DBNavigator: TDBNavigator;
Panel1: TPanel;
DataSource1: TDataSource;
Panel2: TPanel;
Database1: TDatabase;
OKButton: TButton;
private
TableClone: TTableClone;
end;
{ TTableClone }
TTableClone = class(TTable)
private
SrcHandle: HDBICur;
protected
function CreateHandle: HDBICur; override;
public
procedure OpenClone(ASrcHandle: HDBICur);
end;
function EditData(const DBHandle: HDBIDB; const DSHandle: HDBICur):
Boolean; stdcall;
var
DBEditForm: TDBEditForm;
implementation
{$R *.DFM}
{ Exports }
function EditData(const DBHandle: HDBIDB; const DSHandle: HDBICur):
Boolean; stdcall;
var
DBEditForm: TDBEditForm;
begin
DBEditForm := TDBEditForm.Create(Application);
with DBEditForm do
try
// Set the handle of the Database1 to that of the currently opened database
Database1.Handle := DBHandle;
TableClone := TTableClone.Create(DBEditForm);
try
TableClone.DatabaseName := 'DB1';
DataSource1.DataSet := TableClone;
TableClone.OpenClone(DSHandle);
Result := (ShowModal = mrOK);
if Result then
begin
TableClone.UpdateCursorPos;
DbiSetToCursor(DSHandle, TableClone.Handle);
end;
finally
TableClone.Free;
end;
finally
Free;
end;
end;
{ TTableClone }
procedure TTableClone.OpenClone(ASrcHandle: HDBICur);
begin
SrcHandle := ASrcHandle;
Open;
DbiSetToCursor(Handle, SrcHandle);
Resync([]);
end;
function TTableClone.CreateHandle: HDBICur;
begin
Check(DbiCloneCursor(SrcHandle, False, False, Result));
end;
end.
>>
{ EDITFORM.DFM }
object DBEditForm: TDBEditForm
Left = 201
Top = 118
Width = 354
Height = 289
ActiveControl = Panel1
Caption = 'DBEditForm'
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
Position = poScreenCenter
PixelsPerInch = 96
TextHeight = 13
object Panel1: TPanel
Left = 0
Top = 0
Width = 346
Height = 41
Align = alTop
TabOrder = 0
object DBNavigator: TDBNavigator
Left = 8
Top = 8
Width = 240
Height = 25
DataSource = DataSource1
Ctl3D = False
ParentCtl3D = False
TabOrder = 0
end
object OKButton: TButton
Left = 260
Top = 8
Width = 75
Height = 25
Caption = 'OK'
Default = True
ModalResult = 1
TabOrder = 1
end
end
object Panel2: TPanel
Left = 0
Top = 41
Width = 346
Height = 221
Align = alClient
BevelInner = bvLowered
BorderWidth = 4
Caption = 'Panel2'
TabOrder = 1
object ScrollBox: TScrollBox
Left = 6
Top = 6
Width = 334
Height = 209
HorzScrollBar.Margin = 6
HorzScrollBar.Range = 147
VertScrollBar.Margin = 6
VertScrollBar.Range = 198
Align = alClient
AutoScroll = False
BorderStyle = bsNone
TabOrder = 0
object Label1: TLabel
Left = 6
Top = 6
Width = 28
Height = 13
Caption = 'Name'
FocusControl = EditName
end
object Label2: TLabel
Left = 6
Top = 44
Width = 32
Height = 13
Caption = 'Capital'
FocusControl = EditCapital
end
object Label3: TLabel
Left = 6
Top = 82
Width = 45
Height = 13
Caption = 'Continent'
FocusControl = EditContinent
end
object Label4: TLabel
Left = 6
Top = 120
Width = 22
Height = 13
Caption = 'Area'
FocusControl = EditArea
end
object Label5: TLabel
Left = 6
Top = 158
Width = 50
Height = 13
Caption = 'Population'
FocusControl = EditPopulation
end
object EditName: TDBEdit
Left = 6
Top = 21
Width = 135
Height = 21
DataField = 'Name'
DataSource = DataSource1
MaxLength = 0
TabOrder = 0
end
object EditCapital: TDBEdit
Left = 6
Top = 59
Width = 135
Height = 21
DataField = 'Capital'
DataSource = DataSource1
MaxLength = 0
TabOrder = 1
end
object EditContinent: TDBEdit
Left = 6
Top = 97
Width = 135
Height = 21
DataField = 'Continent'
DataSource = DataSource1
MaxLength = 0
TabOrder = 2
end
object EditArea: TDBEdit
Left = 6
Top = 135
Width = 65
Height = 21
DataField = 'Area'
DataSource = DataSource1
MaxLength = 0
TabOrder = 3
end
object EditPopulation: TDBEdit
Left = 6
Top = 173
Width = 65
Height = 21
DataField = 'Population'
DataSource = DataSource1
MaxLength = 0
TabOrder = 4
end
end
end
object DataSource1: TDataSource
Left = 95
Top = 177
end
object Database1: TDatabase
DatabaseName = 'DB1'
LoginPrompt = False
SessionName = 'Default'
Left = 128
Top = 176
end
end
>>
TI