Solved

Properly Coded Indy Reader Threads - Part 3

Posted on 2004-09-18
17
317 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-
Comment Utility
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
Comment Utility
What are we going to do next?
0
 

Author Comment

by:werehamster-
Comment Utility
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-
Comment Utility
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
Comment Utility
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
Comment Utility
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-
Comment Utility
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
Comment Utility
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
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 

Author Comment

by:werehamster-
Comment Utility
ah
just take out the ..\
0
 
LVL 10

Expert Comment

by:Jacco
Comment Utility
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-
Comment Utility
try downloading again, same name...
0
 

Author Comment

by:werehamster-
Comment Utility
0
 
LVL 10

Expert Comment

by:Jacco
Comment Utility
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
Comment Utility
btw you project works now :) Looks great!
0
 

Author Comment

by:werehamster-
Comment Utility
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
Comment Utility
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

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

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…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…
This demo shows you how to set up the containerized NetScaler CPX with NetScaler Management and Analytics System in a non-routable Mesos/Marathon environment for use with Micro-Services applications.

744 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