Partager via


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"

Comments

  • Anonymous
    December 06, 2005
    great post.... it's amazing a wrapper classes doesn't already exist in the bcl.

  • Anonymous
    December 06, 2005
    写地太好了
    使我大开眼界
    希望能写得再深入些

  • Anonymous
    December 07, 2005
    Translation of last comment(from Sam):
    "This article is great, and I learned a lot from it. Looking forward to seeing such article with deeper discussion."

  • Anonymous
    December 08, 2005
    Man that's deep enough for me :-) What a delight every time i come and visit this blog!!

  • Anonymous
    December 08, 2005
    It seems very interesting Calvin. I just fast read your code. You actually develop things AND post in this blog!!?? You must be extremely productive! Bravo for your hard work...!
    Something irrelevant.. Sometime ago I had used VB and DirectX8 to create an ActiveX control that would only show an image. I had tryed to call it from VFP (by giving the _Screen.hwnd) and the image was displayed fine. I wonder if it would be possible to show in a cotrol (inside a form like activex controls do, or in its own window) direct3d scenes. With simple lighting and a few objets, an provide a way to pass data to this control, eg. commands to move, or rotate objects, or pick projects etc. What would be the biggest problem, and do you think it can be done ? (I 'm asking this question, cause I know that you have a better understanding of how VFP works) Thanks, anyway...

  • Anonymous
    January 27, 2006
    To answer to my own question above... It can be done. I created an activex control with atl in c++, and displayed a simple triangle in it. Everything seems ok. Sorry for my message being a little offtopic. (but it has to do with VFP... right? :-)

  • Anonymous
    February 17, 2006
     
    Programs need to read and write data. Sometimes the data storage is only used temporarily. If...

  • Anonymous
    February 17, 2006
    The comment has been removed

  • Anonymous
    May 19, 2007
    The comment has been removed

  • Anonymous
    January 22, 2009
    The comment has been removed

  • Anonymous
    June 13, 2009
    PingBack from http://homelightingconcept.info/story.php?id=183

  • Anonymous
    June 08, 2015
    Hi Calvin did you ever encounter the situation where PeekNamedPipe returns 109 ERROR_BROKEN_PIPE? I couldn't find any docs on that Blog: http://doctorpapadopoulos.com