Jump to content
bernhard_LA

use indy in a console application

Recommended Posts

the windows GUI code  for a simple INDY 10 tcp demo comes from here :    https://github.com/tinydew4/indy-project-demos/tree/master/TCPIP Delphi %26 Indy10 Client Server Demo/1_sample Simple String Exchange 

 

I used this code  to run the server in a cmd line application, but I fail with the function pointer assignment,  what is the correct syntax I need here

 

see the current not working code  below 

 


 

program IndyConsoleApp;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, IdContext, IdSync, IdBaseComponent, IdComponent, IdCustomTCPServer,
  IdTCPServer, IdGlobal;

  type  OnTCPServerExecutefct = procedure (AContext: TIdContext) of Object ;

var IdTCPServer1: TIdTCPServer;
    TCPServerExecutefct : OnTCPServerExecutefct;




procedure ShowStartServerdMessage;
begin
  writeln('START SERVER  @' + TimeToStr(now));
end;

procedure StopStartServerdMessage;
begin
   writeln('STOP SERVER  @' + TimeToStr(now));
end;
procedure  TCPServerExecute(AContext: TIdContext);
var
  LLine: String;
begin
  TIdNotify.NotifyMethod( ShowStartServerdMessage );  // wrong 
  LLine := AContext.Connection.IOHandler.ReadLn();
  writeln(LLine);
  AContext.Connection.IOHandler.WriteLn('OK');
  TIdNotify.NotifyMethod( StopStartServerdMessage );
end;

begin
  IdTCPServer1:=TIdTCPServer.Create;
  try
    { TODO -oUser -cConsole Main : Insert code here }
      IdTCPServer1.Bindings.Add.IP   := '127.0.0.1';
      IdTCPServer1.Bindings.Add.Port := 6000;
      IdTCPServer1.OnExecute :=  TCPServerExecute ;  ///  wrong !

    //   TCPServerExecutefct:= assign(TCPServerExecute);
    readln ;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

 

Edited by bernhard_LA

Share this post


Link to post

An easy way would be to create and use a data module, then you can put your Indy components on it and use the IDE designer to write your event handlers.

Share this post


Link to post

A simple way is to declare a new class, which has a private field IdTCPServer of type TIdCustomTCPServer and a method which matches the signature of the IdTCPServer.OnExecute event handler:

TMyClass = class(TObject)
private
  IdTCPServer: TIdCustomTCPServer;
  
  procedure MyOnExecute(AContext: TIdContext);

public
  constructor Create;
  destructor Destroy; override;
  
  procedure Run;
  
...
constructor TMyClass.Create;
begin
  IdTCPServer := TIdCustomTCPServer.Create;
  IdTCPServer.OnExecute := MyOnExecute;
end;  

Instantiate this class and let it create, configure and start the IdTCPServer instance.

Share this post


Link to post

Indy Callbacks are declared with of object modifier, that means you have to have a method in an object and not a standalone procedure.

 

TCallback1 = procedure (const Arg1: Integer);
TCallback2 = procedure (const Arg1: Integer) of object;
TCallback3 = reference to procedure (const Arg1: Integer);

TCallback1 is used with standalone procedures.

TCallback2 is used with methods from objects.

TCallback3 can be used with anonymous methods.

Share this post


Link to post
5 hours ago, bernhard_LA said:

I used this code  to run the server in a cmd line application, but I fail with the function pointer assignment,  what is the correct syntax I need here

Like the events of most other components, the events of Indy components expect class methods, not standalone functions.

 

You could go the way that others have recommended, where creating a TDataModule at design-time and using it at run-time would be the easiest.

 

But, even if you were to simply write a class in code to wrap the events, note that you don't actually need to instantiate such a class at run-time, you can declare its methods with the class directive instead, eg:

program IndyConsoleApp;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, IdContext, IdBaseComponent, IdComponent, IdCustomTCPServer,
  IdTCPServer, IdGlobal;

var
  IdTCPServer1: TIdTCPServer;

procedure ShowStartServerdMessage;
begin
  WriteLn('START SERVER  @' + TimeToStr(now));
end;

procedure StopStartServerdMessage;
begin
  WriteLn('STOP SERVER  @' + TimeToStr(now));
end;

type
  TCPServerEvents = class
    class procedure OnExecute(AContext: TIdContext);
  end;

class procedure TCPServerEvents.OnExecute(AContext: TIdContext);
var
  LLine: String;
begin
  LLine := AContext.Connection.IOHandler.ReadLn();
  writeln(LLine);
  AContext.Connection.IOHandler.WriteLn('OK');
end;

begin
  try
    IdTCPServer1 := TIdTCPServer.Create;
    try
      with IdTCPServer1.Bindings.Add do
      begin
        IP   := '127.0.0.1';
        Port := 6000;
      end;
      IdTCPServer1.OnExecute := TCPServerEvents.OnExecute;
      IdTCPServer1.Active := True;
      try
        ShowStartServerdMessage;
        Readln;
      finally
        IdTCPServer1.Active := False;
        StopStartServerdMessage;
      end;
    finally
      IdTCPServer1.Free;
    end;
  except
    on E: Exception do
      WriteLn(E.ClassName, ': ', E.Message);
  end;
end.

However, there IS actually a way to use a standalone function instead, but it involves a little trickery using Delphi's TMethod record:

program IndyConsoleApp;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, IdContext, IdBaseComponent, IdComponent, IdCustomTCPServer,
  IdTCPServer, IdGlobal;

var
  IdTCPServer1: TIdTCPServer;

procedure ShowStartServerdMessage;
begin
  WriteLn('START SERVER  @' + TimeToStr(now));
end;

procedure StopStartServerdMessage;
begin
  WriteLn('STOP SERVER  @' + TimeToStr(now));
end;

procedure TCPServerExecute(ASelf: Pointer; AContext: TIdContext); // NOTE THE EXTRA PARAMETER!
var
  LLine: String;
begin
  LLine := AContext.Connection.IOHandler.ReadLn();
  WriteLn(LLine);
  AContext.Connection.IOHandler.WriteLn('OK');
end;

var
  ExecuteFct: TMethod;
begin
  try
    IdTCPServer1 := TIdTCPServer.Create;
    try
      with IdTCPServer1.Bindings.Add do
      begin
        IP   := '127.0.0.1';
        Port := 6000;
      end;

      ExecuteFct.Data := nil; // or anything you want to pass to the ASelf parameter...
      ExecuteFct.Code := @TCPServerExecute;
      IdTCPServer1.OnExecute := TIdServerThreadEvent(ExecuteFct);

      IdTCPServer1.Active := True;
      try
        ShowStartServerdMessage;
        Readln;
      finally
        IdTCPServer1.Active := False;
        StopStartServerdMessage;
      end;
    finally
      IdTCPServer1.Free;
    end;
  except
    on E: Exception do
      WriteLn(E.ClassName, ': ', E.Message);
  end;
end.

 

Edited by Remy Lebeau
  • Like 2
  • Thanks 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×