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"