Solved

Delphi 7 & Indy 10 OnCommand Event and VCL Components Thread Safe?

Posted on 2007-04-10
7
1,122 Views
Last Modified: 2008-03-18
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!
0
Comment
Question by:Marius0188
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 2
7 Comments
 
LVL 17

Accepted Solution

by:
TheRealLoki earned 500 total points
ID: 18914879
You are correct, eachconnection will call the OnCommand method, which is run in the context of a thread.
0
 
LVL 1

Expert Comment

by:rafaelrgl
ID: 18926944
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
 
LVL 17

Expert Comment

by:TheRealLoki
ID: 18928188
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
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 1

Expert Comment

by:rafaelrgl
ID: 18941695
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
 
LVL 17

Expert Comment

by:TheRealLoki
ID: 18943117
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
 
LVL 1

Expert Comment

by:Computer101
ID: 21156544
Forced accept.

Computer101
EE Admin
0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Dev Express grid collapse 2 55
Convert a string into a TDateTime 5 81
Drag & Drop... Data from one grid to another 2 56
Formating a integer number to float 2 10
Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
A short tutorial showing how to set up an email signature in Outlook on the Web (previously known as OWA). For free email signatures designs, visit https://www.mail-signatures.com/articles/signature-templates/?sts=6651 If you want to manage em…

710 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