Solved

Properly Coded Indy Reader Threads - Part 3

Posted on 2004-09-18
17
318 Views
Last Modified: 2010-04-05
This is basically a continuation of some prior topics...

http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_21129083.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.
0
Comment
Question by:werehamster-
  • 8
  • 8
17 Comments
 

Author Comment

by:werehamster-
ID: 12094354
This is just a link to another question for the same project, but doesn't have anything to do with threads really.

http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_21136532.html
0
 
LVL 10

Expert Comment

by:Jacco
ID: 12094467
What are we going to do next?
0
 

Author Comment

by:werehamster-
ID: 12094763
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.
0
 

Author Comment

by:werehamster-
ID: 12094783
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?
0
 
LVL 10

Expert Comment

by:Jacco
ID: 12095060
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

0
 
LVL 10

Expert Comment

by:Jacco
ID: 12095072
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(AOwner: TComponent);
begin
  inherited Create(AOwner);
  fClearOnMax := False;
  MaxItems    := 2000;
  FocusOnLast := True;
  Style       := lbOwnerDrawFixed;
  OnDrawItem  := LocalDrawItem;
end;

procedure TColorListBox.DisplayMessage(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.LocalDrawItem(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.
0
 

Author Comment

by:werehamster-
ID: 12126965
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.Caption := '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.  http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_21141281.html
0
 
LVL 10

Expert Comment

by:Jacco
ID: 12127297
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
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.

 

Author Comment

by:werehamster-
ID: 12128606
ah
just take out the ..\
0
 
LVL 10

Expert Comment

by:Jacco
ID: 12130371
Nope the files I have here are not the newest ones. You changed the ..\xxx ones without knowing it in Delphi.
0
 

Author Comment

by:werehamster-
ID: 12130491
try downloading again, same name...
0
 

Author Comment

by:werehamster-
ID: 12130496
0
 
LVL 10

Expert Comment

by:Jacco
ID: 12135917
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
0
 
LVL 10

Expert Comment

by:Jacco
ID: 12136016
btw you project works now :) Looks great!
0
 

Author Comment

by:werehamster-
ID: 12139394
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.
0
 
LVL 10

Accepted Solution

by:
Jacco earned 500 total points
ID: 12140275
Passing components on a form to a thread is dangerous. This is why:

If Windows needs to repaint say a TMemo it post a message to the application and the a responds by handling the messages when idle. It first calls WMPaint which calls Paint etc. Now paint reads from the TMemo.Lines what it should display. If another thread has a reference to this TMemo it could be writing to the TMemo.Lines exactly at the same moment Paint needs it to display. An then you have a bug.

This is just a sample of how thing can go wrong. It can cause some non-reproducable bugs. (Maybe also this bug you describe)

If talking to form components from another thread I always use the scheme described above or Synchronize (but you have to use a manual critical section to protect the shared data then because Synchronize does not accept parameters.

Regards Jacco
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

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Concerto provides fully managed cloud services and the expertise to provide an easy and reliable route to the cloud. Our best-in-class solutions help you address the toughest IT challenges, find new efficiencies and deliver the best application expe…
The Email Laundry PDF encryption service allows companies to send confidential encrypted  emails to anybody. The PDF document can also contain attachments that are embedded in the encrypted PDF. The password is randomly generated by The Email Laundr…

919 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

17 Experts available now in Live!

Get 1:1 Help Now