Jacco
asked on
Indy server poll thread
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.ProcessMessage s 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(ACreate Suspended: 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(Se nder: TObject);
begin
fPoll := TPollThread.Create(False);
end;
procedure TfrmClient.Button2Click(Se nder: 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.
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.ProcessMessage
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(ACreate
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
else
TSyncSendResult.SendResult
end;
// try to acquire server messages
if fClient.Connected then
begin
try
fClient.WriteLn('POLL');
lsResult := fClient.ReadLn;
if lsResult = 'NOMESSAGES' then
TSyncSendResult.SendResult
except
fClient.Disconnect;
end;
end;
// send queued messages
// todo
end;
{ TSyncSendResult }
class procedure TSyncSendResult.SendResult
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(
frmClient.Label1.Caption := IntToStr(giCount);
Inc(giCount);
end;
{ TfrmClient }
procedure TfrmClient.Button1Click(Se
begin
fPoll := TPollThread.Create(False);
end;
procedure TfrmClient.Button2Click(Se
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.
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...
ASKER
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.
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.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
If you have Delphi6 you can instead of using PeekMessage also use CheckSynchronize, which will do nothing but execute Synchronize requests...
ASKER
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
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
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...
ASKER
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!
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!
:-) 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.
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.
ASKER
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
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
>> 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?
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?
ASKER
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
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
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.
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.
ASKER
Not answered but still I solved the problem differently.
This question should be deleted...
This question should be deleted...
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.