Solved

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

Posted on 2007-04-10
7
1,113 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
  • 3
  • 2
7 Comments
 
LVL 17

Accepted Solution

by:
TheRealLoki earned 500 total points
Comment Utility
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
Comment Utility
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
Comment Utility
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
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 1

Expert Comment

by:rafaelrgl
Comment Utility
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
Comment Utility
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
Comment Utility
Forced accept.

Computer101
EE Admin
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…
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.

771 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

11 Experts available now in Live!

Get 1:1 Help Now