Ext

A small library for organizing interprocess communication (IPC) between software components implemented in different programming languages.

The main purpose of Ext is to provide the ability to call functions implemented in individual executable processes with minimal infrastructure requirements and without the need for network protocols or complex IPC mechanisms.


The solution is based on the concept of dividing the application functionality into client and server processes, where the client initiates the execution of operations, and the server processes requests and returns results. Communication between processes is carried out through two text files used as channels for transmitting commands and responses. Messages are transmitted in JSON format.

The main process (we will call it the client process) starts, when necessary, another process (the server process) and establishes a connection with it, which is carried out through two text files. The client can call functions in the server process and get the results.

Interaction can be carried out both synchronously and asynchronously

For a better understanding of the Ext concept, let's recall one of the similar solutions — UCI (Universal Chess Interface), a protocol for the interaction of chess GUIs with chess engines. Here

This concept can be useful in the following cases::

Usage examples

Simplest client programs:

in Harbour

FUNCTION Main

   LOCAL hExt
   // LOCAL cExe := "python test_srv_1.py"  /* if the server process is implemented in Python */
   LOCAL cExe := "test_srv_1.exe"

   // Run the server process
   hExt := ecli_run( cExe )
   IF Empty( hExt )
      ? "Can't launch " + cExe
      RETURN Nil
   ENDIF

   /* Send a command to server process to launch "fu1" function with given parameters
        and print the result */
   ? ecli_RunFunc( hExt, "fu1",{"test","10"} )
   ?

   RETURN Nil
  

in Python

import sys
sys.path.append('../source/python')
from extcli import ecli_Run, ecli_RunFunc, ecli_Close

def main():

    hExt = None
    exe_name = "test_srv_1.exe"

    # Run the server process
    hExt = ecli_Run(exe_name)
    if hExt == None:
        print(f"Can't launch {exe_name}")
        input("Press any key to continue...")
        return None

    # Send a command to server process to launch "fu1" function with given parameters
    #    and print the result
    print( ecli_RunFunc(hExt, "fu1", ("test","10")) )

    return None

if __name__ == "__main__":
    main()
  

in Go

package main

import (
	"fmt"
	ecli "tests/go/extcli"
)

func main() {
	var exeName = "test_srv_1.exe"

	// Run the server process
	hExt := ecli.Init(exeName, 1, "", "")
	if hExt == nil {
		fmt.Println( "Can't launch program" )
		return
	}

	// Send a command to server process to launch "fu1" function with given parameters
	//   and print the result
	fmt.Println( ecli.RunFunc( hExt, "fu1", []string{"test","10"}, false ) )

	ecli.Close(hExt)
}
  

Appropriate server programs:

in Harbour

FUNCTION Main( ... )

   LOCAL hExt

   // At first, we call esrv_Init() to initialize the connection with a calling process
   hExt := esrv_Init( hb_arrayToParams( hb_aParams() ) )
   IF Empty( hExt )
      RETURN Nil
   ENDIF

   // Now we wait for commands from caling process
   esrv_Wait( hExt )

   RETURN Nil

// A function to be called from client
FUNCTION FU1( aParams )
   RETURN "fu1: " + Time() + " " + aParams[1] + " " + aParams[2]
  

in Python

import sys
sys.path.append('../source/python')
from extsrv import esrv_Init, esrv_Wait

# A function to be called from client
def fu1( aparams ):
    return "fu1: " + aparams[0] + " " + aparams[1]

def main( aparams ):

    # At first, we call esrv_Init() to initialize the connection with a calling process
    hExt = esrv_Init(aparams)
    if hExt == None:
        return None

    # Now we wait for commands from caling process
    esrv_Wait(hExt)

    return None

if __name__ == "__main__":
    main( sys.argv[1:] )
  

in Go

package main

import "os"
import esrv "tests/go/extsrv"

func main() {

	var sParam = ""

	// Registration of functions, which may be launched on request of a calling process
	esrv.RegFunc("fu1", fu1)
	
	// We need to create a string from arguments to pass it to esrv.Init()
	for i := 1; i < len(os.Args); i++ {
		sParam = sParam + "\n" + os.Args[i]
	}
	// Init() function to initialize connection with calling process
	if esrv.Init(sParam) != 0 {
		esrv.WriteLog( "Init failed" )
		return
	}

	// Now we wait for commands from caling process
	esrv.Wait()
	
	// And, finally, we need to close connection - close handles, etc.
	esrv.Exit()
}

// A function to be called from client
func fu1(p []string) string {
	return "fu1 is launched " + p[0] + " " + p[1]
}
  

Documentation

Detailed instructions and code examples are available in project repository.

Please also pay attention to the article in the notes.




Comments:       ()       prev.    next.       Add comment
The length of a comment - no more than 4000 characters.
Your name:

Email address:
(not be shown publicly)
 
Input text from an image: