Solved

Help with comms and threads

Posted on 2004-09-29
21
656 Views
Last Modified: 2010-05-18
OK I have an app which reads data from a serial port processes the data and puts it in a database. While the infromation is recieived pretty slowly (150) millisecs, all is fine. But when info is recieved quickly, Oh dear. I assume that there is a problem with the app trying to process multiple lines. I  tried to get my head around delphi threads but it is not going in. can some help point me in the right direction using my code below.


//The following reads the data from the serial port and then sends the result to the ProcessSearch procedure. I think from here I need to set up some kind of //threading that would pass the data when ProcessSearch is free.
{******************************************************
Read characters for serial port and send string when
EolnChar is recieved.
*******************************************************}
procedure TMainForm.nrComm1AfterReceive(Com: TObject; Buffer: Pointer;
  Received: Cardinal);
var i:integer;
    ch:Char;
begin
  for i:=0 to Received-1 do begin
    ch:=PChar(Buffer)[i];
    if ch=#13 then continue
    else
    if ch=#10 then begin
       StrArg[Index]:=TmpStr;
       TmpStr:='';
       Inc(index);
       if Index>1 then begin
         ProcessSearch(StrArg[0],StrArg[1]);
         Index:=0;
         TmpStr:='';
       end;
    end else TmpStr:=TmpStr+ch;
  end;
end;


{*****************************************************
Processing call data information from the
Serial Port
******************************************************}

procedure TMainForm.ProcessSearch(S1,S2 : String);
var
CallInsert : Pchar;


Begin
With CollexDB do
Begin
CollexDB := TliteDB.Create(self, 'collex.sdb');
end;
        Edit1.Clear;

  group_no := GrpNum(StrArg[0],StrArg[1]); //some stuff accessing a dll
  start_no := StartNum(StrArg[0],StrArg[1]); //some stuff accessing a dll
  ExtVar := PhoneNum(StrArg[0],StrArg[1]);
  Exten := ExtNum(StrArg[0],StrArg[1]);
  CallDay := DateOfCall(StrArg[0],StrArg[1]);
  direction := CallDir(StrArg[0],StrArg[1]);
  trans := Transfer(StrArg[0],StrArg[1]);
  extn_no := ExtNum(StrArg[0],StrArg[1]);
  trunk_no := TrunkNum(StrArg[0],StrArg[1]);
  ddi := 'None';
  dest := PhoneNum(StrArg[0],StrArg[1]);
  SQLDuration := SQLLenofCAll(StrArg[0],StrArg[1]);
  ring_time := RingLen(StrArg[0],StrArg[1]);
  call_time := SQLDateOfCall(StrArg[0],StrArg[1])+' '+TimeOfCall(StrArg[0],StrArg[1]);
  CallTime := TimeOfCall(StrArg[0],StrArg[1]);
  CallDate := SQLDateOfCall(StrArg[0],StrArg[1]);
  Duration := LenOfCall(StrArg[0],StrArg[1]);
  SqlFileDate := ExtractFilePath(Application.ExeName)+FormatDateTime('mmyyyy".sql', Now);


        if  direction = 'Out' then begin
                if copy(dest,1,StrLen(Pchar(LcrAccess))) = LcrAccess then
                dest := copy(dest,1+StrLen(PChar(LcrAccess)),StrLen(PChar(dest)));

        CollexDB.DoQuery('select areacode, band, destination from stdcodes where areacode = (SELECT MAX(areacode) FROM stdcodes WHERE '''+dest+''' LIKE areacode || ''%'')');
        //if there is a match, run calculations
                if  CollexDB.RowCount > 0 then
                Begin
                TimeOfCall1 := StrToTime(CallTime);
                DayBand := DayOfWeek(StrToDate(CallDay));
                LB_query.Lines.Add(CollexDB.Results[0][0]+': '+CollexDB.Results[0][1]);
                band := CollexDB.Results[0][1];
                dest_name := CollexDB.Results[0][2];
                CallBand := CheckCallBand(Callday, Calltime ); {uses CheckCallBand function}
                ChargeTotal :=  CallChargeFunc(CallBand, Band, trunk_no);
                // insert call data into database
                CallInsert:=PChar('Insert into call_data (direction, extn_no, trunk_no, ddi, dest, dest_name, duration, call_time, cost, band, site_id) values ('''+direction+''', '''+extn_no+''', '''+trunk_no+''', '''+ddi+''', '''+dest+''', '''+dest_name+''', '''+SQLduration+''', '''+call_time+''', '''+FloatToStrF(ChargeTotal, ffFixed, 10, 2)+''', '''+band+''', '''+SiteNum+''')');
                CollexDB.Query(CallInsert);
                // Show SQL inserts
                end;
           end;

        end;
CollexDB.Free;
StrArg[0] := '';
StrArg[1] := '';
  end;



{*****************************************************
Function For checking the time of day the band
calculation uses.
******************************************************}
function TMainForm.CheckCallBand (WhenDay, WhenTime : String) : String;
    var
    WhendayInt : Integer;
    WhenTimeInt : TDateTime;

    begin {CheckCallBand }
    WhenDayInt := DayOfWeek(StrToDate(WhenDay));
    WhentimeInt := StrToTime(WhenTime);
//Check wether weekend
        If (WhenDayInt = 1) or (WhenDayInt = 7) then
        begin
        result := 'we_end';
        end else
        // If not weekend, is it day or night
        begin
                If (TimeBandStart <= WhentimeInt) and (TimeBandEnd >= WhentimeInt) then Begin
                result := 'day';
                end
                else begin
                result:= 'night';
                end;
        end;
end {CheckCallBand };


{*****************************************************
Function for calculating the cost of a call
******************************************************}
function TMainForm.CallChargeFunc (CallBandFunc, BandFunc, TrunkFunc : String) : Real;
Var
Charge : Real;
QueryCharge : Pchar;
TrunkCharge : Pchar;
begin {callChargeFunc}
      QueryCharge := Pchar( 'SELECT '+CallBandFunc+', min, max, fixed, min_bill FROM chargecode WHERE band = '''+BandFunc+'''');
        CollexDB.Query(QueryCharge);
        CollexDB.Results[0].Format[0].AsFloat;
      Charge := Duration*CollexDB.Results[0].Format[0].AsFloat;
                Result := Charge + CollexDB.Results[0].Format[3].AsFloat;
            If charge <= CollexDB.Results[0].Format[1].AsFloat then
                   Charge := CollexDB.Results[0].Format[1].AsFloat;

                 If charge >= CollexDB.Results[0].Format[2].AsFloat then
                   Charge := CollexDB.Results[0].Format[2].AsFloat;

      Result := Charge + CollexDB.Results[0].Format[3].AsFloat;

           TrunkCharge := Pchar( 'SELECT network FROM trunks WHERE trunk_no = '''+trunk_no+'''');
           CollexDB.Query(TrunkCharge);

                if Duration = 0 then
                result := 0.00;

                if CollexDB.results[0][0] = 'Yes' then
                  Result := 0.00;
        //CollexDB.Free
end {CallChargeFunc};

Any Help will be appreciated.

0
Comment
Question by:lloydie-t
  • 11
  • 10
21 Comments
 
LVL 12

Expert Comment

by:Ivanov_G
Comment Utility
Does the order in receiving data matters... ?

For example you have 2 threads - the first one receive "1" and the second "2". If the second is faster - "2" will be written in the DB before "1". Is this a problem ?

Another issue - check if you have trigger / index for the DB table where you insert. If so, this can slow down the process...

When do you commit the changes? After each insert or after the final insert ?

0
 

Author Comment

by:lloydie-t
Comment Utility
The data is sent one at a time, so there should be no instances where thread 1 is processed before thread 2.
The database has no trigger and the index is created automatically during insert.
I am using a component to connect to the SQlite database so I am not exactly sure if it uses 'begin commit' but I assume it does, but changes do seem to be commited after each insert.
0
 
LVL 12

Expert Comment

by:Ivanov_G
Comment Utility
> index is created automatically during insert

I am not familiar with SQlite, but probably the index is created manually. I don't imagine how the DB can guess which table field should be indexed ... ?
0
 

Author Comment

by:lloydie-t
Comment Utility
I apologise, I miss understood your question.
sqlite automtically creates its own index field, But you can have your own, In my case it is call_id.
0
 
LVL 12

Expert Comment

by:Ivanov_G
Comment Utility
Since I can not reproduce exactly your environment, can you tell me something more about the problem you have, some error messages, exceptions, etc...
0
 

Author Comment

by:lloydie-t
Comment Utility
I do not have a problem yet, I just want to know the best way I can go about implementing threading in my app.

nrComm1AfterReceive :- collects the data from the serial port and sends the resulting var to the ProcessSearch procedure
ProcessSearch:- processes the string through a DLL and then uses the results to make calculations using  CallBand and ChargeTotal procedures. Once this is done then the results are inserted into a database.

What I want to do is to make sure that when nrComm1AfterReceive sends the data to ProcessSearch that it is threaded so that any othe data send is queued until processsearch is free to process more data.
0
 
LVL 12

Expert Comment

by:Ivanov_G
Comment Utility
I would suggest you the following thing...

You have one thread, which reads data and process them via the DLL. I will try to describe it with mnemo code: ( I am writing it by memory, so take just the idea from it)

  TReadThread = class(TThread)
  private
    ComPort : ....
    DLL_Handle
    procedure Execute; override;
  public
    constructor Create (ComPort : ....; DestDLL : String);
  end;

  constructor TReadThread.Create (ComPortParam : ....; DestDLL : String);
  begin
    inherited Create(False);
    ComPort := ComPortParam;
    // load the DLL below
    DLL_Handle := LoadLibrary(DestDLL);
  end;

  procedure TReadThread.Execute;
  begin
    // read data from ComPort
   
    // process the via DLL

    // store them in TEMP storage
  end;

  Now about this  "// store them in TEMP storage". The idea is similar to the real-time billing systems. They use the DB as backend. First they use cashing methods and after some time they commit in the database. So this temp storage can be commited in the DB at one. Inserting permanently in the DB is heavy operation...

  You can store your data in TClientDataSet for example. And let's say you have TTimer being activated on 2 minutes. Once the timer is activated, the reading thread is suspended and you insert the data in DB. After it is inserted call Resume method of the thread do resume the thread and start reading data again...
0
 

Author Comment

by:lloydie-t
Comment Utility
Thanks for that. I'm afraid I need a bit more help. The DLL I am using is a delphi DLL, So do iI still need to load the library? Before I just had the folowing DLL functions.

Function GrpNum (S1,S2: String): String; stdcall; external 'collexconv.dll';
Function StartNum (S1,S2: String): String; stdcall; external 'collexconv.dll';
Function ExtNum (S1,S2: String): String; stdcall; external 'collexconv.dll';
Function TrunkNum (S1,S2: String): String; stdcall; external 'collexconv.dll';


The comport has some parameters which need to be set. Previously I read these from an ini file on FormCreate. Can I still do this?

  PortReadINI := TINIFile.Create(ExtractFilePath(Application.ExeName) + 'callcollex.ini');
  nrComm1.BaudRate :=  PortReadINI.Readinteger(ComPortSet, 'Speed', 9600);
  nrComm1.ComPortNo := PortReadINI.Readinteger(ComPortSet, 'ComNumber', 1);
  nrComm1.Parity := Tparity(PortReadINI.Readinteger(ComPortSet, 'Parity', 0));
  nrComm1.ByteSize := PortReadINI.Readinteger(ComPortSet, 'Databits', 8);
  nrComm1.StopBits := TStopbits(PortReadINI.Readinteger(ComPortSet, 'Stopbits', 0));
  nrcomm1.StreamProtocol:=TStreamProtocol(PortReadINI.ReadInteger(ComPortSet, 'Flow',0));
  PortReadINI.Destroy;

I also have some other forms which are used for changing the parameters of the comport.
The comport component I am using is not the one which comes with delphi, but thrid part(nrComm).
I am not sure how to use the component. Do I still have to drop the component on the form or does the create constructor do that?


0
 
LVL 12

Expert Comment

by:Ivanov_G
Comment Utility
about the DLL:

declared like this, the DLL is linked statically. So you don't have to load it. All you have to do is to copy the DLL in the target machine, in system32 for e.g. That's all, you don't have to use LoadLibrary and GetProcAddress. You code till here is OK.

About the ComPort options... here I see 2 ways to implement it:
1) build the string INI_FILE_NAME := ExtractFilePath(Application.ExeName) + 'callcollex.ini'; and then pass INI_FILE_NAME in the thread. From inside the thread you can create nrComm1 component and load the settings like you do now. Somewhere in the constructor
2) create nrComm1 outside the thread and pass it as a parameter to the thread... (this is useful if you are going to use the same component in both the thread and your application)
0
 

Author Comment

by:lloydie-t
Comment Utility
I am still confused, I know nothing of delphi Tthread all though I am reading. Please have a look at the following. I am not sure what to do with the comport component. Currently I use the AfterReceive procedure to process the data. Looking at the nrComm class, AfterReceive = nrComm1AfterReceive  and the actual procedure = procedure TMainForm.nrComm1AfterReceive(Com: TObject; Buffer: Pointer; Received: Cardinal); How would I use this in TReadThread?

TReadThread = class(TThread)
  private
    GrpNum, StartNum, ExtNum, TrunkNum : String;  
  Protected
    procedure Execute; override;
    procedure InsertSQL;
  public
     constructor Create (ComPort : ....);
  end;

  constructor TReadThread.Create (ComPortParam : ....;);
  begin
    inherited Create(False);
    ComPort := ComPortParam; //not sure what to do
  end;

  procedure TReadThread.Execute;
var
  nrComm1AfterReceive := TMainForm.nrComm1.AfterReceive
 begin
      // read data from ComPort
       //Not sure what to do here  
   
    // process the via DLL
       group_no := GrpNum (S1,S2: String);
       Start_no := StartNum (S1,S2: String);
       Extn_no:= ExtNum (S1,S2: String);
       trunk_no := TrunkNum (S1,S2: String);


    // Insert into database
Synchronize(InsertSQL);
  end;

procedure TReadThread.InsertSQL;

begin
With CollexDB do
Begin
CollexDB := TliteDB.Create(self, 'collex.sdb');
end;

CallInsert:=PChar('Insert into call_data (group_no, start_no, extn_no, trunk_no) values ('''+group_no+''', '''+start_no+''', '''+extn_no+''', '''+trunk_no+''')');
CollexDB.Query(CallInsert);

CollexDB.Free;
end;

I am going to have to up the points as this is important to me.


0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 12

Expert Comment

by:Ivanov_G
Comment Utility
TReadThread = class(TThread)
  private
    GrpNum, StartNum, ExtNum, TrunkNum : String;  
    procedure OnCustomAfterReceive(Com: TObject; Buffer: Pointer; Received: Cardinal);
....
  constructor TReadThread.Create (ComPortParam : ....;);
  begin
    inherited Create(False);
    ComPort := ComPortParam; //not sure what to do - the idea here is to send the ComPort component as a parameter inside the thread. The other way is to create it here... look at my suggestions 1) and 2) above... This here is 2)
    ComPort.OnAfterReceive := OnCustomAfterReceive;
  end;

  procedure TReadThread.OnCustomAfterReceive(Com: TObject; Buffer: Pointer; Received: Cardinal);
  begin
    // your onAfterReceive code here...
  end;

0
 

Author Comment

by:lloydie-t
Comment Utility
I Still don't understand the concept of using TThreads. Is it possible to give me a bit more of an example based on the following.

procedure ProcessSearch(S1,S2 : String);


type
 TReadThread = class(TThread)
  private
    procedure OnCustomAfterReceive(Com: TObject; Buffer: Pointer; Received: Cardinal);
    procedure Execute; override;
  public
    constructor Create (ComPort : TnrComm);
  end;

 implemetation

constructor TReadThread.Create (nrComm1 : Tnrcomm1;);
  begin
    inherited Create(False);
    ComPort := nrComm1; // i have done something wrong here, but not sure. The existing name for the TnrComm component is nrComm1
    ComPort.OnAfterReceive := OnCustomAfterReceive;
  end;

  procedure TReadThread.OnCustomAfterReceive(Com: TObject; Buffer: Pointer; Received: Cardinal);
var i:integer;
    ch:Char;
begin
  for i:=0 to Received-1 do begin
    ch:=PChar(Buffer)[i];
    if ch=#13 then continue
    else
    if ch=#10 then begin
       StrArg[Index]:=TmpStr;
       TmpStr:='';
       Inc(index);
       if Index>1 then begin
              ProcessSearch(StrArg[0],StrArg[1]);
         Index:=0;
         TmpStr:='';
       end;
    end else TmpStr:=TmpStr+ch;
  end;
             end;


procedure TMainForm.ProcessSearch(S1,S2 : String);
begin
  do something...............;
end;

I have increased the points as it  is a boit more difficult for me.
0
 
LVL 12

Accepted Solution

by:
Ivanov_G earned 500 total points
Comment Utility
> I Still don't understand the concept of using TThreads.

The concept of Threads is to have multithreaded application. For example if you click a button, you can fire up 2(or more) threads which are doing different things. They run simultaneously - at the same time. Thus you can speed up actions you have to do from your application.

> constructor Create (ComPort : TnrComm);

What you did seems to be OK. You have to pass all the objects, parameters you need to use in the thread via the constructor, because Execute method don't have parameters - It works with local (for the thread) object. For example if you want to modify a DataSet, you send in Create method, declare local DataSet in the thread and assign them. So in Execute method you access the local one.

So your code with come modifications should look like this:

TReadThread = class(TThread)
  private
    local_ComPort : TnrComm;
    procedure OnCustomAfterReceive(Com: TObject; Buffer: Pointer; Received: Cardinal);
    procedure Execute; override;
  public
    IsFinished : Boolean; // indicate if the thread is running
    constructor Create (ComPort : TnrComm);
  end;

  constructor TReadThread.Create (nrComm1 : Tnrcomm1;);
  begin
    inherited Create(False);
    local_ComPort := nrComm1; // take the ComPort locally
    local_ComPort.OnAfterReceive := OnCustomAfterReceive;
    IsFinished := False;
  end;

  Some more notes:
> inherited Create(False);

The False parameter is called "CreateSuspended". If it is TRUE, the thread is created suspended and its execution start when you call Resume method. If it is False - the is created and after that the Execute method is called automatically. So If you want to activate the ComPort immediatelly after creation - you can make something like this:

procedure TReadThread.Execute;
begin
  // here you access only objects inside the thread, like local_ComPort
  local_ComPort.Active := True; // I am not sure if you have this property

  // So something here...

  IsFinished := True; // indicate the thread have finished
end;
0
 

Author Comment

by:lloydie-t
Comment Utility
Thanks for that, it is a good start, but I am getting errors as follows:

[Hint] datacollex.pas(162): Overriding virtual method 'TReadThread.Execute' has lower visibility (private) than base class 'TThread' (protected)
[Error] datacollex.pas(713): Undeclared identifier: 'ProcessSearch'
[Fatal Error] datacollex1.dpr(10): Could not compile used unit 'datacollex.pas'

This is the code so far.

 type
TReadThread = class(TThread)
  private
    local_ComPort : TnrComm;
    procedure OnCustomAfterReceive(Com: TObject; Buffer: Pointer; Received: Cardinal);
    procedure Execute; override;
  public
    IsFinished : Boolean; // indicate if the thread is running
    constructor Create (nrComm1 : TnrComm);
  end;

implementation
{$R *.dfm}
uses

OptionSetup, SelectPort, Aboutbox;

  constructor TReadThread.Create (nrComm1 : Tnrcomm);
  begin
    inherited Create(False);
    local_ComPort := nrComm1; // take the ComPort locally
    local_ComPort.OnAfterReceive := OnCustomAfterReceive;
    IsFinished := False;
  end;

.............
........
............

procedure TReadThread.Execute;
begin
local_ComPort.Active := True;
IsFinished := True; // indicate the thread have finished
end;


procedure TMainForm.ProcessSearch(S1,S2 : String);
begin
  do something...............;
end;


procedure TReadThread.OnCustomAfterReceive(Com: TObject; Buffer: Pointer; Received: Cardinal);
var i:integer;
    ch:Char;
begin
   for i:=0 to Received-1 do begin
    ch:=PChar(Buffer)[i];
    if ch=#13 then continue
    else
    if ch=#10 then begin
       StrArg[Index]:=TmpStr;
       TmpStr:='';
       Inc(index);
       if Index>1 then begin
         ProcessSearch(StrArg[0],StrArg[1]); // there is a problem here
         Index:=0;
         TmpStr:='';
       end;
    end else TmpStr:=TmpStr+ch;
  end;
  IsFinished := True; // indicate the thread have finished
end;

0
 

Author Comment

by:lloydie-t
Comment Utility
Fixed the processSearch error by doing:
Mainform.ProcessSearch(StrArg[0],StrArg[1]);
Will that affect the thread?

procedure TReadThread.Execute;
begin
local_ComPort.Active := True;
  // do I need to call OnCustomAfterReceive and if so how?
IsFinished := True;
end;
 
Does the thread wait for that procedure to complete before it continues?
 The only error left is:
[Hint] datacollex.pas(162): Overriding virtual method 'TReadThread.Execute' has lower visibility (private) than base class 'TThread' (protected)
Is this a real problem? I dont really understand what the potential problem is.
0
 
LVL 12

Expert Comment

by:Ivanov_G
Comment Utility
> [Hint] datacollex.pas(162): Overriding virtual method 'TReadThread.Execute' has lower visibility (private)
> than base class 'TThread' (protected)

put the execute method in protected section:
  TReadThread = class(TThread)
  private
    local_ComPort : TObject;
    procedure OnCustomAfterReceive(Com: TObject; Buffer: Pointer; Received: Cardinal);
  protected
    procedure Execute; override;   // <--- HERE
  public
    IsFinished : Boolean;
    constructor Create (nrComm1 : TObject);
  end;

> [Error] datacollex.pas(713): Undeclared identifier: 'ProcessSearch'
procedure TMainForm.ProcessSearch(S1,S2 : String);
why TMainForm (I guess Copy/Paste), it should be

procedure TReadThread.ProcessSearch(S1,S2 : String);
0
 
LVL 12

Expert Comment

by:Ivanov_G
Comment Utility
after your create the thread, in the constructor it sais:
  local_ComPort.OnAfterReceive := OnCustomAfterReceive;
thus, OnAfterReceive event of your COM - OnCustomAfterReceive is executed


procedure TReadThread.Execute;
begin
  local_ComPort.Active := True;
  // do I need to call OnCustomAfterReceive and if so how?

  // do, you don't. You already have assigned the event handler, so once you activate the ComPort component,
  // OnCustomAfterReceive will be fired up. You have just to activate the comport component
  // I suppose there is something like Active property...

  IsFinished := True;
end;
0
 

Author Comment

by:lloydie-t
Comment Utility
Sorry been away. I hope this is the last question.

I am getting an error trying to use a database component in the thread, which is probably due to me going the wrong way about it.
The error I get is as follows
Incompatible types 'TComponent' and 'TReadThread'

This is where I am roughly

code--------------------------------------------------------------------------------
 
type
TReadThread = class(TThread)
  private
    local_ComPort : TnrComm;
    ThreadCollexDB : TliteDB;
    procedure OnCustomAfterReceive(Com: TObject; Buffer: Pointer; Received: Cardinal);
    procedure ProcessSearch(S1,S2 : String);
  protected
    procedure Execute; override;
  public
    IsFinished : Boolean; // indicate if the thread is running
    constructor Create (nrComm1 : TnrComm; CollexDB : TliteDB);
  end;


implementation
{$R *.dfm}

  constructor TReadThread.Create (nrComm1 : Tnrcomm; CollexDB : TliteDB);
  begin
    inherited Create(False);
    local_ComPort := nrComm1; // take the ComPort locally
    ThreadCollexDB := CollexDB;
    local_ComPort.OnAfterReceive := OnCustomAfterReceive;
    IsFinished := False;
  end;


procedure TReadThread.Execute;
begin
local_ComPort.Active := True;
IsFinished := True;
end;

procedure TReadThread.OnCustomAfterReceive(Com: TObject; Buffer: Pointer; Received: Cardinal);
var i:integer;
    ch:Char;
begin
   for i:=0 to Received-1 do begin
    ch:=PChar(Buffer)[i];
    if ch=#13 then continue
    else
    if ch=#10 then begin
       StrArg[Index]:=TmpStr;
       TmpStr:='';
       Inc(index);
       if Index>1 then begin
         //ProcessSearch(StrArg[0],StrArg[1]);
         ProcessSearch(StrArg[0],StrArg[1]);
         Index:=0;
         TmpStr:='';
       end;
    end else TmpStr:=TmpStr+ch;
  end;
end;


procedure TReadThread.ProcessSearch(S1,S2 : String);
var
CallInsert : Pchar;

Begin
With ThreadCollexDB do
Begin
ThreadCollexDB := TliteDB.Create(self, 'collex.sdb'); // This is where it goes wrong.
end;

MainForm.IncomingDataEdit.Lines.Add(StrArg[0]);
mainForm.IncomingDataEdit.Lines.Add(StrArg[1]);
...........................
................................
end;


0
 
LVL 12

Expert Comment

by:Ivanov_G
Comment Utility
Aaaaa, I see the problem

ThreadCollexDB := TliteDB.Create(self, 'collex.sdb');

The self is problematic. In Delphi variables as Self, Application are "transparent". If you use Self in Form1 - then Self is the form itself. If you use it in thread - then the Thread is Self. So it could be something like:

ThreadCollexDB := TliteDB.Create(nil, 'collex.sdb');
try
  with ThreadCollexDB do
    begin
       // USE YOUR DB Connection Here...
    end;
finally
  ThreadCollexDB.Free;
end;
0
 

Author Comment

by:lloydie-t
Comment Utility
I have just tested it and it works. Thankyou for all your help. I now have a better understanding of how delphi  threads work.
0
 
LVL 12

Expert Comment

by:Ivanov_G
Comment Utility
welcome :)
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now