0

I want to add a Tabsheet dynamically to a PageControl when a client connected to my IdCmdTCPServer same as this code:

procedure TForm1.IdCmdTCPServer1Connect(AContext: TIdContext);
var
  ATabSheet: TTabSheet;
begin
  ATabSheet := TTabSheet.Create(PageControl1);
  ATabSheet.PageControl := PageControl1;
  ATabSheet.Caption := 'Hello!';
  {...}
end;

Its OK, but when closing application i will get EOSError with message: "System Error. Code: 1400. Invalid window handle". I've been added this code block in a TButton's OnClick event, and application will close without any problem. Is there any safer way to do this?


After David Heffernan's help, I've Changed my methode this way:

....

type
  TMyThrd = class(TThread)
    protected
      procedure Execute; override;
  end;

...

procedure TMyThrd.Execute;
begin
  with TTabsheet.Create(Form1.PageControl1) do
    PageControl := Form1.PageControl1;
end;

...

procedure TForm1.cmdAddTabCommand(ASender: TIdCommand);
begin
  with TMyThrd.Create(True) do
  begin
    FreeOnTerminate := True;
    Resume;
  end;
end;

And command test result:

addtab Command Testing Result

but the mentioned problem is steel happening on application close!

Community
  • 1
  • 1
Farshad H.
  • 303
  • 4
  • 16
  • maybe this will help http://stackoverflow.com/q/9139940/1699210 – bummi May 30 '13 at 08:11
  • @bummi: thanks for your suggestion. but i checked my TCP parts of code, and they are running perfect without this block code in `OnConnect` event. just when i add this block, problem will appear! – Farshad H. May 30 '13 at 08:24

2 Answers2

1

Your event handler, and therefore the VCL accessing code that it contains, executes on a thread other than the GUI thread. All code that accesses VCL objects must execute on the GUI thread. Hence, you will need to change your code to make sure that the VCL portions are marshalled on to the GUI thread.

Use TIdSync or TThread.Synchronize to marshal the VCL portions of your code onto the GUI thread. Remy gives an example of the former approach here: Indy synchronize ServerTCPExecute

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • You have to do what I say in the answer. Call TThread.Synchronize. Don't create yet another thread! – David Heffernan May 30 '13 at 09:11
  • which Thread? the `TThread` that you say is a class, exactly i must synchronize which identifier base on this class? – Farshad H. May 30 '13 at 09:18
  • 1
    Use the class method TThread.Synchronize. Or the variant that Remy describes in the question linked from my answer. But right now you need to stop using trial and error and read up on the basics of threading, and on the concept of Synchronize and why it is needed. You need understanding before you proceed. – David Heffernan May 30 '13 at 09:24
  • Tanks a lot! I've change my codes same Remy's idea, the error 1400 message not seen this time, but the 'invalid operation handle' happens when closing app! – Farshad H. May 30 '13 at 09:41
0

Final correct idea:

uses
..., IdSync, ...
...

type
  TSyncThrd = class(TIdSync)
  protected
    procedure DoSynchronize; override;
end;

...

procedure TForm1.cmdAddTabCommand(ASender: TIdCommand);
begin
  with TSyncThrd.Create do
  begin
    try
      Synchronize;
    finally
      Free;
    end;
  end;
end;

...


procedure TSyncThrd.DoSynchronize;
begin
  with TTabsheet.Create(Form1.PageControl1) do
    PageControl := Form1.PageControl1;
end;

Of course current connection must be closed before application close. If no, invalid pointer operation will occur!

Specially thanks to David Heffernan

Community
  • 1
  • 1
Farshad H.
  • 303
  • 4
  • 16