Solved

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

Posted on 2007-04-10
7
1,114 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
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
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
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

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
Delphi 10 Seattle Dataset Actions 5 79
problem when i try to pack my dll file with upx 9 72
Delphi XE2 application frozen on Windows 10 10 278
Best Firemonkey component pack 1 88
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…
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…
In this video I am going to show you how to back up and restore Office 365 mailboxes using CodeTwo Backup for Office 365. Learn more about the tool used in this video here: http://www.codetwo.com/backup-for-office-365/ (http://www.codetwo.com/ba…
Sending a Secure fax is easy with eFax Corporate (http://www.enterprise.efax.com). First, just open a new email message. In the To field, type your recipient's fax number @efaxsend.com. You can even send a secure international fax — just include t…

863 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

25 Experts available now in Live!

Get 1:1 Help Now