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 removedAnonymous
May 19, 2007
The comment has been removedAnonymous
January 22, 2009
The comment has been removedAnonymous
June 13, 2009
PingBack from http://homelightingconcept.info/story.php?id=183Anonymous
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