Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 3258
  • Last Modified:

Indy IDTelnet Freezes with no error message in Delphi 7

I am writing an application to automate some work I do with Cisco Switches.  The easiest way to get data off the switches is by Telnet, so I am using Delphi and Indy's IDTelnet component.

I can connect and get the data I need off the switches - but when I disconnect, my application hangs with no error message.

I have written a component to do the IDTelnet handling, it appears to freeze whenever Disconnect is called.  I have written a test application that just calls TSwitchReader.Connect, and then TSwitchReader.Disconnect - even this freezes.

Any ideas?

I am running Delphi 7.

type
  TSwitchReader = class(TComponent)
  private
    fTelnet         : TIdTelnet;
    fLogonList      : TStringList;
    fLogon          : Boolean;
    fCurrentCommand : String;

    procedure TelnetStatus(ASender: TObject; const AStatus: TIdStatus;
      const AStatusText: String);
    procedure TelnetDataAvailable(Sender: TIdTelnet; const Buffer: String);
    procedure TelnetConnected(Sender: TObject);
    procedure TelnetDisconnected(Sender: TObject);

    function GetLast(Text: String): String;
  protected
    fBuffer: String;
    fCommandEnd: String;
    fEnablePassword: String;
    fHost: String;
    fTelnetPassword: String;

    fOnCompleted: TCompletedEvent;
    fOnConnected: TNotifyEvent;
    fOnDisconnected: TNotifyEvent;
    fOnStatus: TStatusChangeEvent;

    procedure SetHost(Value: String);
  public
    constructor Create(Owner: TComponent); override;
    destructor Destroy; override;

    procedure Connect;
    procedure Disconnect;
    procedure DoCommand(Command: String);
  published
    property EnablePassword: String read fEnablePassword write fEnablePassword;
    property Host: String read fHost write SetHost;
    property TelnetPassword: String read fTelnetPassword write fTelnetPassword;

    property OnCompleted: TCompletedEvent read fOnCompleted write fOnCompleted;
    property OnConnected: TNotifyEvent read fOnConnected write fOnConnected;
    property OnDisconnected: TNotifyEvent read fOnDisconnected write fOnDisconnected;
    property OnStatus: TStatusChangeEvent read fOnStatus write fOnStatus;
  end;

implementation

constructor TSwitchReader.Create;
begin
  inherited;
  fTelnet := TIDTelnet.Create(Self);
  fLogonList := TStringList.Create;

  // Setup out instance of the Indy Telnet client
  fTelnet.OnStatus := TelnetStatus;
  fTelnet.OnDataAvailable := TelnetDataAvailable;
  fTelnet.OnConnected := TelnetConnected;
  fTelnet.OnDisconnected := TelnetDisconnected;
  fLogon := False;
end;

destructor TSwitchReader.Destroy;
begin
  // Free all items used!
  fTelnet.Free;
  fLogonList.Free;
  inherited;
end;

procedure TSwitchReader.TelnetStatus(ASender: TObject; const AStatus: TIdStatus;
  const AStatusText: String);
begin
  // Pass status from Indy Client to our event
  if Assigned(fOnStatus) then
    fOnStatus(Self, AStatus, AStatusText);
end;

procedure TSwitchReader.TelnetDataAvailable(Sender: TIdTelnet; const Buffer: String);
var
  newBuffer: String;
begin
  // Cisco puts --more-- breaks in its readouts, this often breaks up input and the buffer
  // sometimes starts with garbage - if this is the case, trim off non printable characters.
  if (Length(Buffer) > 0) then begin
    if (ord(Buffer[1]) < 32) then
      newBuffer := TrimLeft(Buffer)
    else
      newBuffer := Buffer;
  end;

  // If we get a --more-- break, remove it from the buffer and send a ' ' to continue output.
  if LowerCase(Trim(GetLast(newBuffer))) = '--more--' then begin
    newBuffer := Copy(newBuffer, 1, Length(newBuffer)-10);
    fTelnet.write(' ')
  end;

  // Append to overall buffer until this command is complete.
  fBuffer := fBuffer + newBuffer;

  // Logon process
  if (fLogon) and (fLogonList.Count = 1) then begin
    fLogonList.Delete(0);
    // Set the syntax for the end of command.
    fCommandEnd := Trim(GetLast(Buffer));
    // Kill buffer so we don't flag the end of a command.
    newBuffer := '';
    // We are now logged on
    fLogon := False;
    if Assigned(fOnConnected) then fOnConnected(Self);
  end;
 
  // More logon stuff
  if (fLogon) and (GetLast(Buffer) = 'Password: ') then begin
    if (fLogonList.Count > 0) then
      fLogonList.Delete(0);
    if (fLogonList.Count > 0) then
      DoCommand(fLogonList.Strings[0]);
  end;

  // Command finished send out OnCompleted event.
  if (fLogon = False) and (GetLast(newBuffer) = fCommandEnd) then begin
    // Strip out our own echo, and the switch prompt after command completed
    if (LeftStr(fBuffer,Length(fCurrentCommand)) = fCurrentCommand) then
      fBuffer := Copy(fBuffer, Length(fCurrentCommand) + 1, Length(fBuffer));
    fBuffer := Copy(fBuffer, 1, Length(fBuffer) - Length(fCommandEnd));
    if Assigned(fOnCompleted) then fOnCompleted(Self, fBuffer);
  end;
end;

procedure TSwitchReader.TelnetConnected(Sender: TObject);
begin
  // Order of logon is:
  // 1) Give Telnet password
  // 2) Enter enable mode
  // 3) Give Enable password

  fLogonList.Add(fTelnetPassword);
  fLogonList.Add('enable');
  fLogonList.Add(fEnablePassword);
  fLogon := True;

  // Start log on going
  DoCommand(fLogonList.Strings[0]);
end;

procedure TSwitchReader.TelnetDisconnected(Sender: TObject);
begin
  // Disconnected, signal our own event for this.
  if Assigned(fOnDisconnected) then
    fOnDisconnected(Self);
end;

function TSwitchReader.GetLast(Text: String): String;
var
  List: TStringList;
begin
  // Get last line of a large block of text.
  List := TStringList.Create;
  List.Text := Text;
  if List.Count > 0 then
    Result := List.Strings[List.Count-1];
  List.Free;
end;

procedure TSwitchReader.SetHost(Value: String);
begin
  fHost := Value;
  fTelnet.Host := Value;
end;

procedure TSwitchReader.Connect;
begin
  // Start Telnet Session
  fTelnet.Connect(0);
end;

procedure TSwitchReader.Disconnect;
begin
  if fTelnet.Connected then begin
    fTelnet.Disconnect;
  end;
end;

procedure TSwitchReader.DoCommand(Command: String);
begin
  // Clear our own buffer ready for Indy's buffer to be appended to.
  fBuffer := '';
  fCurrentCommand := Command;
  fTelnet.WriteLn(Command);
end;
0
SpannerBracket
Asked:
SpannerBracket
  • 4
  • 3
1 Solution
 
BlackTigerXCommented:
have you tried dropping a
IdAntiFreeze component in your form?
(Indy Misc tab)
0
 
TheRealLokiSenior DeveloperCommented:
trydoing
    IdTelnet1.Disconnect(True);  // as in Disconnect(AImmediate: boolean);
calling this overloaded method (doesn't appear to matter whether you specify true or false) calls an overloaded procedure instead

btw, are you using Indy 9, or 10, or latest snapshot?
0
 
SpannerBracketAuthor Commented:
How do I tell what version I have - I couldn't find it any of the source.  Either way, im downloading the latest 10.0.74 and will try with that.

Couldn't find much info on IdAntiFreeze - I created one in my component - should I have to do anything else with it?
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
SpannerBracketAuthor Commented:
Oh,

when I tried calling Disconnect(True) - it wouldn't compile, error - too many parameters for procedure Disconnect.
0
 
TheRealLokiSenior DeveloperCommented:
Rught click on your TIdTelnet and it will tell you in the "about"
Indy 10 still has bugs, but they say to use it because indy 9 has more bugs - up to you.
Also, a lot of things changed between 9 and 10
If you have other Indy apps uding Indy 9 (e.g. relyingon TCPServer etc) I'd recommend not upgrading unless you want to undergo the learning curve for changing those apps too.

FWIW, I'm using the latest Indy 10, and have tried updating the old Indy Telnet Demo, doing a connect to my cisco router, and disconnecting, and then closing the app, and it works ok
0
 
SpannerBracketAuthor Commented:
I found I had an old version of the Indy clients - so I removed them and installed the new version.  Had to uninstall the whole IDE in the end to get the new version to work properly.

I made the few changes needed to get the application working - but I now get errors about a Canvas, and get dropped out in IDTelnet.pas where the OnDataAvailable event is fired.  This is intermittent, sometimes it just works, but then I still get the freeze on disconnect.  On the few occasions it doesn't freeze, I cant close my application - Close, Application.Terminate do nothing, and Application.Destroy creates an error.

I imagine I am not using the IDTelnet component exactly how I should be, but I haven't time to troubleshoot such random errors, so I am going to try moving to the ICS telnet component instead.

Thanks for your help, Ill award the poitns anyway, as Ive learned a lot more about Indy clients!
0
 
TheRealLokiSenior DeveloperCommented:
Did you take a look at the OnDataAVailable event in the Indy Telnet demo?
I admit I had trouble with their telnet until I used the demo as a base for my app.
btw, I much prefer ICS too :-D
0
 
SpannerBracketAuthor Commented:
I must admit, I had trouble locating the demos, but I didn't spend a lot of time on it.  I got v frustrated on the Indy website as its not very well organized.

Last night I changed my component to use ICS telnet instead - it only needed minor modifications.  This works a treat with no freezes or errors!

I think me and Indy just dont mix!
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 4
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now