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 (12)

  1. Hasani says:

    great post…. it’s amazing a wrapper classes doesn’t already exist in the bcl.

  2. lugreen says:

    写地太好了

    使我大开眼界

    希望能写得再深入些

  3. Calvin_Hsia says:

    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."

  4. Jaime B. Pinto says:

    Man that’s deep enough for me 🙂 What a delight every time i come and visit this blog!!

  5. Theof says:

    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…

  6. Theof says:

    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? 🙂

  7.  

    Programs need to read and write data. Sometimes the data storage is only used temporarily. If…

  8. gor says:

    Thanks for enlightment. What are the requirements for a named pipe between different machines? Need they be in the same domain? What’s the advantage over a TCP/IP connection?

  9. jeremy says:

    What flags need to be set to send *Binary* data over a named-pipe?

  10. Savas Papadopoulos says:

    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