ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/runtime/IBEvents.pas
(Generate patch)

Comparing ibx/trunk/runtime/IBEvents.pas (file contents):
Revision 7 by tony, Sun Aug 5 18:28:19 2012 UTC vs.
Revision 47 by tony, Mon Jan 9 15:31:51 2017 UTC

# Line 44 | Line 44
44  
45   unit IBEvents;
46  
47 < {$Mode Delphi}
47 > {$mode objfpc}{$H+}
48  
49   interface
50  
# Line 54 | Line 54 | uses
54   {$ELSE}
55    unix,
56   {$ENDIF}
57 <  Classes, Graphics, Controls,
58 <  Forms, Dialogs, IBHeader, IBExternals, IB, IBDatabase;
57 >  Classes, IBExternals, IB, IBDatabase;
58  
59   const
60    MaxEvents = 15;
# Line 69 | Line 68 | type
68  
69    TIBEvents = class(TComponent)
70    private
72    FIBLoaded: Boolean;
71      FBase: TIBBase;
72 +    FEventIntf: IEvents;
73      FEvents: TStrings;
74      FOnEventAlert: TEventAlert;
76    FEventHandler: TObject;
75      FRegistered: boolean;
76      FDeferredRegister: boolean;
77 +    FStartEvent: boolean;
78 +    procedure EventHandler(Sender: IEvents);
79 +    procedure ProcessEvents;
80      procedure EventChange(sender: TObject);
81      function GetDatabase: TIBDatabase;
81    function GetDatabaseHandle: TISC_DB_HANDLE;
82      procedure SetDatabase( value: TIBDatabase);
83      procedure ValidateDatabase( Database: TIBDatabase);
84      procedure DoBeforeDatabaseDisconnect(Sender: TObject);
# Line 93 | Line 93 | type
93      destructor Destroy; override;
94      procedure RegisterEvents;
95      procedure UnRegisterEvents;
96    property DatabaseHandle: TISC_DB_HANDLE read GetDatabaseHandle;
96      property DeferredRegister: boolean read FDeferredRegister write FDeferredRegister;
97 +    property EventIntf: IEvents read FEventIntf;
98    published
99      property Database: TIBDatabase read GetDatabase write SetDatabase;
100      property Events: TStrings read FEvents write SetEvents;
# Line 105 | Line 105 | type
105  
106   implementation
107  
108 < uses
109 <  IBIntf, syncobjs;
110 <
111 < type
112 <
113 <  TEventHandlerStates = (
114 <    stIdle,           {Events not monitored}
115 <    stHasEvb,         {Event Block Allocated but not queued}
116 <    stQueued,         {Waiting for Event}
117 <    stSignalled       {Event Callback signalled Event}
118 <   );
119 <
120 <  { TEventHandler }
121 <
122 <  TEventHandler = class(TThread)
123 <  private
124 <    FOwner: TIBEvents;
125 <    FCriticalSection: TCriticalSection;   {protects race conditions in stQueued state}
126 <    {$IFDEF WINDOWS}
127 <    {Make direct use of Windows API as TEventObject don't seem to work under
128 <     Windows!}
129 <    FEventHandler: THandle;
130 <    {$ELSE}
131 <    FEventWaiting: TEventObject;
132 <    {$ENDIF}
133 <    FState: TEventHandlerStates;
134 <    FEventBuffer: PChar;
135 <    FEventBufferLen: integer;
136 <    FEventID: ISC_LONG;
137 <    FRegisteredState: Boolean;
138 <    FResultBuffer: PChar;
139 <    FEvents: TStringList;
140 <    FSignalFired: boolean;
141 <    procedure QueueEvents;
142 <    procedure CancelEvents;
143 <    procedure HandleEventSignalled(length: short; updated: PChar);
144 <    procedure DoEventSignalled;
145 <  protected
146 <    procedure Execute; override;
147 <  public
148 <    constructor Create(Owner: TIBEvents);
149 <    destructor Destroy; override;
150 <    procedure Terminate;
151 <    procedure RegisterEvents(Events: TStrings);
152 <    procedure UnregisterEvents;
153 <  end;
154 <
155 < {This procedure is used for the event call back - note the cdecl }
156 <
157 < procedure IBEventCallback( ptr: pointer; length: short; updated: PChar); cdecl;
158 < begin
159 <  if (ptr = nil) or (length = 0) or (updated = nil) then
160 <    Exit;
161 <  { Handle events asynchronously in second thread }
162 <  TEventHandler(ptr).HandleEventSignalled(length,updated);
163 < end;
164 <
165 <
166 <
167 < { TEventHandler }
168 <
169 < procedure TEventHandler.QueueEvents;
170 < var
171 <  callback: pointer;
172 <  DBH: TISC_DB_HANDLE;
173 < begin
174 <  if FState <> stHasEvb then
175 <    Exit;
176 <  FCriticalSection.Enter;
177 <  try
178 <    callback := @IBEventCallback;
179 <    DBH := FOwner.DatabaseHandle;
180 <    if (isc_que_events( StatusVector, @DBH, @FEventID, FEventBufferLen,
181 <                     FEventBuffer, TISC_CALLBACK(callback), PVoid(Self)) <> 0) then
182 <      IBDatabaseError;
183 <    FState := stQueued
184 <  finally
185 <    FCriticalSection.Leave
186 <  end;
187 < end;
188 <
189 < procedure TEventHandler.CancelEvents;
190 < var
191 <  DBH: TISC_DB_HANDLE;
192 < begin
193 <  if FState in [stQueued,stSignalled] then
194 <  begin
195 <    FCriticalSection.Enter;
196 <    try
197 <      DBH := FOwner.DatabaseHandle;
198 <      if (isc_Cancel_events( StatusVector, @DBH, @FEventID) <> 0) then
199 <          IBDatabaseError;
200 <      FState := stHasEvb;
201 <    finally
202 <      FCriticalSection.Leave
203 <    end;
204 <  end;
205 <
206 <  if FState = stHasEvb then
207 <  begin
208 <    isc_free( FEventBuffer);
209 <    FEventBuffer := nil;
210 <    isc_free( FResultBuffer);
211 <    FResultBuffer := nil;
212 <    FState := stIdle
213 <  end;
214 <  FSignalFired := false
215 < end;
216 <
217 < procedure TEventHandler.HandleEventSignalled(length: short; updated: PChar);
218 < begin
219 <  FCriticalSection.Enter;
220 <  try
221 <    if FState <> stQueued then
222 <      Exit;
223 <    Move(Updated[0], FResultBuffer[0], Length);
224 <    FState := stSignalled;
225 <    {$IFDEF WINDOWS}
226 <    SetEVent(FEventHandler);
227 <    {$ELSE}
228 <    FEventWaiting.SetEvent;
229 <    {$ENDIF}
230 <  finally
231 <    FCriticalSection.Leave
232 <  end;
233 < end;
234 <
235 < procedure TEventHandler.DoEventSignalled;
236 < var
237 <  i: integer;
238 <  CancelAlerts: boolean;
239 <  Status: array[0..19] of ISC_LONG; {Note in 64 implementation the ibase.h implementation
240 <                                     is different from Interbase 6.0 API documentatoin}
241 < begin
242 <    if FState <> stSignalled then
243 <      Exit;
244 <    isc_event_counts( @Status, FEventBufferLen, FEventBuffer, FResultBuffer);
245 <    CancelAlerts := false;
246 <    if not FSignalFired then
247 <      FSignalFired := true   {Ignore first time}
248 <    else
249 <    if assigned(FOwner.FOnEventAlert)  then
250 <    begin
251 <      for i := 0 to FEvents.Count - 1 do
252 <      begin
253 <        try
254 <        if (Status[i] <> 0) and not CancelAlerts then
255 <            FOwner.FOnEventAlert( self, FEvents[i], Status[i], CancelAlerts);
256 <        except
257 <          Application.HandleException( nil);
258 <        end;
259 <      end;
260 <    end;
261 <    FState := stHasEvb;
262 <  if  CancelAlerts then
263 <      CancelEvents
264 <    else
265 <      QueueEvents
266 < end;
267 <
268 < procedure TEventHandler.Execute;
269 < begin
270 <  while not Terminated do
271 <  begin
272 <    {$IFDEF WINDOWS}
273 <    WaitForSingleObject(FEventHandler,INFINITE);
274 <    {$ELSE}
275 <    FEventWaiting.WaitFor(INFINITE);
276 <    {$ENDIF}
277 <
278 <    if not Terminated and (FState = stSignalled) then
279 <      Synchronize(DoEventSignalled)
280 <  end;
281 < end;
282 <
283 <
284 <
285 < constructor TEventHandler.Create(Owner: TIBEvents);
286 < var
287 <  PSa : PSecurityAttributes;
288 < {$IFDEF WINDOWS}
289 <  Sd : TSecurityDescriptor;
290 <  Sa : TSecurityAttributes;
291 < begin
292 <  InitializeSecurityDescriptor(@Sd,SECURITY_DESCRIPTOR_REVISION);
293 <  SetSecurityDescriptorDacl(@Sd,true,nil,false);
294 <  Sa.nLength := SizeOf(Sa);
295 <  Sa.lpSecurityDescriptor := @Sd;
296 <  Sa.bInheritHandle := true;
297 <  PSa := @Sa;
298 < {$ELSE}
299 < begin
300 <  PSa:= nil;
301 < {$ENDIF}
302 <  inherited Create(true);
303 <  FOwner := Owner;
304 <  FState := stIdle;
305 <  FCriticalSection := TCriticalSection.Create;
306 <  {$IFDEF WINDOWS}
307 <  FEventHandler := CreateEvent(PSa,false,true,nil);
308 <  {$ELSE}
309 <  FEventWaiting := TEventObject.Create(PSa,false,true,FOwner.Name+'.Events');
310 <  {$ENDIF}
311 <  FEvents := TStringList.Create;
312 <  FreeOnTerminate := true;
313 <  Resume
314 < end;
315 <
316 < destructor TEventHandler.Destroy;
317 < begin
318 <  if assigned(FCriticalSection) then FCriticalSection.Free;
319 <  {$IFDEF WINDOWS}
320 <  CloseHandle(FEventHandler);
321 <  {$ELSE}
322 <  if assigned(FEventWaiting) then FEventWaiting.Free;
323 <  {$ENDIF}
324 <  if assigned(FEvents) then FEvents.Free;
325 <  inherited Destroy;
326 < end;
327 <
328 < procedure TEventHandler.Terminate;
329 < begin
330 <  inherited Terminate;
331 <  {$IFDEF WINDOWS}
332 <  SetEvent(FEventHandler);
333 <  {$ELSE}
334 <  FEventWaiting.SetEvent;
335 <  {$ENDIF}
336 <  CancelEvents;
337 < end;
338 <
339 < procedure TEventHandler.RegisterEvents(Events: TStrings);
340 < var
341 <  i: integer;
342 <  EventNames: array of PChar;
343 < begin
344 <  UnregisterEvents;
345 <
346 <  if Events.Count = 0 then
347 <    exit;
348 <
349 <  setlength(EventNames,MaxEvents);
350 <  try
351 <    for i := 0 to Events.Count-1 do
352 <      EventNames[i] := PChar(Events[i]);
353 <    FEvents.Assign(Events);
354 <    FEventBufferlen := isc_event_block(@FEventBuffer,@FResultBuffer,
355 <                        Events.Count,
356 <                        EventNames[0],EventNames[1],EventNames[2],
357 <                        EventNames[3],EventNames[4],EventNames[5],
358 <                        EventNames[6],EventNames[7],EventNames[8],
359 <                        EventNames[9],EventNames[10],EventNames[11],
360 <                        EventNames[12],EventNames[13],EventNames[14]
361 <                        );
362 <    FState := stHasEvb;
363 <    FRegisteredState := true;
364 <    QueueEvents
365 <  finally
366 <    SetLength(EventNames,0)
367 <  end;
368 < end;
369 <
370 < procedure TEventHandler.UnregisterEvents;
371 < begin
372 <  if FRegisteredState then
373 <  begin
374 <    CancelEvents;
375 <    FRegisteredState := false;
376 <  end;
377 < end;
108 > uses SysUtils, FBMessages;
109  
110   { TIBEvents }
111  
# Line 389 | Line 120 | end;
120   constructor TIBEvents.Create( AOwner: TComponent);
121   begin
122    inherited Create( AOwner);
392  FIBLoaded := False;
393  CheckIBLoaded;
394  FIBLoaded := True;
123    FBase := TIBBase.Create(Self);
124 <  FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
125 <  FBase.AfterDatabaseConnect := DoAfterDatabaseConnect;
124 >  FBase.BeforeDatabaseDisconnect := @DoBeforeDatabaseDisconnect;
125 >  FBase.AfterDatabaseConnect := @DoAfterDatabaseConnect;
126    FEvents := TStringList.Create;
127 +  FStartEvent := true;
128    with TStringList( FEvents) do
129    begin
130 <    OnChange := EventChange;
130 >    OnChange := @EventChange;
131      Duplicates := dupIgnore;
132    end;
404  FEventHandler := TEventHandler.Create(self)
133   end;
134  
135   destructor TIBEvents.Destroy;
136   begin
137 <  if FIBLoaded then
138 <  begin
139 <    UnregisterEvents;
140 <    SetDatabase(nil);
141 <    TStringList(FEvents).OnChange := nil;
414 <    FBase.Free;
415 <    FEvents.Free;
416 <  end;
417 <  if assigned(FEventHandler) then
418 <    TEventHandler(FEventHandler).Terminate;
419 <  FEventHandler := nil;
420 <  inherited Destroy;
137 >  UnregisterEvents;
138 >  SetDatabase(nil);
139 >  TStringList(FEvents).OnChange := nil;
140 >  FBase.Free;
141 >  FEvents.Free;
142   end;
143  
144 + procedure TIBEvents.EventHandler(Sender: IEvents);
145 + begin
146 +  TThread.Synchronize(nil,@ProcessEvents);
147 + end;
148  
149 + procedure TIBEvents.ProcessEvents;
150 + var EventCounts: TEventCounts;
151 +    CancelAlerts: Boolean;
152 +    i: integer;
153 + begin
154 +  if (csDestroying in ComponentState) or (FEventIntf = nil) then Exit;
155 +  CancelAlerts := false;
156 +  EventCounts := FEventIntf.ExtractEventCounts;
157 +  if FStartEvent then
158 +    FStartEvent := false {ignore the first one}
159 +  else
160 +  if assigned(FOnEventAlert) then
161 +  begin
162 +    CancelAlerts := false;
163 +    for i := 0 to Length(EventCounts) -1 do
164 +    begin
165 +      OnEventAlert(self,EventCounts[i].EventName,EventCounts[i].Count,CancelAlerts);
166 +      if CancelAlerts then break;
167 +    end;
168 +  end;
169 +  if CancelAlerts then
170 +    UnRegisterEvents
171 +  else
172 +    FEventIntf.AsyncWaitForEvent(@EventHandler);
173 + end;
174  
175   procedure TIBEvents.EventChange( sender: TObject);
176   begin
# Line 432 | Line 182 | begin
182    begin
183      TStringList(Events).OnChange := nil;
184      Events.Delete( MaxEvents);
185 <    TStringList(Events).OnChange := EventChange;
185 >    TStringList(Events).OnChange := @EventChange;
186      IBError(ibxeMaximumEvents, [nil]);
187    end;
188 <  if Registered then
189 <    TEventHandler(FEventHandler).RegisterEvents(Events);
188 >  if Registered  and (FEventIntf <> nil) then
189 >  begin
190 >    FEventIntf.SetEvents(Events);
191 >    FEventIntf.AsyncWaitForEvent(@EventHandler);
192 >  end;
193   end;
194  
195   procedure TIBEvents.Notification( AComponent: TComponent;
# Line 452 | Line 205 | end;
205  
206   procedure TIBEvents.RegisterEvents;
207   begin
208 +  if FRegistered then Exit;
209    ValidateDatabase( Database);
210    if csDesigning in ComponentState then FRegistered := true
211    else
# Line 460 | Line 214 | begin
214        FDeferredRegister := true
215      else
216      begin
217 <      TEventHandler(FEventHandler).RegisterEvents(Events);
217 >      FEventIntf := Database.Attachment.GetEventHandler(Events);
218 >      FEventIntf.AsyncWaitForEvent(@EventHandler);
219        FRegistered := true;
220      end;
221    end;
# Line 488 | Line 243 | begin
243    Result := FBase.Database
244   end;
245  
246 < procedure TIBEvents.SetRegistered( value: Boolean);
246 > procedure TIBEvents.SetRegistered(value: boolean);
247   begin
248    FDeferredRegister := false;
249    if not assigned(FBase) or (FBase.Database = nil) then
# Line 500 | Line 255 | begin
255    if value then RegisterEvents else UnregisterEvents;
256   end;
257  
258 < procedure TIBEvents.UnregisterEvents;
258 > procedure TIBEvents.UnRegisterEvents;
259   begin
260    FDeferredRegister := false;
261    if not FRegistered then
# Line 509 | Line 264 | begin
264      FRegistered := false
265    else
266    begin
267 <    TEventHandler(FEventHandler).UnRegisterEvents;
267 >    FEventIntf := nil;
268      FRegistered := false;
269    end;
270   end;
# Line 525 | Line 280 | begin
280      Registered := true
281   end;
282  
528 function TIBEvents.GetDatabaseHandle: TISC_DB_HANDLE;
529 begin
530  ValidateDatabase(FBase.Database);
531  Result := FBase.Database.Handle;
532 end;
533
283  
284   end.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines