Marius0188
asked on
Delphi 7 & Indy 10 OnCommand Event and VCL Components Thread Safe?
Dear Experts,
I am using Delphi 7 and Indy 10 components.
In the TidCommandHandler component I have created several commands and in their OnCommand events I have code that updates VCL components.
If I am not mistaken the events of the TidCommandHandler's commands occurs in a Threads handled by Indy itself. Do I need to take any special caution when updating VCL components in these events? I experience some problem errors, "EOSERROR with Code 1400, invalid window handle" and I am sure it is because I am not updating the VCL in a thread safe manner.
Can you please help me and show me how should I update VCL components in a safe manner in these events?
Thanks in advance!
I am using Delphi 7 and Indy 10 components.
In the TidCommandHandler component I have created several commands and in their OnCommand events I have code that updates VCL components.
If I am not mistaken the events of the TidCommandHandler's commands occurs in a Threads handled by Indy itself. Do I need to take any special caution when updating VCL components in these events? I experience some problem errors, "EOSERROR with Code 1400, invalid window handle" and I am sure it is because I am not updating the VCL in a thread safe manner.
Can you please help me and show me how should I update VCL components in a safe manner in these events?
Thanks in advance!
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
I have same problem experts, please help us. I will open other question to give you guys more 500 points. Please I'm waiting.
Tell me what vcl components you are updating, and what information you are passing to them, and i will show you how to do it in a thread safe manner.
Would also be handy if you could paste your main indy command method here.
Would also be handy if you could paste your main indy command method here.
Is that Safe???????
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////////// ///////
procedure TFPrinc.IdCmdTCPServer1Com mandHandle rs5Command (
ASender: TIdCommand);
var
datetime, msg : string;
begin
datetime := Asender.Context.Connection .IOHandler .ReadLn;
msg := FindComp(ASender.Context.C onnection. Socket.Bin ding.PeerI P) + ' - Nucleo Client ' + datetime;
HistoryAdd(msg,1);
BD.SPSecurityAdd.ParamByNa me('@idfun c').AsStri ng := FuncionarioID;
BD.SPSecurityAdd.ParamByNa me('@idcai xa').AsStr ing := StatusBar1.Panels[2].Text;
BD.SPSecurityAdd.ParamByNa me('@data' ).AsString := FormatDateTime('MM/dd/yyyy hh:nn:ss AM/PM',now());
BD.SPSecurityAdd.ParamByNa me('@mensa gem').AsSt ring := msg;
Bd.SPSecurityAdd.ExecProc;
FEMail_Send.Mensagem.Clear ;
FEMail_Send.Mensagem.Lines .Add('Secu rity Email');
FEMail_Send.Mensagem.Lines .Add(' ');
FEMail_Send.Mensagem.Lines .Add(Msg);
FEMail_Send.Mensagem.Lines .Add(' ');
FEMail_Send.Mensagem.Lines .Add(' ');
FEMail_Send.Mensagem.Lines .Add('Func ionario... .......: ' + Funcionario);
FEMail_Send.Mensagem.Lines .Add('Caix a......... .......: ' + StatusBar1.Panels[2].Text) ;
FEMail_Send.Send;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////////// ///////
//////////////////////////
procedure TFPrinc.IdCmdTCPServer1Com
ASender: TIdCommand);
var
datetime, msg : string;
begin
datetime := Asender.Context.Connection
msg := FindComp(ASender.Context.C
HistoryAdd(msg,1);
BD.SPSecurityAdd.ParamByNa
BD.SPSecurityAdd.ParamByNa
BD.SPSecurityAdd.ParamByNa
BD.SPSecurityAdd.ParamByNa
Bd.SPSecurityAdd.ExecProc;
FEMail_Send.Mensagem.Clear
FEMail_Send.Mensagem.Lines
FEMail_Send.Mensagem.Lines
FEMail_Send.Mensagem.Lines
FEMail_Send.Mensagem.Lines
FEMail_Send.Mensagem.Lines
FEMail_Send.Mensagem.Lines
FEMail_Send.Mensagem.Lines
FEMail_Send.Send;
//////////////////////////
well it's safe, but looking at your code, probably not the best solution for you.
I thought you meant things like a log (TMemo's etc).
If you are using SQL queries, etc. you should be defining them inside thread instead, as long as they do not update a component in the main vcl.
here's some of my code for doing this. It's from a IdTCPServer, but it should be the same for the CMD one
type
TSpecificClientConnection = class(TServerClientWinSock et)
public
ID: string; // just the unique id we create for each connection
StatusBarText: string; // you could fill this when the client connects...
ClientTimer: TTimer; // just an example of a component
Constructor CreateWithSettings(ID_: string; statusbartext_: string);
Destructor Destroy; override;
end;
{ TClientSpecificData }
constructor TClientSpecificData.Create WithSettin gs(ID_, statusbartext_: string);
begin
Inherited Create;
ID := ID_;
statusbartext := statusbartext_;
ClientTimer := TTimer.create(nil); // just an example...
end;
destructor TClientSpecificData.Destro y;
begin
ClientTimer.Free;
inherited;
end;
procedure TfServerMain.IdTCPServer1C onnect(ATh read: TIdPeerThread);
var
ID_: string;
begin
ID_ := AThread.Connection.Socket. Binding.Pe erIP + ':' + IntToStr(AThread.Connectio n.Socket.B inding.Pee rPort);
AThread.Data := TClientSpecificData.Create WithSettin gs(ID_);
AThread.Connection.Tag := integer(AThread.Data); //can also store our data object here as long as we type cast it back
...
end;
procedure TfServerMain.IdTCPServer1D isconnect( AThread: TIdPeerThread);
begin
if assigned(AThread.Data) then
begin
(AThread.Data as TClientSpecificData).Free;
AThread.Data := nil;
end;
end;
you can then access it in various places by typecasting it back
eg. (AThread.Data as TClientSpecificData).Clien tTimer (or StatusBartext)
To access the StatusBar like you are doing is a bit different. This is one of the things you should not be doing in threads really.
However, you can get around it by using a TCriticalSection, or windows messages, or indys syncing methods (a little trickier actually)
for a simple, TCriticalSection, in a shared unit, declare the following
mycrit: TCriticalSection
crittext: string;
put the following in your form's create (or some other more relevent place)
mycrit := TCriticalSection.Create;
and in the form's destroy (or some other more relevent place)
mycrit.free;
then, EVERY TIME you want to change or read the value of "crittext" you must do it like this
mycrit.enter;
try
s := crittext;
finally
mycrit.Leave;
end;
note: you must do this in teh main vcl as well as in the threads.
hth, Loki
I thought you meant things like a log (TMemo's etc).
If you are using SQL queries, etc. you should be defining them inside thread instead, as long as they do not update a component in the main vcl.
here's some of my code for doing this. It's from a IdTCPServer, but it should be the same for the CMD one
type
TSpecificClientConnection = class(TServerClientWinSock
public
ID: string; // just the unique id we create for each connection
StatusBarText: string; // you could fill this when the client connects...
ClientTimer: TTimer; // just an example of a component
Constructor CreateWithSettings(ID_: string; statusbartext_: string);
Destructor Destroy; override;
end;
{ TClientSpecificData }
constructor TClientSpecificData.Create
begin
Inherited Create;
ID := ID_;
statusbartext := statusbartext_;
ClientTimer := TTimer.create(nil); // just an example...
end;
destructor TClientSpecificData.Destro
begin
ClientTimer.Free;
inherited;
end;
procedure TfServerMain.IdTCPServer1C
var
ID_: string;
begin
ID_ := AThread.Connection.Socket.
AThread.Data := TClientSpecificData.Create
AThread.Connection.Tag := integer(AThread.Data); //can also store our data object here as long as we type cast it back
...
end;
procedure TfServerMain.IdTCPServer1D
begin
if assigned(AThread.Data) then
begin
(AThread.Data as TClientSpecificData).Free;
AThread.Data := nil;
end;
end;
you can then access it in various places by typecasting it back
eg. (AThread.Data as TClientSpecificData).Clien
To access the StatusBar like you are doing is a bit different. This is one of the things you should not be doing in threads really.
However, you can get around it by using a TCriticalSection, or windows messages, or indys syncing methods (a little trickier actually)
for a simple, TCriticalSection, in a shared unit, declare the following
mycrit: TCriticalSection
crittext: string;
put the following in your form's create (or some other more relevent place)
mycrit := TCriticalSection.Create;
and in the form's destroy (or some other more relevent place)
mycrit.free;
then, EVERY TIME you want to change or read the value of "crittext" you must do it like this
mycrit.enter;
try
s := crittext;
finally
mycrit.Leave;
end;
note: you must do this in teh main vcl as well as in the threads.
hth, Loki
Forced accept.
Computer101
EE Admin
Computer101
EE Admin