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!
Marius0188Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

TheRealLokiSenior DeveloperCommented:
You are correct, eachconnection will call the OnCommand method, which is run in the context of a thread.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
rafaelrglCommented:
I have same problem experts, please help us. I will open other question to give you guys more 500 points. Please I'm waiting.
0
TheRealLokiSenior DeveloperCommented:
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.
0
Build an E-Commerce Site with Angular 5

Learn how to build an E-Commerce site with Angular 5, a JavaScript framework used by developers to build web, desktop, and mobile applications.

rafaelrglCommented:
Is that Safe???????

/////////////////////////////////////////////////////////////////////////////////////////////

procedure TFPrinc.IdCmdTCPServer1CommandHandlers5Command(
  ASender: TIdCommand);
var
  datetime, msg : string;
begin
    datetime := Asender.Context.Connection.IOHandler.ReadLn;
    msg := FindComp(ASender.Context.Connection.Socket.Binding.PeerIP) + ' - Nucleo Client ' +  datetime;
    HistoryAdd(msg,1);
    BD.SPSecurityAdd.ParamByName('@idfunc').AsString := FuncionarioID;
    BD.SPSecurityAdd.ParamByName('@idcaixa').AsString := StatusBar1.Panels[2].Text;
    BD.SPSecurityAdd.ParamByName('@data').AsString := FormatDateTime('MM/dd/yyyy hh:nn:ss AM/PM',now());
    BD.SPSecurityAdd.ParamByName('@mensagem').AsString := msg;
    Bd.SPSecurityAdd.ExecProc;

    FEMail_Send.Mensagem.Clear;
    FEMail_Send.Mensagem.Lines.Add('Security 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('Funcionario..........: ' + Funcionario);
    FEMail_Send.Mensagem.Lines.Add('Caixa................: ' + StatusBar1.Panels[2].Text);
    FEMail_Send.Send;

/////////////////////////////////////////////////////////////////////////////////////////////
0
TheRealLokiSenior DeveloperCommented:
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(TServerClientWinSocket)
  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.CreateWithSettings(ID_, statusbartext_: string);
    begin
        Inherited Create;
        ID := ID_;
        statusbartext := statusbartext_;
        ClientTimer := TTimer.create(nil); // just an example...
    end;

destructor TClientSpecificData.Destroy;
    begin
        ClientTimer.Free;
        inherited;
    end;


procedure TfServerMain.IdTCPServer1Connect(AThread: TIdPeerThread);
var
    ID_: string;
begin
    ID_ := AThread.Connection.Socket.Binding.PeerIP + ':' + IntToStr(AThread.Connection.Socket.Binding.PeerPort);
    AThread.Data := TClientSpecificData.CreateWithSettings(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.IdTCPServer1Disconnect(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).ClientTimer (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
0
Computer101Commented:
Forced accept.

Computer101
EE Admin
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.