Join 34,000+ subscribers and receive articles from our blog about software quality, testing, QA and security.
 

Create object TSmartInspect at runtime


#1

I want to create the smartinspect object at the run time on one method that is called at fixed interval. For the first execution it is ok , but next execution throw exception.

The code follow the pattern :

try
   CreateSmartInspect(connectionTCP, 'DemoServer', SI1, SIMain1);
…. Log the mesage
finally
    FreeSmartInspect(SI1);
end;

where 

Procedure TDemoServer.CreateSmartInspect(Connection: String; SessionName: String; Var ASI: TSmartInspect; Var ASIMain: TSiSession);
Var retVal: TSmartInspect;
Begin
  If ASI <> Nil Then Begin
    ASI.Free;
  End;

  ASI := TSmartInspect.Create(Application.Title);
  ASI.Connections := Connection;
  ASI.OnError := ExceptionHandler;
  ASIMain := ASI.AddSession(SessionName);
  ASI.Enabled := true;

End;

Procedure TDemoServer.FreeSmartInspect(Var ASI: TSmartInspect);
Begin
  If ASI <> Nil Then Begin
    ASI.OnError := Nil;
    ASI.Enabled := false;
    ASI.Free;
  End;


End;

At second execution it is throw AV exception
Note that this error is not raise if I using the default SmartInspect SI object(but I need separate log connection and message pattern)

I used the 2.3.4.7144 version (I think the last available from download)

It is you code bug or the bug is in my square :frowning: ?

Thanks
Aurelian


#2

Hello Aurelian,

Thanks for your posting. I tried your code but unfortunately could not reproduce this problem (i.e. it does not throw an AV here). I have a few questions:

At which interval do you call the CreateSmartInspect routine and which component do you use for this (TTimer?)?
In which line does the AV occur?
Do you use the SI1 variable in other threads while freeing/creating the TSmartInspect objects?


#3

Thanks for you response, I put all the code here
The form contain 2 buttons


Unit SmartInspectAdv;

Interface

Uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls,
  SmartInspect, SiAuto;

Type
  TFrmSIAdv = Class(TForm)
    Panel1: TPanel;
    Memo1: TMemo;
    btn2SI: TButton;
    btnSIMain: TButton;
    Procedure btn2SIClick(Sender: TObject);
    Procedure btnSIMainClick(Sender: TObject);
 Private
    { Private declarations }
  Public
    { Public declarations }
    Procedure ExceptionHandler(ASender: TSmartInspect; AException: Exception);
    Procedure CreateSmartInspect(Connection: String; SessionName: String; Var ASI: TSmartInspect; Var ASIMain: TSiSession);
    Procedure FreeSmartInspect(Var ASI: TSmartInspect);

  End;

Var
  FrmSIAdv: TFrmSIAdv;

Implementation
{$R *.dfm}


Var SI1, SI2: TSmartInspect;
  SIMain1, SIMain2: TSiSession;
Const
  connectionTCP: String = 'tcp(host="localhost")';
  connectionTCP_FILE: String = 'file(filename="log.sil", append=true),tcp(host="localhost")';

  TEN = 10;

//ExceptionHandler display error on Memo
Procedure TFrmSIAdv.ExceptionHandler(ASender: TSmartInspect; AException: Exception);
Begin
  Memo1.Lines.Add(AException.Message);
  Memo1.Lines.Add('Error Receive with  ...' + TSmartInspect(ASender).Version);
End;

Procedure TFrmSIAdv.CreateSmartInspect(Connection: String; SessionName: String; Var ASI: TSmartInspect; Var ASIMain: TSiSession);
Begin
  If ASI <> Nil Then Begin
    ASI.Free;
  End;

  ASI := TSmartInspect.Create(Application.Title);
  ASI.Connections := Connection;
  ASI.OnError := ExceptionHandler;
  ASIMain := ASI.AddSession(SessionName);
  ASI.Enabled := true;

End;

Procedure TFrmSIAdv.FreeSmartInspect(Var ASI: TSmartInspect);
Begin
  If ASI <> Nil Then Begin
    ASI.OnError := Nil;
    ASI.Enabled := false;
    ASI.Free;
  End;


End;

Procedure TFrmSIAdv.btn2SIClick(Sender: TObject);
Var i, j: integer;
Begin
 // Create 2 TSmartInspect Object that send date to the same TCP Connection
  CreateSmartInspect(connectionTCP, 'Main01', SI1, SIMain1);
  CreateSmartInspect(connectionTCP, 'Main02', SI2, SIMain2);

 // CreateSmartInspect(connectionTCP_FILE,SI1,SIMain1);
 // CreateSmartInspect(connectionTCP_FILE,SI2,SIMain2);


  SIMain1.Color := clCream;
  SIMain2.Color := clYellow;

  SiMain1.EnterMethod(Self, 'btn2SIClick');
  SiMain2.EnterMethod(Self, 'btn2SIClick');
  Try

    For i := 1 To TEN Do
    Begin
      Memo1.Lines.Add('Step ' + intToStr(i));
      For j := 1 To TEN Do
      Begin
        SIMain1.LogDebug('Main_1 %4d %4d', [i, j]);
        SIMain1.WatchInteger('Internal_1 I ', i);
        SIMain1.WatchInteger('Internal_1 J ', j);
        Sleep(10);
        SIMain2.LogDebug('Main_2 %4d %4d', [i, j]);
        SIMain1.WatchInteger('Internal_2 I ', i);
        SIMain1.WatchInteger('Internal_2 J ', j);

        Sleep(10);

      End;
      SIMain.LogSeparator;

    End;
  Finally
    SiMain1.LeaveMethod(Self, 'btn2SIClick');
    SiMain2.LeaveMethod(Self, 'btn2SIClick');
  End;

 //Free the connection
  FreeSmartInspect(SI1);
  FreeSmartInspect(SI2);

End;

//use SI and SIMain
Procedure TFrmSIAdv.btnSIMainClick(Sender: TObject);
Var i, j: integer;
Begin
  SIMain.Color := clTeal;

  For i := 1 To TEN Do
  Begin
    Memo1.Lines.Add('Step ' + intToStr(i));
    For j := 1 To TEN Do
    Begin
      SIMain.LogDebug('Main_1 %4d %4d', [i, j]);

    End;
  End;

End;



End.

If click twice on the btn2SI button you see the AV.


#4

I was able to reproduce the problem. The problem is that you call Free twice on the same TSmartInspect object, once in FreeSmartInspect on the first run and then the second time in CreateSmartInspect. This then results in the Access Violation.

In most cases, it’s a good idea to use FreeAndNil instead of the Free method directly because this also sets the Pointer/Reference to nil and thus avoids the ‘dangling pointer’ problem (a reference to an object which is no longer valid). So, one fix would be to replace the ASI.Free calls with FreeAndNil(ASI). It works fine then.


#5

Yap, you right,

Thanks
Aurelian