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

  1. {********************************************************}
  2. {                                                        }
  3. {       Borland Delphi Visual Component Library          }
  4. {       InterBase Express core components                }
  5. {                                                        }
  6. {       Copyright (c) 1998-1999 Inprise Corporation      }
  7. {                                                        }
  8. {    InterBase Express is based in part on the product   }
  9. {    Free IB Components, written by Gregory H. Deatz for }
  10. {    Hoagland, Longo, Moran, Dunst & Doukas Company.     }
  11. {    Free IB Components is used under license.           }
  12. {                                                        }
  13. {********************************************************}
  14.  
  15. unit IBSQLMonitor;
  16.  
  17. interface
  18.  
  19. uses
  20.   SysUtils, Windows, Messages, Classes, Forms, Controls, IBUtils,
  21.   IBSQL, IBCustomDataSet, IBDatabase, Dialogs, StdCtrls, IBServices;
  22.  
  23. resourcestring
  24.   SCantPrintValue = 'Cannot print value';
  25.   SEOFReached = 'SEOFReached'; 
  26.   
  27. const
  28.   WM_MIN_IBSQL_MONITOR = WM_USER;
  29.   WM_MAX_IBSQL_MONITOR = WM_USER + 512;
  30.   WM_IBSQL_SQL_EVENT = WM_MIN_IBSQL_MONITOR + 1;
  31.  
  32. type
  33.   TIBSQLMonitorHook = class;
  34.   TIBCustomSQLMonitor = class;
  35.  
  36.   { TIBSQLMonitor }
  37.   TSQLEvent = procedure(EventText: String) of object;
  38.  
  39.   TIBCustomSQLMonitor = class(TComponent)
  40.   private
  41.     FAtom: TAtom;
  42.     FHWnd: HWND;
  43.     FThread: THandle;
  44.     FOnSQLEvent: TSQLEvent;
  45.     FTraceFlags: TTraceFlags;
  46.   protected
  47.     procedure MonitorHandler(var Msg: TMessage); virtual;
  48.     property OnSQL: TSQLEvent read FOnSQLEvent write FOnSQLEvent;
  49.     property TraceFlags: TTraceFlags read FTraceFlags write FTraceFlags;
  50.   public
  51.     constructor Create(AOwner: TComponent); override;
  52.     destructor Destroy; override;
  53.   end;
  54.  
  55.   TIBSQLMonitor = class(TIBCustomSQLMonitor)
  56.   published
  57.     property OnSQL;
  58.   end;
  59.  
  60.   { TIBSQLMonitorHook }
  61.   TIBSQLMonitorHook = class(TObject)
  62.   private
  63.     FSharedBuffer,                   { MMF for shared memory }
  64.     FWriteLock,                      { Only one writer at a time }
  65.     FWriteEvent,                     { The SQL log has been written }
  66.     FWriteFinishedEvent,             { The SQL log write is finished }
  67.     FReadEvent,                      { All readers are ready }
  68.     FReadFinishedEvent: THandle;     { The SQL log read is now finished }
  69.     FBuffer: PChar;                  { The shared buffer }
  70.     FMonitorCount: PInteger;         { Number of registered monitors }
  71.     FReaderCount: PInteger;          { Number of monitors currently reading }
  72.     FTraceDataType: PInteger;        { Datatype: connect, prepare, trans, .. }
  73.     FBufferSize: PInteger;           { Size of written buffer }
  74.     FTraceFlags: TTraceFlags;
  75.   protected
  76.     procedure ResetStates;
  77.     procedure Lock;
  78.     procedure Unlock;
  79.     procedure BeginWrite;
  80.     procedure EndWrite;
  81.     procedure BeginRead;
  82.     procedure EndRead;
  83.     procedure WriteSQLData(Text: String; DataType: TTraceFlag);
  84.   public
  85.     constructor Create;
  86.     destructor Destroy; override;
  87.     function MonitorCount: Integer;
  88.     procedure RegisterMonitor;
  89.     procedure UnregisterMonitor;
  90.     function ReadSQLData(Arg: TIBCustomSQLMonitor): String;
  91.     procedure SQLPrepare(qry: TIBSQL); virtual;
  92.     procedure SQLExecute(qry: TIBSQL); virtual;
  93.     procedure SQLFetch(qry: TIBSQL); virtual;
  94.     procedure DBConnect(db: TIBDatabase); virtual;
  95.     procedure DBDisconnect(db: TIBDatabase); virtual;
  96.     procedure TRStart(tr: TIBTransaction); virtual;
  97.     procedure TRCommit(tr: TIBTransaction); virtual;
  98.     procedure TRCommitRetaining(tr: TIBTransaction); virtual;
  99.     procedure TRRollback(tr: TIBTransaction); virtual;
  100.     procedure TRRollbackRetaining(tr: TIBTransaction); virtual;
  101.     procedure ServiceAttach(service: TIBCustomService); virtual;
  102.     procedure ServiceDetach(service: TIBCustomService); virtual;
  103.     procedure ServiceQuery(service: TIBCustomService); virtual;
  104.     procedure ServiceStart(service: TIBCustomService); virtual;
  105.     property TraceFlags: TTraceFlags read FTraceFlags write FTraceFlags;
  106.   end;
  107.  
  108. function MonitorHook: TIBSQLMonitorHook;
  109. procedure EnableMonitoring;
  110. procedure DisableMonitoring;
  111. function MonitoringEnabled: Boolean;
  112.  
  113. implementation
  114.  
  115. uses
  116.   IB;
  117.  
  118. var
  119.   bMonitoringEnabled: Boolean;
  120.  
  121. procedure EnableMonitoring;
  122. begin
  123.   bMonitoringEnabled := True;
  124. end;
  125.  
  126. procedure DisableMonitoring;
  127. begin
  128.   bMonitoringEnabled := False;
  129. end;
  130.  
  131. function MonitoringEnabled: Boolean;
  132. begin
  133.   result := bMonitoringEnabled;
  134. end;
  135.  
  136. { TIBCustomSQLMonitor }
  137.  
  138. procedure IBSQLM_Thread(Arg: TIBCustomSQLMonitor); stdcall;
  139. var
  140.   st: String;
  141.   len: Integer;
  142.   FBuffer: PChar;
  143. begin
  144.   while (Arg <> nil) and (Arg.FHWnd <> 0) do begin
  145.     st := MonitorHook.ReadSQLData(Arg);
  146.     if (st <> '') and (Arg.FHWnd <> 0) then begin
  147.       len := Length(st);
  148.       GetMem(FBuffer, len + SizeOf(Integer));
  149.       Move(len, FBuffer[0], SizeOf(Integer));
  150.       Move(st[1], FBuffer[SizeOf(Integer)], len);
  151.       PostMessage(
  152.         Arg.FHWnd,
  153.         WM_IBSQL_SQL_EVENT,
  154.         WPARAM(Arg),
  155.         LPARAM(FBuffer));
  156.     end;
  157.   end;
  158.   ExitThread(0);
  159. end;
  160.  
  161. {function InitWndProc(HWindow: HWnd; Message, WParam: Longint;
  162.   LParam: Longint): Longint; stdcall;}
  163. function IBSQLM_WindowProc(_hWnd: HWND; Msg: UINT; _wParam: WPARAM;
  164.   _lParam: LPARAM): LRESULT; stdcall;
  165. var
  166.   MsgRec: TMessage;
  167. begin
  168.   MsgRec.Msg := Msg;
  169.   MsgRec.WParam := _wParam;
  170.   MsgRec.LParam := _lParam;
  171.   case Msg of
  172.     WM_CREATE:
  173.       result := 0;
  174.     else begin
  175.       if ((Msg >= WM_MIN_IBSQL_MONITOR) and
  176.           (Msg <= WM_MAX_IBSQL_MONITOR)) then begin
  177.         try
  178.           TIBCustomSQLMonitor(_wParam).MonitorHandler(MsgRec);
  179.         except
  180.           ;
  181.         end;
  182.         result := MsgRec.Result;
  183.       end else
  184.         result := DefWindowProc(_hWnd, Msg, _wParam, _lParam);
  185.     end;
  186.   end;
  187. end;
  188.  
  189. var
  190.   MonitorClass: TWndClass = (
  191.     style: 0;
  192.     lpfnWndProc: @IBSQLM_WindowProc;
  193.     cbClsExtra: 0;
  194.     cbWndExtra: 0;
  195.     hInstance: 0;
  196.     hIcon: 0;
  197.     hCursor: 0;
  198.     hbrBackground: 0;
  199.     lpszMenuName: nil;
  200.     lpszClassName: 'TIBCustomSQLMonitor' {do not localize}
  201.   );
  202.  
  203. constructor TIBCustomSQLMonitor.Create(AOwner: TComponent);
  204. var
  205.   TempClass: TWndClass;
  206.   ThreadID: DWORD;
  207. begin
  208.   inherited;
  209.   FTraceFlags := [tfqPrepare .. tfMisc];
  210.   MonitorClass.hInstance := HInstance;
  211.   if not GetClassInfo(HInstance, MonitorClass.lpszClassName,
  212.            TempClass) then begin
  213.     FAtom := Windows.RegisterClass(MonitorClass);
  214.     if FAtom = 0 then
  215.       IBError(ibxeWindowsAPIError, [GetLastError, GetLastError]);
  216.   end;
  217.   FHWnd := CreateWindow(PChar(FAtom), '', 0, 0, 0, 0, 0,
  218.              0, 0, HInstance, nil);
  219.   if FHWnd = 0 then
  220.     IBError(ibxeSQLMonitorAlreadyPresent, []);
  221.   if not (csDesigning in ComponentState) then
  222.   begin
  223.     MonitorHook.RegisterMonitor;
  224.     FThread := CreateThread(nil, 0, @IBSQLM_Thread, Pointer(Self), 0, ThreadID);
  225.     if FThread = 0 then
  226.       IBError(ibxeWindowsAPIError, [GetLastError, GetLastError]);
  227.   end;
  228. end;
  229.  
  230. destructor TIBCustomSQLMonitor.Destroy;
  231. begin
  232.   if csDesigning in ComponentState then
  233.   begin
  234.     DestroyWindow(FHWnd);
  235.     Windows.UnregisterClass(PChar(FAtom), HInstance);
  236.   end
  237.   else begin
  238.     MonitorHook.UnregisterMonitor;
  239.     DestroyWindow(FHWnd);
  240.     FHWnd := 0;
  241.     MonitorHook.ResetStates;
  242.     if WaitForSingleObject(FThread, 10000) = WAIT_TIMEOUT then
  243.       CloseHandle(FThread);
  244.     Windows.UnregisterClass(PChar(FAtom), HInstance);
  245.     FAtom := 0;
  246.   end;
  247.   inherited;
  248. end;
  249.  
  250. procedure TIBCustomSQLMonitor.MonitorHandler(var Msg: TMessage);
  251. var
  252.   st: String;
  253. begin
  254.   if (Msg.Msg = WM_IBSQL_SQL_EVENT) then begin
  255.     if (Assigned(FOnSQLEvent)) then begin
  256.       begin
  257.         SetString(st, PChar(Msg.LParam) + SizeOf(Integer),
  258.                   PInteger(Msg.LParam)^);
  259.         FreeMem(PChar(Msg.LParam));
  260.         FOnSQLEvent(st);
  261.       end;
  262.     end;
  263.   end else
  264.     Msg.Result := DefWindowProc(FHWnd, Msg.Msg, Msg.WParam, Msg.LParam);
  265. end;
  266.  
  267. { TIBSQLMonitorHook }
  268.  
  269. const
  270.   MonitorHookNames: array[0..5] of String = (
  271.     'IB.SQL.MONITOR.Mutex',
  272.     'IB.SQL.MONITOR.SharedMem',
  273.     'IB.SQL.MONITOR.WriteEvent',
  274.     'IB.SQL.MONITOR.WriteFinishedEvent',
  275.     'IB.SQL.MONITOR.ReadEvent',
  276.     'IB.SQL.MONITOR.ReadFinishedEvent'
  277.   );
  278.   cMonitorHookSize = 1024;
  279.   cMaxBufferSize = cMonitorHookSize - (4 * SizeOf(Integer));
  280.   cDefaultTimeout = 2000; { 2 seconds }
  281.  
  282. constructor TIBSQLMonitorHook.Create;
  283.   function CreateLocalEvent(Idx: Integer; InitialState: Boolean): THandle;
  284.   begin
  285.     result := CreateEvent(nil, True, InitialState, PChar(MonitorHookNames[Idx]));
  286.     if result = 0 then
  287.       IBError(ibxeCannotCreateSharedResource, [GetLastError]);
  288.   end;
  289.  
  290. begin
  291.   { Create the MMF with the initial size, and
  292.     create all events with an initial state of non-signalled }
  293.   FWriteLock := CreateMutex(nil, False, PChar(MonitorHookNames[0]));
  294.   if (FWriteLock = 0) then
  295.     IBError(ibxeCannotCreateSharedResource, [GetLastError]);
  296.   Lock; { Serialize the creation of memory mapped files. }
  297.   try
  298.     FWriteEvent := CreateLocalEvent(2, False);
  299.     FWriteFinishedEvent := CreateLocalEvent(3, True);
  300.     FReadEvent := CreateLocalEvent(4, False);
  301.     FReadFinishedEvent := CreateLocalEvent(5, False);
  302.     { Set up the MMF }
  303.     FSharedBuffer := CreateFileMapping(
  304.                        $FFFFFFFF, nil, PAGE_READWRITE, 0, cMonitorHookSize,
  305.                        PChar(MonitorHookNames[1]));
  306.     if (FSharedBuffer = 0) then
  307.       IBError(ibxeCannotCreateSharedResource, [GetLastError]);
  308.     FBuffer := MapViewOfFile(FSharedBuffer, FILE_MAP_ALL_ACCESS, 0, 0, 0);
  309.     FMonitorCount := PInteger(FBuffer + cMonitorHookSize - SizeOf(Integer));
  310.     FReaderCount := PInteger(PChar(FMonitorCount) - SizeOf(Integer));
  311.     FTraceDataType := PInteger(PChar(FReaderCount) - SizeOf(Integer));
  312.     FBufferSize := PInteger(PChar(FTraceDataType) - SizeOf(Integer));
  313.     FMonitorCount^ := 0;
  314.     FReaderCount^ := 0;
  315.     FBufferSize^ := 0;
  316.   finally
  317.     Unlock;
  318.   end;
  319. end;
  320.  
  321. destructor TIBSQLMonitorHook.Destroy;
  322. begin
  323.   Lock;
  324.   try
  325.     UnmapViewOfFile(FBuffer);
  326.     CloseHandle(FSharedBuffer);
  327.     CloseHandle(FWriteEvent);
  328.     CloseHandle(FWriteFinishedEvent);
  329.     CloseHandle(FReadEvent);
  330.     CloseHandle(FReadFinishedEvent);
  331.   finally
  332.     Unlock;
  333.     CloseHandle(FWriteLock);
  334.   end;
  335.   inherited;
  336. end;
  337.  
  338. procedure TIBSQLMonitorHook.ResetStates;
  339. begin
  340.   SetEvent(FWriteEvent);
  341.   SetEvent(FWriteFinishedEvent);
  342. end;
  343.  
  344.  
  345. procedure TIBSQLMonitorHook.Lock;
  346. begin
  347.   WaitForSingleObject(FWriteLock, INFINITE);
  348. end;
  349.  
  350. procedure TIBSQLMonitorHook.Unlock;
  351. begin
  352.   ReleaseMutex(FWriteLock);
  353. end;
  354.  
  355. procedure TIBSQLMonitorHook.BeginWrite;
  356. begin
  357.   Lock;
  358. end;
  359.  
  360. procedure TIBSQLMonitorHook.EndWrite;
  361. begin
  362.   {
  363.    * 1. Wait to end the write until all registered readers have
  364.    *    started to wait for a write event
  365.    * 2. Block all of those waiting for the write to finish.
  366.    * 3. Block all of those waiting for all readers to finish.
  367.    * 4. Unblock all readers waiting for a write event.
  368.    * 5. Wait until all readers have finished reading.
  369.    * 6. Now, block all those waiting for a write event.
  370.    * 7. Unblock all readers waiting for a write to be finished.
  371.    * 8. Unlock the mutex.
  372.    }
  373.   while WaitForSingleObject(FReadEvent, cDefaultTimeout) = WAIT_TIMEOUT do begin
  374.     if FMonitorCount^ > 0 then
  375.       Dec(FMonitorCount^);
  376.     if (FReaderCount^ = FMonitorCount^ - 1) or (FMonitorCount^ = 0) then
  377.       SetEvent(FReadEvent);
  378.   end;
  379.   ResetEvent(FWriteFinishedEvent);
  380.   ResetEvent(FReadFinishedEvent);
  381.   SetEvent(FWriteEvent); { Let all readers pass through. }
  382.   while WaitForSingleObject(FReadFinishedEvent,
  383.           cDefaultTimeout) = WAIT_TIMEOUT do begin
  384.     if (FReaderCount^ = 0) or (InterlockedDecrement(FReaderCount^) = 0) then
  385.       SetEvent(FReadFinishedEvent);
  386.   end;
  387.   ResetEvent(FWriteEvent);
  388.   SetEvent(FWriteFinishedEvent);
  389.   Unlock;
  390. end;
  391.  
  392. procedure TIBSQLMonitorHook.BeginRead;
  393. begin
  394.   {
  395.    * 1. Wait for the "previous" write event to complete.
  396.    * 2. Increment the number of readers.
  397.    * 3. if the reader count is the number of interested readers, then
  398.    *    inform the system that all readers are ready.
  399.    * 4. Finally, wait for the FWriteEvent to signal.
  400.    }
  401.   WaitForSingleObject(FWriteFinishedEvent, INFINITE);
  402.   InterlockedIncrement(FReaderCount^);
  403.   if FReaderCount^ = FMonitorCount^ then
  404.     SetEvent(FReadEvent);
  405.   WaitForSingleObject(FWriteEvent, INFINITE);
  406. end;
  407.  
  408. procedure TIBSQLMonitorHook.EndRead;
  409. begin
  410.   if InterlockedDecrement(FReaderCount^) = 0 then
  411.     SetEvent(FReadFinishedEvent);
  412. end;
  413.  
  414. procedure TIBSQLMonitorHook.RegisterMonitor;
  415. begin
  416.   Lock;
  417.   try
  418.     Inc(FMonitorCount^);
  419.   finally
  420.     Unlock;
  421.   end;
  422. end;
  423.  
  424. procedure TIBSQLMonitorHook.UnregisterMonitor;
  425. begin
  426.   Lock;
  427.   try
  428.     Dec(FMonitorCount^);
  429.   finally
  430.     Unlock;
  431.   end;
  432. end;
  433.  
  434. function TIBSQLMonitorHook.MonitorCount: Integer;
  435. begin
  436.   Lock;
  437.   try
  438.     result := FMonitorCount^;
  439.   finally
  440.     Unlock;
  441.   end;
  442. end;
  443.  
  444. function TIBSQLMonitorHook.ReadSQLData(Arg: TIBCustomSQLMonitor): String;
  445. begin
  446.   BeginRead;
  447.   try
  448.     if TTraceFlag(FTraceDataType^) in Arg.TraceFlags then
  449.       SetString(result, FBuffer, FBufferSize^)
  450.     else
  451.       result := '';
  452.   finally
  453.     EndRead;
  454.   end;
  455. end;
  456.  
  457. procedure TIBSQLMonitorHook.SQLPrepare(qry: TIBSQL);
  458. var
  459.   st: String;
  460. begin
  461.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  462.     if not ((tfQPrepare in FTraceFlags) or (tfStmt in FTraceFlags)) then
  463.       Exit;
  464.     if qry.Owner is TIBDataSet then
  465.       st := TIBDataSet(qry.Owner).Name
  466.     else
  467.       st := qry.Name;
  468.     st := st + ': [Prepare] ' + qry.SQL.Text + CRLF; {do not localize}
  469.     st := st + '  Plan: ' + qry.Plan; {do not localize}
  470.     WriteSQLData(st, tfQPrepare);
  471.   end;
  472. end;
  473.  
  474. procedure TIBSQLMonitorHook.SQLExecute(qry: TIBSQL);
  475. var
  476.   st: String;
  477.   i: Integer;
  478. begin
  479.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  480.     if not ((tfQExecute in FTraceFlags) or (tfStmt in FTraceFlags)) then
  481.       Exit;
  482.     if qry.Owner is TIBDataSet then
  483.       st := TIBDataSet(qry.Owner).Name
  484.     else
  485.       st := qry.Name;
  486.     st := st + ': [Execute] ' + qry.SQL.Text; {do not localize}
  487.     if qry.Params.Count > 0 then begin
  488.       for i := 0 to qry.Params.Count - 1 do begin
  489.         st := st + CRLF + '  ' + qry.Params[i].Name + ' = '; 
  490.         try
  491.           if qry.Params[i].IsNull then
  492.             st := st + '<NULL>'; {do not localize}
  493.           st := st + qry.Params[i].AsString;
  494.         except
  495.           st := st + '<' + SCantPrintValue + '>';
  496.         end;
  497.       end;
  498.     end;
  499.     WriteSQLData(st, tfQExecute);
  500.   end;
  501. end;
  502.  
  503. procedure TIBSQLMonitorHook.SQLFetch(qry: TIBSQL);
  504. var
  505.   st: String;
  506. begin
  507.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  508.     if not ((tfQFetch in FTraceFlags) or (tfStmt in FTraceFlags)) then
  509.       Exit;
  510.     if qry.Owner is TIBDataSet then
  511.       st := TIBDataSet(qry.Owner).Name
  512.     else
  513.       st := qry.Name;
  514.     st := st + ': [Fetch] ' + qry.SQL.Text; {do not localize}
  515.     if (qry.EOF) then
  516.       st := st + CRLF + '  ' + SEOFReached;
  517.     WriteSQLData(st, tfQFetch);
  518.   end;
  519. end;
  520.  
  521. procedure TIBSQLMonitorHook.DBConnect(db: TIBDatabase);
  522. var
  523.   st: String;
  524. begin
  525.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  526.     if not (tfConnect in FTraceFlags) then
  527.       Exit;
  528.     st := db.Name + ': [Connect]'; {do not localize}
  529.     WriteSQLData(st, tfConnect) ;
  530.   end;
  531. end;
  532.  
  533. procedure TIBSQLMonitorHook.DBDisconnect(db: TIBDatabase);
  534. var
  535.   st: String;
  536. begin
  537.   if (Self = nil) then exit;
  538.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  539.     if not (tfConnect in FTraceFlags) then
  540.       Exit;
  541.     st := db.Name + ': [Disconnect]'; {do not localize}
  542.     WriteSQLData(st, tfConnect);
  543.   end;
  544. end;
  545.  
  546. procedure TIBSQLMonitorHook.TRStart(tr: TIBTransaction);
  547. var
  548.   st: String;
  549. begin
  550.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  551.     if not (tfTransact in FTraceFlags) then
  552.       Exit;
  553.     st := tr.Name + ': [Start transaction]'; {do not localize}
  554.     WriteSQLData(st, tfTransact);
  555.   end;
  556. end;
  557.  
  558. procedure TIBSQLMonitorHook.TRCommit(tr: TIBTransaction);
  559. var
  560.   st: String;
  561. begin
  562.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  563.     if not (tfTransact in FTraceFlags) then
  564.       Exit;
  565.     st := tr.Name + ': [Commit (Hard commit)]'; {do not localize}
  566.     WriteSQLData(st, tfTransact);
  567.   end;
  568. end;
  569.  
  570. procedure TIBSQLMonitorHook.TRCommitRetaining(tr: TIBTransaction);
  571. var
  572.   st: String;
  573. begin
  574.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  575.     if not (tfTransact in FTraceFlags) then
  576.       Exit;
  577.     st := tr.Name + ': [Commit retaining (Soft commit)]'; {do not localize}
  578.     WriteSQLData(st, tfTransact);
  579.   end;
  580. end;
  581.  
  582. procedure TIBSQLMonitorHook.TRRollback(tr: TIBTransaction);
  583. var
  584.   st: String;
  585. begin
  586.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  587.     if not (tfTransact in FTraceFlags) then
  588.       Exit;
  589.     st := tr.Name + ': [Rollback]'; {do not localize}
  590.     WriteSQLData(st, tfTransact);
  591.   end;
  592. end;
  593.  
  594. procedure TIBSQLMonitorHook.TRRollbackRetaining(tr: TIBTransaction);
  595. var
  596.   st: String;
  597. begin
  598.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  599.     if not (tfTransact in FTraceFlags) then
  600.       Exit;
  601.     st := tr.Name + ': [Rollback retaining (Soft rollback)]'; {do not localize}
  602.     WriteSQLData(st, tfTransact);
  603.   end;
  604. end;
  605.  
  606. procedure TIBSQLMonitorHook.ServiceAttach(service: TIBCustomService);
  607. var
  608.   st: String;
  609. begin
  610.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  611.     if not (tfService in FTraceFlags) then
  612.       Exit;
  613.     st := service.Name + ': [Attach]'; {do not localize}
  614.     WriteSQLData(st, tfService);
  615.   end;
  616. end;
  617.  
  618. procedure TIBSQLMonitorHook.ServiceDetach(service: TIBCustomService);
  619. var
  620.   st: String;
  621. begin
  622.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  623.     if not (tfService in FTraceFlags) then
  624.       Exit;
  625.     st := service.Name + ': [Detach]'; {do not localize}
  626.     WriteSQLData(st, tfService);
  627.   end;
  628. end;
  629.  
  630. procedure TIBSQLMonitorHook.ServiceQuery(service: TIBCustomService);
  631. var
  632.   st: String;
  633. begin
  634.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  635.     if not (tfService in FTraceFlags) then
  636.       Exit;
  637.     st := service.Name + ': [Query]'; {do not localize}
  638.     WriteSQLData(st, tfService);
  639.   end;
  640. end;
  641.  
  642. procedure TIBSQLMonitorHook.ServiceStart(service: TIBCustomService);
  643. var
  644.   st: String;
  645. begin
  646.   if bMonitoringEnabled and (MonitorCount > 0)then begin
  647.     if not (tfService in FTraceFlags) then
  648.       Exit;
  649.     st := service.Name + ': [Start]'; {do not localize}
  650.     WriteSQLData(st, tfService);
  651.   end;
  652. end;
  653.  
  654. procedure TIBSQLMonitorHook.WriteSQLData(Text: String; DataType: TTraceFlag);
  655. var
  656.   i, len: Integer;
  657.  
  658. begin
  659.   Text := CRLF + '[Application: ' + Application.Title + ']' + CRLF + Text; {do not localize}
  660.   Lock;
  661.   try
  662.     i := 1;
  663.     len := Length(Text);
  664.     while (len > 0) do begin
  665.       BeginWrite;
  666.       try
  667.         FTraceDataType^ := Integer(DataType);
  668.         FBufferSize^ := Min(len, cMaxBufferSize);
  669.         Move(Text[i], FBuffer[0], FBufferSize^);
  670.         Inc(i, cMaxBufferSize);
  671.         Dec(len, cMaxBufferSize);
  672.       finally
  673.         EndWrite;
  674.       end;
  675.     end;
  676.   finally
  677.     Unlock;
  678.   end;
  679. end;
  680.  
  681. var
  682.   _MonitorHook: TIBSQLMonitorHook;
  683.   bDone: Boolean;
  684.  
  685. function MonitorHook: TIBSQLMonitorHook;
  686. begin
  687.   if (_MonitorHook = nil) and (not bDone) then
  688.     _MonitorHook := TIBSQLMonitorHook.Create;
  689.   result := _MonitorHook;
  690. end;
  691.  
  692. initialization
  693.  
  694.   bMonitoringEnabled := True;
  695.   _MonitorHook := nil;
  696.   bDone := False;
  697.  
  698. finalization
  699.  
  700.   bDone := True;
  701.   _MonitorHook.Free;
  702.   _MonitorHook := nil;
  703.  
  704. end.
  705.