Use Named Pipes to communicate between processes or machines

Pipes have been useful for decades for interprocess communication. At a Windows Command prompt, you can type “dir | more”, which just creates a pipe between the DIR command and the MORE command. The standard output for the left side of the pipe is redirected through a pipe to the standard input of the MORE command.

You can use the CreateNamedPipe function to create a named pipe to communicate easily between 2 processes, perhaps on different machines.

Run the sample below. It creates some code for a “server” to run, then creates an instance of VFP as the server and runs it. The server creates a named pipe and waits for a request for data.

The client code then calls the Named pipe with a parameter of “A” to get some data. The server gets the request and responds by sending a string back to the client.

To run the server on machine B, you can copy the code to machine B, then change the code so the server code always runs (change the “IF _vfp.StartMode line to “IF .t.” so it always runs)

On the client, change the pipe name to the name of the server. For me, I changed it to:

cPipeName="\\calvinh6\pipe\mypipe" && the name of the server is calvinh6

Keep the pipename on the server as \\.\pipe\mypipe

CLEAR ALL

CLEAR

*from Winbase.h:

#define INVALID_HANDLE_VALUE -1

#define FILE_FLAG_WRITE_THROUGH 0x80000000

#define FILE_FLAG_OVERLAPPED 0x40000000

#define FILE_FLAG_NO_BUFFERING 0x20000000

#define FILE_FLAG_RANDOM_ACCESS 0x10000000

#define FILE_FLAG_SEQUENTIAL_SCAN 0x08000000

#define FILE_FLAG_DELETE_ON_CLOSE 0x04000000

#define FILE_FLAG_BACKUP_SEMANTICS 0x02000000

#define FILE_FLAG_POSIX_SEMANTICS 0x01000000

#define FILE_FLAG_OPEN_REPARSE_POINT 0x00200000

#define FILE_FLAG_OPEN_NO_RECALL 0x00100000

#define FILE_FLAG_FIRST_PIPE_INSTANCE 0x00080000

#define NMPWAIT_WAIT_FOREVER 0xffffffff

#define NMPWAIT_NOWAIT 0x00000001

#define NMPWAIT_USE_DEFAULT_WAIT 0x00000000

#define CREATE_NEW 1

#define CREATE_ALWAYS 2

#define OPEN_EXISTING 3

#define OPEN_ALWAYS 4

#define TRUNCATE_EXISTING 5

#define PIPE_ACCESS_INBOUND 0x00000001

#define PIPE_ACCESS_OUTBOUND 0x00000002

#define PIPE_ACCESS_DUPLEX 0x00000003

#define PIPE_CLIENT_END 0x00000000

#define PIPE_SERVER_END 0x00000001

#define PIPE_WAIT 0x00000000

#define PIPE_NOWAIT 0x00000001

#define PIPE_READMODE_BYTE 0x00000000

#define PIPE_READMODE_MESSAGE 0x00000002

#define PIPE_TYPE_BYTE 0x00000000

#define PIPE_TYPE_MESSAGE 0x00000004

#define PIPE_UNLIMITED_INSTANCES 255

*From winerror.h:

#define ERROR_FILE_NOT_FOUND 2

#define ERROR_INVALID_HANDLE 6

#define ERROR_BROKEN_PIPE 109

#define ERROR_BAD_PIPE 230

#define ERROR_PIPE_BUSY 231

#define ERROR_NO_DATA 232

#define ERROR_PIPE_NOT_CONNECTED 233

#define ERROR_MORE_DATA 234

DECLARE integer CreateNamedPipe IN WIN32API ;

  string lpPipeName,;

  integer dwOpenMode,;

  integer dwPipeMode,;

  integer nMaxInstances,;

  integer nOutBufferSize,;

  integer nInBufferSize,;

  integer nDefaultTimeOut,;

  integer lpSecurityAttributes

DECLARE integer CallNamedPipe IN WIN32API ;

      string lpPipeName,;

      string lpInBuff,;

      integer nInBuffSize,;

      string @ lpOutBuff,;

      integer nOutBuffSize,;

      integer @lpBytesRead,;

      integer nTimeout

     

DECLARE integer ConnectNamedPipe IN WIN32API integer hNamedPipe, string @ lpOverLapped

DECLARE integer SetNamedPipeHandleState IN WIN32API integer hNamedPipe,;

      integer @ lpMode,;

      integer @ lpMaxCollectionCount,;

      integer @ lpCollectDataTimeout

DECLARE integer PeekNamedPipe IN WIN32API integer hNamedPipe, string @ lpBuffer, integer nBuffSize, ;

            integer @ lpBytesRead,;

            integer @ lpTotalBytesAvail,;

            integer @ lpBytesLeftThisMessage

DECLARE integer ReadFile IN WIN32API integer hFile, string @ lpBuffer, ;

      integer nBytesToRead, integer @ nBytesRead, string @ lpOverLapped

DECLARE integer WriteFile IN WIN32API integer hFile, string lpBuffer, ;

      integer nBytesToWrite, integer @ nBytesWritten, string @ lpOverLapped

DECLARE integer CloseHandle IN WIN32API integer

DECLARE integer GetLastError IN win32api

cPipeName="\\.\pipe\mypipe"

*cPipeName="\\calvinh6\pipe\mypipe"&& the name of the server is calvinh6

IF _vfp.StartMode >0 && if we're on the server

      * IF ConnectNamedPipe(hPipe,0) > 0 && wait for client to connect

      * ?"connected"

      * ENDIF

      DO WHILE .t.

            hPipe = CreateNamedPipe(cPipeName,;

                  PIPE_ACCESS_DUPLEX + FILE_FLAG_FIRST_PIPE_INSTANCE, ;

                  PIPE_TYPE_MESSAGE + PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,100,100,100,0)

            ?"hp=",hPipe

            IF hPipe=INVALID_HANDLE_VALUE

                  ?"CreateNamedPipe GetLastError",GetLastError(),DATETIME()

            ELSE

                  DO WHILE !CHRSAW(.5)

                        IF PeekNamedPipe(hPipe,0,0,0,0,0)=0

                              ?"Peek no data ",GetLastError(),DATETIME()

                        ELSE

                              ?"Peek got data",DATETIME()

                              cBuff=SPACE(1) && read 1 char from pipe

                              dwRead = 0

                              IF ReadFile(hPipe,@cBuff,LEN(cBuff),@dwRead,0)>0

                                    ?"data read= ",cBuff

                                    DO CASE

                                    CASE cBuff='A'

                                          nBytes=0

                                          cResult=TRANSFORM(DATETIME())+" From Server "+GETENV("COMPUTERNAME")

                                          IF WriteFile(hPipe,cResult, LEN(cResult),@nBytes,0)=0 OR nBytes != LEN(cResult)

                                                ?"Write File GetLastError",GetLastError()

                                          ENDIF

                                    ENDCASE

                              ENDIF

                              CloseHandle(hPipe)

                              hPipe=0

                              INKEY(.5)

                              EXIT

                        ENDIF

                  ENDDO

                  IF hPipe>0

                        CloseHandle(hPipe)

                  ENDIF

                  IF INKEY()>0

                        IF _vfp.StartMode>0

                              QUIT && if we were started on same machine

                        ELSE

                              EXIT

                        ENDIF

                  ENDIF

            ENDIF

      ENDDO

      RETURN

ENDIF

*Client code: create the server and have it DO this program

IF LEFT(cPipeName,3)="\\." && if on the same machine, start the server

      LOCAL ovfp as VisualFoxpro.Application

      ovfp=CREATEOBJECT("VisualFoxpro.Application")

      ovfp.visible=1

      ovfp.left=0

      ovfp.DoCmd("set development off") && server doesn't need to compile to fxp

      INKEY(.5)

      ovfp.DoCmd("keyboard 'do " + PROGRAM()+"'+CHR(13)") && start the prg asynchronously

      INKEY(2)

ENDIF

FOR i = 1 TO 5

      cBuf=SPACE(50)

      nBytes=0

      IF CallNamedPipe(cPipeName,"A",1,@cbuf,LEN(cBuf),@nBytes,NMPWAIT_USE_DEFAULT_WAIT)>0

            ?"Got data: ",cBuf

      ELSE

            ?"CallNamedPipe GetLastError",GetLastError()

      ENDIF

      INKEY(.5)

ENDFOR

?"done"