home *** CD-ROM | disk | FTP | other *** search
/ Delphi Anthology / aDELPHI.iso / Runimage / Delphi50 / Source / Vcl / ibevents.pas < prev    next >
Pascal/Delphi Source File  |  1999-08-11  |  10KB  |  369 lines

  1. {********************************************************}
  2. {                                                        }
  3. {       Borland Delphi Visual Component Library          } 
  4. {       InterBase Express EventAlerter components        }
  5. {                                                        }
  6. {       Copyright (c) 1998-1999 Inprise Corporation      }
  7. {                                                        }
  8. {       Adapted from code written by:                    }
  9. {         James Thorpe                                   }
  10. {         CSA Australasia                                }
  11. {         Compuserve: 100035,2064                        }
  12. {         Internet:   csa@csaa.com.au                    }
  13. {                                                        }
  14. {********************************************************}
  15.  
  16. unit IBEvents;
  17.  
  18. interface
  19.  
  20. uses
  21.   SysUtils, Windows, Messages, Classes, Graphics, Controls,
  22.   Forms, Dialogs, DB, IBHeader, IBExternals, IB, IBDatabase;
  23.  
  24. const
  25.   MaxEvents = 15;
  26.   EventLength = 64;
  27.  
  28. type
  29.  
  30.   TEventAlert = procedure( Sender: TObject; EventName: string; EventCount: longint;
  31.                            var CancelAlerts: Boolean) of object;
  32.  
  33.   TEventBuffer = array[ 0..MaxEvents-1, 0..EventLength-1] of char;
  34.  
  35.   TIBEvents = class(TComponent)
  36.   private
  37.     FIBLoaded: Boolean;
  38.     FEvents: TStrings;
  39.     FOnEventAlert: TEventAlert;
  40.     FQueued: Boolean;
  41.     FRegistered: Boolean;
  42.     Buffer: TEventBuffer;
  43.     Changing: Boolean;
  44.     CS: TRTLCriticalSection;
  45.     EventBuffer: PChar;
  46.     EventBufferLen: integer;
  47.     EventID: ISC_LONG;
  48.     ProcessingEvents: Boolean;
  49.     RegisteredState: Boolean;
  50.     ResultBuffer: PChar;
  51.     FDatabase: TIBDatabase;
  52.     procedure SetDatabase( value: TIBDatabase);
  53.     procedure ValidateDatabase( Database: TIBDatabase);
  54.     procedure DoQueueEvents;
  55.     procedure EventChange( sender: TObject);
  56.     procedure UpdateResultBuffer( length: short; updated: PChar);
  57.   protected
  58.     procedure HandleEvent;
  59.     procedure Loaded; override;
  60.     procedure Notification( AComponent: TComponent; Operation: TOperation); override;
  61.     procedure SetEvents( value: TStrings);
  62.     procedure SetRegistered( value: boolean);
  63.     function  GetNativeHandle: TISC_DB_HANDLE;
  64.  
  65.   public
  66.     constructor Create( AOwner: TComponent); override;
  67.     destructor Destroy; override;
  68.     procedure CancelEvents;
  69.     procedure QueueEvents;
  70.     procedure RegisterEvents;
  71.     procedure UnRegisterEvents;
  72.     property  Queued: Boolean read FQueued;
  73.   published
  74.     property  Database: TIBDatabase read FDatabase write SetDatabase;
  75.     property Events: TStrings read FEvents write SetEvents;
  76.     property Registered: Boolean read FRegistered write SetRegistered;
  77.     property OnEventAlert: TEventAlert read FOnEventAlert write FOnEventAlert;
  78.   end;
  79.  
  80. implementation
  81.  
  82. uses
  83.   IBIntf;
  84.  
  85. function TIBEvents.GetNativeHandle: TISC_DB_HANDLE;
  86. begin
  87.   if assigned( FDatabase) and FDatabase.Connected then
  88.     Result := FDatabase.Handle
  89.   else result := nil;
  90. end;
  91.  
  92. procedure TIBEvents.ValidateDatabase( Database: TIBDatabase);
  93. begin
  94.   if not assigned( Database) then
  95.     IBError(ibxeDatabaseNameMissing, [nil]);
  96.   if not Database.Connected then
  97.     IBError(ibxeDatabaseOpen, [nil]);
  98. end;
  99.  
  100. { TIBEvents }
  101.  
  102. procedure HandleEvent( param: integer); stdcall;
  103. begin
  104.   { don't let exceptions propogate out of thread }
  105.   try
  106.     TIBEvents( param).HandleEvent;
  107.   except
  108.     Application.HandleException( nil);
  109.   end;
  110. end;
  111.  
  112. procedure IBEventCallback( ptr: pointer; length: short; updated: PChar); cdecl;
  113. var
  114.   ThreadID: DWORD;
  115. begin
  116.   { Handle events asynchronously in second thread }
  117.   EnterCriticalSection( TIBEvents( ptr).CS);
  118.   TIBEvents( ptr).UpdateResultBuffer( length, updated);
  119.   if TIBEvents( ptr).Queued then
  120.     CloseHandle( CreateThread( nil, 8192, @HandleEvent, ptr, 0, ThreadID));
  121.   LeaveCriticalSection( TIBEvents( ptr).CS);
  122. end;
  123.  
  124. constructor TIBEvents.Create( AOwner: TComponent);
  125. begin
  126.   inherited Create( AOwner);
  127.   FIBLoaded := False;
  128.   CheckIBLoaded;
  129.   FIBLoaded := True;
  130.   InitializeCriticalSection( CS);
  131.   FEvents := TStringList.Create;
  132.   with TStringList( FEvents) do
  133.   begin
  134.     OnChange := EventChange;
  135.     Duplicates := dupIgnore;
  136.   end;
  137. end;
  138.  
  139. destructor TIBEvents.Destroy;
  140. begin
  141.   if FIBLoaded then
  142.   begin
  143.     UnregisterEvents;
  144.     SetDatabase( nil);
  145.     TStringList(FEvents).OnChange := nil;
  146.     FEvents.Free;
  147.     DeleteCriticalSection( CS);
  148.   end;
  149.   inherited Destroy;
  150. end;
  151.  
  152. procedure TIBEvents.CancelEvents;
  153. begin
  154.   if ProcessingEvents then
  155.     IBError(ibxeInvalidCancellation, [nil]);  
  156.   if FQueued then
  157.   begin
  158.     try
  159.       { wait for event handler to finish before cancelling events }
  160.       EnterCriticalSection( CS);
  161.       ValidateDatabase( Database);
  162.       FQueued := false;
  163.       Changing := true;
  164.       if (isc_Cancel_events( StatusVector, @FDatabase.Handle, @EventID) > 0) then
  165.         IBDatabaseError;
  166.     finally
  167.       LeaveCriticalSection( CS);
  168.     end;
  169.   end;
  170. end;
  171.  
  172. procedure TIBEvents.DoQueueEvents;
  173. var
  174.   callback: pointer;
  175. begin
  176.   ValidateDatabase( DataBase);
  177.   callback := @IBEventCallback;
  178.   if (isc_que_events( StatusVector, @FDatabase.Handle, @EventID, EventBufferLen,
  179.                      EventBuffer, TISC_CALLBACK(callback), PVoid(Self)) > 0) then
  180.     IBDatabaseError;
  181.   FQueued := true;
  182. end;
  183.  
  184. procedure TIBEvents.EventChange( sender: TObject);
  185. begin
  186.   { check for blank event }
  187.   if TStringList(Events).IndexOf( '') <> -1 then
  188.     IBError(ibxeInvalidEvent, [nil]);
  189.   { check for too many events }
  190.   if Events.Count > MaxEvents then
  191.   begin
  192.     TStringList(Events).OnChange := nil;
  193.     Events.Delete( MaxEvents);
  194.     TStringList(Events).OnChange := EventChange;
  195.     IBError(ibxeMaximumEvents, [nil]);
  196.   end;
  197.   if Registered then RegisterEvents;
  198. end;
  199.  
  200. procedure TIBEvents.HandleEvent;
  201. var
  202.   Status: PStatusVector;
  203.   CancelAlerts: Boolean;
  204.   i: integer;
  205. begin
  206.   try
  207.     { prevent modification of vital data structures while handling events }
  208.     EnterCriticalSection( CS);
  209.     ProcessingEvents := true;
  210.     isc_event_counts( StatusVector, EventBufferLen, EventBuffer, ResultBuffer);
  211.     CancelAlerts := false;
  212.     if assigned(FOnEventAlert) and not Changing then
  213.     begin
  214.       for i := 0 to Events.Count-1 do
  215.       begin
  216.         try
  217.         Status := StatusVectorArray;
  218.         if (Status[i] <> 0) and not CancelAlerts then
  219.             FOnEventAlert( self, Events[Events.Count-i-1], Status[i], CancelAlerts);
  220.         except
  221.           Application.HandleException( nil);
  222.         end;
  223.       end;
  224.     end;
  225.     Changing := false;
  226.     if not CancelAlerts and FQueued then DoQueueEvents;
  227.   finally
  228.     ProcessingEvents := false;
  229.     LeaveCriticalSection( CS);
  230.   end;
  231. end;
  232.  
  233. procedure TIBEvents.Loaded;
  234. begin
  235.   inherited Loaded;
  236.   try
  237.     if RegisteredState then RegisterEvents;
  238.   except
  239.     if csDesigning in ComponentState then
  240.       Application.HandleException( self)
  241.     else raise;
  242.   end;
  243. end;
  244.  
  245. procedure TIBEvents.Notification( AComponent: TComponent;
  246.                                         Operation: TOperation);
  247. begin
  248.   inherited Notification( AComponent, Operation);
  249.   if (Operation = opRemove) and (AComponent = FDatabase) then
  250.   begin
  251.     UnregisterEvents;
  252.     FDatabase := nil;
  253.   end;
  254. end;
  255.  
  256. procedure TIBEvents.QueueEvents;
  257. begin
  258.   if not FRegistered then
  259.     IBError(ibxeNoEventsRegistered, [nil]);
  260.   if ProcessingEvents then
  261.     IBError(ibxeInvalidQueueing, [nil]);
  262.   if not FQueued then
  263.   begin
  264.     try
  265.       { wait until current event handler is finished before queuing events }
  266.       EnterCriticalSection( CS);
  267.       DoQueueEvents;
  268.       Changing := true;
  269.     finally
  270.       LeaveCriticalSection( CS);
  271.     end;
  272.   end;
  273. end;
  274.  
  275. procedure TIBEvents.RegisterEvents;
  276. var
  277.   i: integer;
  278.   bufptr: pointer;
  279.   eventbufptr: pointer;
  280.   resultbufptr: pointer;
  281.   buflen: integer;
  282. begin
  283.   ValidateDatabase( Database);
  284.   if csDesigning in ComponentState then FRegistered := true
  285.   else begin
  286.     UnregisterEvents;
  287.     if Events.Count = 0 then exit;
  288.     for i := 0 to Events.Count-1 do
  289.       StrPCopy( @Buffer[i][0], Events[i]);
  290.     i := Events.Count;
  291.     bufptr := @buffer[0];
  292.     eventbufptr :=  @EventBuffer;
  293.     resultBufPtr := @ResultBuffer;
  294.     asm
  295.       mov ecx, dword ptr [i]
  296.       mov eax, dword ptr [bufptr]
  297.       @@1:
  298.       push eax
  299.       add  eax, EventLength
  300.       loop @@1
  301.       push dword ptr [i]
  302.       push dword ptr [resultBufPtr]
  303.       push dword ptr [eventBufPtr]
  304.       call [isc_event_block]
  305.       mov  dword ptr [bufLen], eax
  306.       mov eax, dword ptr [i]
  307.       shl eax, 2
  308.       add eax, 12
  309.       add esp, eax
  310.     end;
  311.     EventBufferlen := Buflen;
  312.     FRegistered := true;
  313.     QueueEvents;
  314.   end;
  315. end;
  316.  
  317. procedure TIBEvents.SetEvents( value: TStrings);
  318. begin
  319.   FEvents.Assign( value);
  320. end;
  321.  
  322. procedure TIBEvents.SetDatabase( value: TIBDatabase);
  323. begin
  324.   if value <> FDatabase then
  325.   begin
  326.     UnregisterEvents;
  327.     if assigned( value) and value.Connected then ValidateDatabase( value);
  328.     FDatabase := value;
  329.   end;
  330. end;
  331.  
  332. procedure TIBEvents.SetRegistered( value: Boolean);
  333. begin
  334.   if (csReading in ComponentState) then
  335.     RegisteredState := value
  336.   else if FRegistered <> value then
  337.     if value then RegisterEvents else UnregisterEvents;
  338. end;
  339.  
  340. procedure TIBEvents.UnregisterEvents;
  341. begin
  342.   if ProcessingEvents then
  343.     IBError(ibxeInvalidRegistration, [nil]);
  344.   if csDesigning in ComponentState then
  345.     FRegistered := false
  346.   else if not (csLoading in ComponentState) then
  347.   begin
  348.     CancelEvents;
  349.     if FRegistered then
  350.     begin
  351.       isc_free( EventBuffer);
  352.       EventBuffer := nil;
  353.       isc_free( ResultBuffer);
  354.       ResultBuffer := nil;
  355.     end;
  356.     FRegistered := false;
  357.   end;
  358. end;
  359.  
  360. procedure TIBEvents.UpdateResultBuffer( length: short; updated: PChar);
  361. var
  362.   i: integer;
  363. begin
  364.   for i := 0 to length-1 do
  365.     ResultBuffer[i] := updated[i];
  366. end;
  367.  
  368. end.
  369.