How to communicate between Delphi Windows Desktop Application and Delphi Windows Service Application

I have already posted how to prepare a Windows Service Application in Delphi  in my blog. And this time I am adding how to communicate between a Delphi Windows Desktop Application and Delphi Windows Service Application.

For this I have used IndyTCP Server/Client components to communicate with a Server that deployed as a Windows Service.

A little description about Indy TCP Server/Client Components.
In this blog I have referenced only Indy TCP Server(TIdTCPServer) and Client(TIdTCPClient) components. Indy works in a way to have the client (TidTCPClient) connect to the server (TidTCPServer) and then exchange data between them, back-and-forth until the connection is terminated either willfully or by premature disconnect.

For in detail explanation I have prepared a Server application which can be deployed as Windows Service and a desktop Client application. Desktop application can connect will server application and send request. Then server application can read a client's request data and process on it. Then Server can send a response to Client as per request. In this example I have just passed String data between Client and Server but we can also pass different type of  data by using Buffer and Stream type. You can find a good example in following link.

Delphi TidTCPServer and TidTCPClient transferring a record

Now my Examples...
In my example I have just created sample Add, Subtract, Multiply and divide function for two passed values. So to get the Server result Client application need to pass request  in following format. However we can change the data format as per our need.
Method;Param1;Param2

Preparing a Windows Service Application with TIdTCPServer component which will act as a server and can response a client request.

To create a service application in Delphi start Delphi IDE and select File->New->Other. Then select "Service Application". Then you can see application which includes a Datamodule descendent Tservice class.


Then put a TIdTCPServer component on TService data module like follow

Following is code of Service application Server form ... But still you face any issue during create a service application please visit Windows Service Application in Delphi  
unit TestService;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs,
  IdContext, IdServerIOHandler, IdServerIOHandlerSocket, IdServerIOHandlerStack,
  IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer;
type
  TService1 = class(TService)
    IdTCPServer1: TIdTCPServer;
    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure IdTCPServer1Execute(AContext: TIdContext);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
    procedure ServiceShutdown(Sender: TService);
  private
    { Private declarations }

    // PROCEDURE TO PARSE REQUEST PARAMETERS // IN THIS CASE i AM FOLLOWING MATHODNAME;PARAM1;PARAM2 //
    procedure ParseInData(strMain: string; var slMain: TStringList);

    // SERVER MATHOD TO ADD 2 VALUE //
    function Add(var A: Integer; var B: Integer): Integer;
    // SERVER MATHOD TO sub 2 VALUE //
    function Substract(var A: Integer; var B: Integer): Integer;
    // SERVER MATHOD TO sub 2 VALUE //
    function Multi(var A: Integer; var B: Integer): Integer;
    // SERVER MATHOD TO sub 2 VALUE //
    function Division(var A: Integer; var B: Integer): Integer;

  public
    function GetServiceController: TServiceController; override;
    { Public declarations }

  end;

var
  Service1: TService1;

implementation

{$R *.DFM}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;
// SERVER METHODS TO PROCESS REQUEST DATA FOR REPONSE //
function TService1.Add(var A, B: Integer): Integer;
begin
  Result := A + B;
end;

function TService1.Substract(var A, B: Integer): Integer;
begin
  Result := A - B;
end;

function TService1.Multi(var A, B: Integer): Integer;
begin
  Result := A * B;
end;

function TService1.Division(var A, B: Integer): Integer;
begin
  Result := A DIV B;
end;

function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure TService1.IdTCPServer1Execute(AContext: TIdContext);
var
  sRequest: string;
  slReqData: TStringList;
  sMethod: string;
  ival1, ival2: Integer;

  sResponse: string;
begin
  sRequest := AContext.Connection.Socket.ReadLn(); // READ CLIENT REQUEST
  // PARSE RQUEST DATA TO GET METHOD NAME AND PARAMAS //
  slReqData := TStringList.Create;
  ParseInData(sRequest, slReqData);
  try
    // PREPARE RESPONSE AS PER REQUEST //
    sResponse := '';

    try
      // GET MEHOTD NAME AND PARAMETER VALUES FORM PARSED STRING LIST //
      sMethod := UpperCase(slReqData.Strings[0]);
      ival1 := StrToIntDef(slReqData.Strings[1], 0);
      ival2 := StrToIntDef(slReqData.Strings[2], 0);

      if (sMethod = 'ADD') then // TO CALL ADD METHODS
        sResponse := IntToStr(Add(ival1, ival2))
      else if (sMethod = 'SUB') then // TO CALL SUBSTRACT METHODS
        sResponse := IntToStr(Substract(ival1, ival2))
      else if (sMethod = 'MULT') then // TO CALL MULTIPLICATION METHODS
        sResponse := IntToStr(Multi(ival1, ival2))
      else if (sMethod = 'DIV') then // TO CALL DIVISION METHODS
        sResponse := IntToStr(Division(ival1, ival2))
      else
        sResponse := 'Method not found';

    except
      // IF ANY EXCEPTION DURING PROCESS REQUEST DATA //
      sResponse := 'Bad REQUEST format. Please follow METHOD;PARAM1;PARAM2 format.';
    end;
  finally
    slReqData.Free;
    AContext.Connection.Socket.WriteLn(sResponse); // RESPONSE TO CLIENT
  end;

end;

procedure TService1.ParseInData(strMain: string; var slMain: TStringList);
begin
// METHOD TO PARSE REQUEST IN FORMAT METHD;PARAM1;PARAM2
  slMain.Clear;
  slMain.Delimiter := ';';
  slMain.DelimitedText := strMain;
end;

procedure TService1.ServiceShutdown(Sender: TService);
begin
// STOP INDY SERVER ON SERVICE SHUTDOWN//
  IdTCPServer1.StopListening;
  IdTCPServer1.Active := False;
end;

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
// START INDY SERVER ON SERVICE START//
  IdTCPServer1.DefaultPort := 12345;
  IdTCPServer1.Active := True;
  IdTCPServer1.StartListening;
end;

procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
// STOP INDY SERVER ON SERVICE STOP//
  IdTCPServer1.StopListening;
  IdTCPServer1.Active := False;
end;

end.

Then prepare build of Service application and then install the Service.exe with /silent mode. 

And after install Start the Service 












Now preparing a Windows Desktop Client Application with TIdTCPPClient  component which will communicate with the Server application that we just installed as a windows Service. 

Just create new Delphi VCL Application.


Then in form put a TIdTCPClient component. 

Following is code of client form...
unit Unit1;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent,
  IdComponent, IdTCPConnection, IdTCPClient, IdIOHandlerStream, IdIOHandler,
  IdIOHandlerSocket, IdIOHandlerStack, IdIntercept, IdContext,
  IdCustomTCPServer, IdTCPServer, IDGlobal, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    btnConnect: TButton;
    btnDisconnect: TButton;
    btnSend: TButton;
    btnClose: TButton;
    Memo1: TMemo;
    IdTCPClient1: TIdTCPClient;
    Shape1: TShape;
    Label1: TLabel;
    edtVal1: TEdit;
    edtVal2: TEdit;
    Label2: TLabel;
    Label3: TLabel;
    cmbMethod: TComboBox;
    procedure btnCloseClick(Sender: TObject);
    procedure btnConnectClick(Sender: TObject);
    procedure btnDisconnectClick(Sender: TObject);
    procedure btnSendClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnSendClick(Sender: TObject);
var
  sMethod : string;
  iVal1, iVal2: integer;
  sRequest: string;
  sResponse: string;
begin
// GET METHOD AND PARAMS VALUES //
  sMethod := 'NONE';
  if cmbMethod.ItemIndex=0 then
    sMethod := 'ADD'
  else if cmbMethod.ItemIndex=1 then
    sMethod := 'SUB'
  else if cmbMethod.ItemIndex=2 then
    sMethod := 'MULT'
  else if cmbMethod.ItemIndex=3 then
    sMethod := 'DIV'
  else
    sMethod := 'NONE';
// VALIDATE VALUE 1 //
  Try
    iVal1 := StrToInt(edtVal1.Text);
  except
    MessageDlg('Please check Value1.',mtError,[mbOK],0);
    Exit;
  End;
// VALIDATE VALUE 2 //
  Try
    iVal2 := StrToInt(edtVal2.Text);
  except
    MessageDlg('Please check Value2.',mtError,[mbOK],0);
    Exit;
  End;
  try
  // PREPARE REQUEST AND SEND TO SERVER //
    sRequest := sMethod + ';' + IntToStr(iVal1) + ';' + IntToStr(iVal2);
    IdTCPClient1.Socket.WriteLn(sRequest);
    Memo1.Lines.Add('REQUEST: '+sRequest);
  // READ RESPONSE FROM SERVER //
    sResponse := IdTCPClient1.Socket.ReadLn();
    Memo1.Lines.Add('REPONSE: '+sResponse);
  except
    MessageDlg('Please check that Server is ready', mtWarning, [mbOK], 0);
    Exit;
  end;
end;

procedure TForm1.btnCloseClick(Sender: TObject);
begin
// DISCONNECT CLIENT //
 IdTCPClient1.Disconnect;

 Close;
end;

procedure TForm1.btnConnectClick(Sender: TObject);
begin
// CONNECT CLIENT //
  IdTCPClient1.Host := '127.0.0.1';   // LOCALHOST // WE CAN CHANGE IP FOR DIFFERENT SERVER //
  IdTCPClient1.Port := 12345;       // IS PORT NUMBER THAT WE SET AT CLIENT SIDE TOO //
  IdTCPClient1.Connect;

  Shape1.Brush.Color :=  clGreen;
end;

procedure TForm1.btnDisconnectClick(Sender: TObject);
begin
// DISCONNECT CLIENT //
  IdTCPClient1.Disconnect;

  Shape1.Brush.Color :=  clRed;
end;

end.

After complete coding prepare build of Client application and execute the exe. 
Press connect to connect with Server and Send to send a request. 
Here I am following below request data format that I prepare at runtime during request send. However we can change the data format as per our need.
Method;Param1;Param2
REQUEST = DIV;300;50


yeah..  Thats all. Here we go....

And if you guys find any other way or using any other third party components the please share with me with comment. 

Comments

Post a Comment

Popular posts from this blog

ShellExecute in Delphi

How to send Email in Delphi?

Variants in Delphi. Use of Variant Array. How to check a Variant is unassigned or empty or clear ?