werehamster-
asked on
Properly Coded Indy Reader Threads - Part 3
This is basically a continuation of some prior topics...
https://www.experts-exchange.com/questions/21129083/Properly-Coded-Indy-Reader-Threads-Part-2.html#12091007
Anyway, this is the deal. I am writing a bot program for battle.net and trying to get the most stability out of the program as possible.
Basically, the program uses a main reader thread, which temporarily uses a 2nd reader thread to connect to a 2nd server to get some data that needs to be passed to the first server.
here is a link to the source files, which I will leave up indefinately for those seeking answers in the future.
www.datazap.net/ftp/werehamster/MyBot/MyBot4.zip
If the link is broken for whatever reason or another, just look at part 2.
https://www.experts-exchange.com/questions/21129083/Properly-Coded-Indy-Reader-Threads-Part-2.html#12091007
Anyway, this is the deal. I am writing a bot program for battle.net and trying to get the most stability out of the program as possible.
Basically, the program uses a main reader thread, which temporarily uses a 2nd reader thread to connect to a 2nd server to get some data that needs to be passed to the first server.
here is a link to the source files, which I will leave up indefinately for those seeking answers in the future.
www.datazap.net/ftp/werehamster/MyBot/MyBot4.zip
If the link is broken for whatever reason or another, just look at part 2.
What are we going to do next?
ASKER
Well this is just something I threw in the wind as an excuse to give more points. I got a little stuck tonight with trying to get TRichEdit working right, but it is simply ticking me off that I can't just add a line with colored text in an easy fashion.
However, I am still having problems with making a Connect/Disconnect button work properly.
I don't want the thing going into an infinate loop if it can't connect. It should have some kind of delay between getting disconnected from the server and reconnecting, and finally giving up after so many attemps and ending the thread.
Basically, instead of reconnecting automatically in the thread, I think I need it to terminate instead. And just have the form create a new thread after a period of time using a timer or when the user presses connect him/herself.
However, I am still having problems with making a Connect/Disconnect button work properly.
I don't want the thing going into an infinate loop if it can't connect. It should have some kind of delay between getting disconnected from the server and reconnecting, and finally giving up after so many attemps and ending the thread.
Basically, instead of reconnecting automatically in the thread, I think I need it to terminate instead. And just have the form create a new thread after a period of time using a timer or when the user presses connect him/herself.
ASKER
This is basically going to be completely compatible with another bot that is already in existence thus I have the INI file already for it which is where I am getting the ip address and port from, plus I want to use the connectonstartup=y/n, and reconnect timer values.
Also, this is an MDI interface. I'd like more than one running at a time, and the Connection status/buttons changing as you change the focus to different windows. Ideally, I just want one button that changes to Connect and Disconnect instead of a popup. A popup would be fine in the menus as well if I could automatically enable and disable the appropriate buttons so that they can't attempt to connect if already connected, or change it so it goes from connect to reconnect instead.
Any thoughts on this?
Also, this is an MDI interface. I'd like more than one running at a time, and the Connection status/buttons changing as you change the focus to different windows. Ideally, I just want one button that changes to Connect and Disconnect instead of a popup. A popup would be fine in the menus as well if I could automatically enable and disable the appropriate buttons so that they can't attempt to connect if already connected, or change it so it goes from connect to reconnect instead.
Any thoughts on this?
For the colored line I use an owner draw TListBox in the past. You can use the Items.Objects to store color or an object with the desired attributes that you use in the OnDrawItem.
For the rest I will download your project and make a few changes. Got the flue right now so it have to wait a bit I'm afraid.
Regards Jacco
For the rest I will download your project and make a few changes. Got the flue right now so it have to wait a bit I'm afraid.
Regards Jacco
Here is an old source I used with a list box. It stores the color of the text at the start. Maybe you can do something with it. It is also kind of threadsafe.
unit xColorListBox;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, Math;
type
TColorListBox = class(TListBox)
private
fMaxItems: Integer;
fFocusOnLast: Boolean;
fClearOnMax: Boolean;
procedure LocalDrawItem(Control: TWinControl;Index: Integer; Rect: TRect; State: TOwnerDrawState);
procedure SetMaxItems(const Value: integer);
protected
public
procedure DisplayMessage(aMess : string; aColor: TColor = clBlack);
published
constructor Create(AOwner: TComponent); override;
property MaxItems: Integer read fMaxItems write SetMaxItems;
property FocusOnLast: Boolean read fFocusOnLast write fFocusOnLast;
property ClearOnMax: Boolean read fClearOnMax write fClearOnMax default True;
end;
implementation
uses IdGlobal;
{$R xCOLORLISTBOX.RES}
constructor TColorListBox.Create(AOwne r: TComponent);
begin
inherited Create(AOwner);
fClearOnMax := False;
MaxItems := 2000;
FocusOnLast := True;
Style := lbOwnerDrawFixed;
OnDrawItem := LocalDrawItem;
end;
procedure TColorListBox.DisplayMessa ge(aMess: string; aColor: TColor);
var
lsColor: string;
liIndex: Integer;
begin
// This one is "threadsafe" (not completly though)
try
if Perform(LB_GETCOUNT, 0, 0) >= MaxItems then
begin
if ClearOnMax then
begin
Perform(LB_RESETCONTENT, 0, 0);
Perform(LB_ADDSTRING, 0, LongInt(PChar('Black/ ...continued')));
end else
begin
Perform(LB_DELETESTRING, 0, 0);
end;
end;
if ColorToIdent(aColor, lsColor) then
aMess := Copy(lsColor, 3, Length(lsColor)-2) + '/' + aMess;
Perform(LB_ADDSTRING, 0, LongInt(PChar(aMess)));
liIndex := Max(0, Perform(LB_GETCOUNT, 0, 0) - (Height div ItemHeight));
if FocusOnLast then
PostMessage(Handle, LB_SETTOPINDEX, liIndex, 0); // This one must be posted!!
Refresh; //thread-safe
// end;
except
end;
end;
procedure TColorListBox.LocalDrawIte m(Control: TWinControl;
Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
lColor : longint;
lsMessage : string;
liPos : integer;
begin
{
This function isn't thread safe, so it is possible that
the item requested is already deleted (MaxItem). In that case
an error is allowed (what else to do?) so try except
}
try
with TListBox(Control), TListBox(Control).Canvas do
begin
liPos := Pos('/', Items[Index]);
if liPos = 0 then
begin
lsMessage := Items[Index];
lColor := clBlack;
end else begin
lsMessage := Copy(Items[Index], liPos + 1, Length(Items[Index]) - liPos);
if not IdentToColor('cl' + Copy(Items[Index], 1, liPos - 1), lColor) then
lColor := clBlack;
end;
if odSelected in State then
lColor := clWhite;
Font.Color := lColor;
FillRect(Rect);
TextOut(Rect.Left + 3, Rect.Top + 2, lsMessage);
end;
except
end;
end;
procedure TColorListBox.SetMaxItems( const Value: integer);
begin
if Value > 0 then
fMaxItems := Value;
end;
end.
unit xColorListBox;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, Math;
type
TColorListBox = class(TListBox)
private
fMaxItems: Integer;
fFocusOnLast: Boolean;
fClearOnMax: Boolean;
procedure LocalDrawItem(Control: TWinControl;Index: Integer; Rect: TRect; State: TOwnerDrawState);
procedure SetMaxItems(const Value: integer);
protected
public
procedure DisplayMessage(aMess : string; aColor: TColor = clBlack);
published
constructor Create(AOwner: TComponent); override;
property MaxItems: Integer read fMaxItems write SetMaxItems;
property FocusOnLast: Boolean read fFocusOnLast write fFocusOnLast;
property ClearOnMax: Boolean read fClearOnMax write fClearOnMax default True;
end;
implementation
uses IdGlobal;
{$R xCOLORLISTBOX.RES}
constructor TColorListBox.Create(AOwne
begin
inherited Create(AOwner);
fClearOnMax := False;
MaxItems := 2000;
FocusOnLast := True;
Style := lbOwnerDrawFixed;
OnDrawItem := LocalDrawItem;
end;
procedure TColorListBox.DisplayMessa
var
lsColor: string;
liIndex: Integer;
begin
// This one is "threadsafe" (not completly though)
try
if Perform(LB_GETCOUNT, 0, 0) >= MaxItems then
begin
if ClearOnMax then
begin
Perform(LB_RESETCONTENT, 0, 0);
Perform(LB_ADDSTRING, 0, LongInt(PChar('Black/ ...continued')));
end else
begin
Perform(LB_DELETESTRING, 0, 0);
end;
end;
if ColorToIdent(aColor, lsColor) then
aMess := Copy(lsColor, 3, Length(lsColor)-2) + '/' + aMess;
Perform(LB_ADDSTRING, 0, LongInt(PChar(aMess)));
liIndex := Max(0, Perform(LB_GETCOUNT, 0, 0) - (Height div ItemHeight));
if FocusOnLast then
PostMessage(Handle, LB_SETTOPINDEX, liIndex, 0); // This one must be posted!!
Refresh; //thread-safe
// end;
except
end;
end;
procedure TColorListBox.LocalDrawIte
Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
lColor : longint;
lsMessage : string;
liPos : integer;
begin
{
This function isn't thread safe, so it is possible that
the item requested is already deleted (MaxItem). In that case
an error is allowed (what else to do?) so try except
}
try
with TListBox(Control), TListBox(Control).Canvas do
begin
liPos := Pos('/', Items[Index]);
if liPos = 0 then
begin
lsMessage := Items[Index];
lColor := clBlack;
end else begin
lsMessage := Copy(Items[Index], liPos + 1, Length(Items[Index]) - liPos);
if not IdentToColor('cl' + Copy(Items[Index], 1, liPos - 1), lColor) then
lColor := clBlack;
end;
if odSelected in State then
lColor := clWhite;
Font.Color := lColor;
FillRect(Rect);
TextOut(Rect.Left + 3, Rect.Top + 2, lsMessage);
end;
except
end;
end;
procedure TColorListBox.SetMaxItems(
begin
if Value > 0 then
fMaxItems := Value;
end;
end.
ASKER
Alright, I got the thing to works and I have something resembling a chat client now.
www.datazap.net/ftp/werehamster/MyBot/3.zip
I am still having a few issues...
1) How do I inform the form that my reader thread has closed connection? Do I just use fForm.SomeComponent.Captio n := 'Disconnected' and stuff inside a synchronized procedure or is there a better way?
2) Shared data between 2 threads/readers. Right now my main reader thread starts up, opens a quick helper thread for logon purposes, then disposes of it. What is the proper way for one thread to pass data back to another thread? What I have works, but dunno if it is safe.
3) I use a TINIFile to store information. It does not work correctly. How do I make it thread safe? Starting a new thread on this topic alone since it really has nothing to do with a reader thread. https://www.experts-exchange.com/questions/21141281/1-MDI-Form-2-Indy-Reader-Threads-1-INIFile-How-do-I-make-it-thread-safe.html
www.datazap.net/ftp/werehamster/MyBot/3.zip
I am still having a few issues...
1) How do I inform the form that my reader thread has closed connection? Do I just use fForm.SomeComponent.Captio
2) Shared data between 2 threads/readers. Right now my main reader thread starts up, opens a quick helper thread for logon purposes, then disposes of it. What is the proper way for one thread to pass data back to another thread? What I have works, but dunno if it is safe.
3) I use a TINIFile to store information. It does not work correctly. How do I make it thread safe? Starting a new thread on this topic alone since it really has nothing to do with a reader thread. https://www.experts-exchange.com/questions/21141281/1-MDI-Form-2-Indy-Reader-Threads-1-INIFile-How-do-I-make-it-thread-safe.html
The wrong versions of the follwoing files are in the zip:
PacketConsts in '..\PacketConsts.pas',
myUtils in '..\myUtils.pas';
You probably altered the files in one directory up.
Regards Jacco
PacketConsts in '..\PacketConsts.pas',
myUtils in '..\myUtils.pas';
You probably altered the files in one directory up.
Regards Jacco
ASKER
ah
just take out the ..\
just take out the ..\
Nope the files I have here are not the newest ones. You changed the ..\xxx ones without knowing it in Delphi.
ASKER
try downloading again, same name...
ASKER
Like I said in the other Q:
>>It would be a good decision to separate the TWerebot from TBNLSReader and TBNETReader.
>>It is always dangerous to have them in one unit since they can then mess with each other private parts <G> to easily.
A scheme I often use is the following:
Use one threadsafe storage of things. (Like the INI, in fact you might try using one, maybe an in memory one, a safe TMemIniFile descendant) You can safely store the caption of the form there. When changing a value in this storage post a message to the main form (notice that posting the message will not stop your thread from running, it will not wait at all). Then when the form handles the message you can read from the safe storage.
Now if you already posted a message to the mainform you can refrain from posting the same message again easily by storing in the safe storage that you did already posted the message. When the main thread finally handles the message you could clear this flag.
Regards Jacco
>>It would be a good decision to separate the TWerebot from TBNLSReader and TBNETReader.
>>It is always dangerous to have them in one unit since they can then mess with each other private parts <G> to easily.
A scheme I often use is the following:
Use one threadsafe storage of things. (Like the INI, in fact you might try using one, maybe an in memory one, a safe TMemIniFile descendant) You can safely store the caption of the form there. When changing a value in this storage post a message to the main form (notice that posting the message will not stop your thread from running, it will not wait at all). Then when the form handles the message you can read from the safe storage.
Now if you already posted a message to the mainform you can refrain from posting the same message again easily by storing in the safe storage that you did already posted the message. When the main thread finally handles the message you could clear this flag.
Regards Jacco
btw you project works now :) Looks great!
ASKER
Well, the problem is that I have 2 threads that completely rely on information from the other and the main form. In fact, the soul purpose of BNLS is to get some hashed data from another server to help in the login process. After it's logged in, it is not used again.
I'll see about rewriting the code so that it is a seperate unit. I suppose I could make it create with the type of components it needs in the constructor...
constructor creater(OutPutMemo, UsersList, Etc.);
Thanks for the help so far...
For some reason though, not everyone who uses it can get it to work. I think it is mostly user error, but who knows. Trying to make it as tight as possible.
I'll see about rewriting the code so that it is a seperate unit. I suppose I could make it create with the type of components it needs in the constructor...
constructor creater(OutPutMemo, UsersList, Etc.);
Thanks for the help so far...
For some reason though, not everyone who uses it can get it to work. I think it is mostly user error, but who knows. Trying to make it as tight as possible.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
https://www.experts-exchange.com/questions/21136532/TRichEdit-and-Colors.html