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 45 by tony, Tue Dec 6 10:33:46 2016 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 +    procedure EventHandler(Sender: IEvents);
78 +    procedure ProcessEvents;
79      procedure EventChange(sender: TObject);
80      function GetDatabase: TIBDatabase;
81    function GetDatabaseHandle: TISC_DB_HANDLE;
81      procedure SetDatabase( value: TIBDatabase);
82      procedure ValidateDatabase( Database: TIBDatabase);
83      procedure DoBeforeDatabaseDisconnect(Sender: TObject);
# Line 93 | Line 92 | type
92      destructor Destroy; override;
93      procedure RegisterEvents;
94      procedure UnRegisterEvents;
96    property DatabaseHandle: TISC_DB_HANDLE read GetDatabaseHandle;
95      property DeferredRegister: boolean read FDeferredRegister write FDeferredRegister;
96 +    property EventIntf: IEvents read FEventIntf;
97    published
98      property Database: TIBDatabase read GetDatabase write SetDatabase;
99      property Events: TStrings read FEvents write SetEvents;
# Line 105 | Line 104 | type
104  
105   implementation
106  
107 < 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;
107 > uses SysUtils, FBMessages;
108  
109   { TIBEvents }
110  
# Line 389 | Line 119 | end;
119   constructor TIBEvents.Create( AOwner: TComponent);
120   begin
121    inherited Create( AOwner);
392  FIBLoaded := False;
393  CheckIBLoaded;
394  FIBLoaded := True;
122    FBase := TIBBase.Create(Self);
123 <  FBase.BeforeDatabaseDisconnect := DoBeforeDatabaseDisconnect;
124 <  FBase.AfterDatabaseConnect := DoAfterDatabaseConnect;
123 >  FBase.BeforeDatabaseDisconnect := @DoBeforeDatabaseDisconnect;
124 >  FBase.AfterDatabaseConnect := @DoAfterDatabaseConnect;
125    FEvents := TStringList.Create;
126    with TStringList( FEvents) do
127    begin
128 <    OnChange := EventChange;
128 >    OnChange := @EventChange;
129      Duplicates := dupIgnore;
130    end;
404  FEventHandler := TEventHandler.Create(self)
131   end;
132  
133   destructor TIBEvents.Destroy;
134   begin
135 <  if FIBLoaded then
136 <  begin
137 <    UnregisterEvents;
138 <    SetDatabase(nil);
139 <    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;
135 >  UnregisterEvents;
136 >  SetDatabase(nil);
137 >  TStringList(FEvents).OnChange := nil;
138 >  FBase.Free;
139 >  FEvents.Free;
140   end;
141  
142 + procedure TIBEvents.EventHandler(Sender: IEvents);
143 + begin
144 +  TThread.Synchronize(nil,@ProcessEvents);
145 + end;
146  
147 + procedure TIBEvents.ProcessEvents;
148 + var EventCounts: TEventCounts;
149 +    CancelAlerts: Boolean;
150 +    i: integer;
151 + begin
152 +  if (csDestroying in ComponentState) or (FEventIntf = nil) then Exit;
153 +  EventCounts := FEventIntf.ExtractEventCounts;
154 +  if assigned(FOnEventAlert) then
155 +  begin
156 +    CancelAlerts := false;
157 +    for i := 0 to Length(EventCounts) -1 do
158 +    begin
159 +      OnEventAlert(self,EventCounts[i].EventName,EventCounts[i].Count,CancelAlerts);
160 +      if CancelAlerts then break;
161 +    end;
162 +  end;
163 +  if CancelAlerts then
164 +    UnRegisterEvents
165 +  else
166 +    FEventIntf.AsyncWaitForEvent(@EventHandler);
167 + end;
168  
169   procedure TIBEvents.EventChange( sender: TObject);
170   begin
# Line 432 | Line 176 | begin
176    begin
177      TStringList(Events).OnChange := nil;
178      Events.Delete( MaxEvents);
179 <    TStringList(Events).OnChange := EventChange;
179 >    TStringList(Events).OnChange := @EventChange;
180      IBError(ibxeMaximumEvents, [nil]);
181    end;
182 <  if Registered then
183 <    TEventHandler(FEventHandler).RegisterEvents(Events);
182 >  if Registered  and (FEventIntf <> nil) then
183 >  begin
184 >    FEventIntf.SetEvents(Events);
185 >    FEventIntf.AsyncWaitForEvent(@EventHandler);
186 >  end;
187   end;
188  
189   procedure TIBEvents.Notification( AComponent: TComponent;
# Line 452 | Line 199 | end;
199  
200   procedure TIBEvents.RegisterEvents;
201   begin
202 +  if FRegistered then Exit;
203    ValidateDatabase( Database);
204    if csDesigning in ComponentState then FRegistered := true
205    else
# Line 460 | Line 208 | begin
208        FDeferredRegister := true
209      else
210      begin
211 <      TEventHandler(FEventHandler).RegisterEvents(Events);
211 >      FEventIntf := Database.Attachment.GetEventHandler(Events);
212 >      FEventIntf.AsyncWaitForEvent(@EventHandler);
213        FRegistered := true;
214      end;
215    end;
# Line 488 | Line 237 | begin
237    Result := FBase.Database
238   end;
239  
240 < procedure TIBEvents.SetRegistered( value: Boolean);
240 > procedure TIBEvents.SetRegistered(value: boolean);
241   begin
242    FDeferredRegister := false;
243    if not assigned(FBase) or (FBase.Database = nil) then
# Line 500 | Line 249 | begin
249    if value then RegisterEvents else UnregisterEvents;
250   end;
251  
252 < procedure TIBEvents.UnregisterEvents;
252 > procedure TIBEvents.UnRegisterEvents;
253   begin
254    FDeferredRegister := false;
255    if not FRegistered then
# Line 509 | Line 258 | begin
258      FRegistered := false
259    else
260    begin
261 <    TEventHandler(FEventHandler).UnRegisterEvents;
261 >    FEventIntf := nil;
262      FRegistered := false;
263    end;
264   end;
# Line 525 | Line 274 | begin
274      Registered := true
275   end;
276  
528 function TIBEvents.GetDatabaseHandle: TISC_DB_HANDLE;
529 begin
530  ValidateDatabase(FBase.Database);
531  Result := FBase.Database.Handle;
532 end;
533
277  
278   end.

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines