We help IT Professionals succeed at work.

Indy server poll thread

Jacco
Jacco asked
on
Hi all,

I want to create a thread for my application that has a TIdTCPClient that polls the server for messages. This polling needs to continue when my application is doing a lengthy process that is why I created the TIdTCPClient inside a thread. I do not want to call Application.ProcessMessages since this can disturb the length process.

I have tried first without the PeekMessage instruction in the source below but the Thread did not poll anymore during my lengthy call (Button2Click). Now when I put a PeekMessage (which does not remove a message) my thread keeps on polling. Now I don't want this PeekMessage either, I just want my thread to keep on polling.

My question is how can this be? The PeekMessage should not do anything. And secondly: is there a way to program this behaviour without calling PeekMessage in my main thread?

My guess is that the PeekMessage gives windows the chance to forward messages to my thread. Will this work for all Windows versions?

Can someway explain what is going on here?

Thanks Jacco

Here is the source for the client:

unit uClient;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  IdAntiFreezeBase, IdAntiFreeze, IdBaseComponent, IdComponent,
  IdTCPConnection, IdTCPClient, StdCtrls, IdThread, ComCtrls;

type
  TPollThread = class(TIdThread)
  private
    fClient: TIdTCPClient;
  protected
    procedure Run; override;
  public
    constructor Create(ACreateSuspended: Boolean = True); override;
    destructor Destroy; override;
  end;

  TSyncSendResult = class
  private
    fResult: string;
    procedure Sync;
  public
    class procedure SendResult(aThread: TIdThread; aResult: string);
  end;

  TfrmClient = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    pbProgress: TProgressBar;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    fPoll: TPollThread;
  public
    { Public declarations }
  end;

var
  frmClient: TfrmClient;

implementation

{$R *.DFM}

{ TPollThread }

constructor TPollThread.Create(ACreateSuspended: Boolean);
begin
  fClient := TIdTCPClient.Create(nil);
  // setup client
  with fClient do
  begin
    Host := '127.0.0.1';
    Port := 45000;
  end;
  FreeOnTerminate := True;
  inherited Create(ACreateSuspended);
end;

destructor TPollThread.Destroy;
begin
  fClient.Free;
  inherited Destroy;
end;

procedure TPollThread.Run;
var
  lsResult: string;
begin
  // try to connect
  if not fClient.Connected then
  begin
    try
      fClient.Connect;
    except
    end;
    if fClient.Connected then
      TSyncSendResult.SendResult(Self, 'Connection established')
    else
      TSyncSendResult.SendResult(Self, 'Connection failed');
  end;
  // try to acquire server messages
  if fClient.Connected then
  begin
    try
      fClient.WriteLn('POLL');
      lsResult := fClient.ReadLn;
      if lsResult = 'NOMESSAGES' then
        TSyncSendResult.SendResult(Self, 'No messages');
    except
      fClient.Disconnect;
    end;
  end;
  // send queued messages
  // todo
end;

{ TSyncSendResult }

class procedure TSyncSendResult.SendResult(aThread: TIdThread; aResult: string);
begin
  with Create do
  try
    fResult := aResult;
    aThread.Synchronize(Sync);
  finally
    Free;
  end;
end;

var
  giCount: LongInt = 0;

procedure TSyncSendResult.Sync;
begin
  frmClient.Memo1.Lines.Add(fResult);
  frmClient.Label1.Caption := IntToStr(giCount);
  Inc(giCount);
end;

{ TfrmClient }

procedure TfrmClient.Button1Click(Sender: TObject);
begin
  fPoll := TPollThread.Create(False);
end;

procedure TfrmClient.Button2Click(Sender: TObject);
var
  liCount: Integer;
  Msg: TMsg;
begin
  liCount := 0;
  while liCount < 10000 do
  begin
    if pbProgress.Position = 100 then
      pbProgress.Position := 0
    else
      pbProgress.Position := pbProgress.Position + 1;
    // if this is removed the thread will stop polling
    PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);
    Inc(liCount);
  end;
end;

end.
Comment
Watch Question

Commented:
I can't really follow your sources. A TThread object has an Execute method, which you have to fill. Where is that?

Some explanations: Each thread in win32 programming has its own windows and also its completely own message queue. Messages to a specific window can only get handled, when the thread (to which this window belongs) does message handling. Message handling functions are PeekMessage, GetMessage and related.

Regards, Madshi.

Commented:
P.S: I see, you're using TIdThread, not TThread. Well, I don't have Indy installed on my PC, so I can't check the Execute method of TIdThread. Generally calling PeekMessage in one thread should not have any effect on another thread - except if the other thread uses Synchronize and thus depends on that the main thread handles messages...

Author

Commented:
Hi Madshi,

The execute of the thread is TPollThread.Run. I use Synchronize there. So you say without the PeekMessage my thread hangs on a Synchronize and PeekMessage lets the Synchronize take place but new user interface messages will not be processed?

Regards Jacco

P.S. Maybe I can make an example using TThread that exposes this behaviour.
Commented:
If you call Synchronize in a thread, this thread is waiting for the main thread to take over the work. The main thread of course doesn't at once stop its original work to do what the other thread wants. That would be a catastrophe. The main thread does only what you tell him to say. And only if you tell him to handle the Synchronize request, it does handle it. And only then the other thread returns from the Synchronize call.
I'm not absolutely sure, what possibilities you have to handle the Synchronize request in the main thread. Both Application.ProcessMessages and Application.HandleMessage are internally looking for Synchronize tasks and then fulfilling them. I'm not sure about PeekMessage. Maybe it also results in Synchronize requests being processed. From what you are reporting, it seems like that.

Regards, Madshi.

Commented:
If you have Delphi6 you can instead of using PeekMessage also use CheckSynchronize, which will do nothing but execute Synchronize requests...

Author

Commented:
Thanks Madsi,

I have D6 at work. I'll check out the function.

Strange though isn't it that a pure WinAPI call PeekMessage causes my Delphi app main thread to process the synchronise calls from other threads?

Regards Jacco

Commented:
Well, in D5 (and older) Synchronize was internally realized by a SendMessage call from the other thread. It's quite possible, that Windows does handle SendMessage requests, when you call PeekMessage. But PeekMessage should not work for D6, I guess...

Author

Commented:
I try some thing at work tomorrow.

Thanks so far Madshi

B.T.W. Your MadExcept component works great. I have actually fixed bugs with a screenshot of you exception box users send me in very little time!

Commented:
:-)  Fine! I like to hear that!! But why do they send a screenshot to you? Why don't you let them send the bug report directly from the exception box? Or are you still using madExcept 1.x? The new version 2.0 is much nicer...  (-:

P.S: Perhaps you need to use PeekMessage for D2-D5 and CheckSynchronize for D6, quite possible. I guess, PeekMessage will only work for D2-D5 and not for D6, while CheckSynchronize is only available in D6.

Author

Commented:
I think you've send me the update. (Haven't installed it yet because I am trying to resolve a problem: When I am running my app in Delphi the exception box appears and Delphi stops somewhere in MadExcept then I can press F9 over and over again but it keeps stopping in MadExcept source. The only way out is Ctrl-F2 then. I think it has something to do with compiling the sources or with the debug path).

I have tried the PeekMessage in D5 with NT and Win ME both behave the same. Haven't tried D6 CheckSynchronize yet.

More soon...

Regards Jacco

Commented:
>> I think you've send me the update. (Haven't installed it yet because I am trying to resolve a problem: When I am running my app in Delphi the exception box appears and Delphi stops somewhere in MadExcept then I can press F9 over and over again but it keeps stopping in MadExcept source.

Does this only occur with madExcept 2.0? The first version of madExcept actually had a problem like that. But the second release does not. Is it possible that you only tested the first release?

Author

Commented:
This is getting a little off topic...

Yes it is the first one I've tested. How can I get the update to version 2 ? Do I need to order it?

Regards Jacco

Commented:
Sorry for the late reaction, I just got the notification about your comment today, not any earlier...   :-(

I guess, you're Jacco Kulman? In that case I mailed you madExcept 2.0. If you don't have it anymore I can send it again, no problem...

Did you try D6 CheckSynchronize in the meanwhile?

Regards, Madshi.

Author

Commented:
Not answered but still I solved the problem differently.

This question should be deleted...