Technical Information Database TI1339D.txt Synchronize a DLL to an Open Dataset Category :Database Programming Platform :All Product :Delphi 2.0 Description: 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 >> Reference: 7/16/98 4:34:09 PM
Last Modified: 01-SEP-99