Virtual Developer Workshop: Containerized Development with Docker
Environment: VC6 SP3, NT4 SP5
IntroductionIn a project I needed inter process communication and the use of named pipes seamed to be the most appropriate solution as well as the recommended when I discussed the problem (and requirements) with an other experienced developer.
Hence I started to search MSDN and CodeGuru for some nice reusable code using named pipes. At CodeGuru I found nothing usable and the code found in MSDN was ugly and not easy to reuse. Anxious to get progress, I started experimenting my self to get something that worked for me.
What I needed was for one server to communicate with several other processes and to keep it simple the server and one client only should share the same pipe. Both server and client should be able to communicate with the other at any given time. The result is the class CNamedPipe that actually uses two "one-way"-pipes and a listener thread on each side (i.e. Server and Client side). And I had the advantage of knowing that the server always has created the pipes correctly before the client tries to open them (saves me from some error handling). See the source file for further details on implementation.
Basic pipingThe named pipe must have a name. It looks like this:
The bold parts (including back slashes) are mandatory. ServerName should be a dot (i.e. \\.\PIPE\...) if the pipe is created (the creator will be called the server side of the pipe or the computer name if the name is to be used by a client.
pipe is a name used to uniquely identify a pipe on a certain computer. Note that pipe names are always case insensitive.
The server side of a pipe is the creator of a pipe and the pipe may be created with read, write or read/write access. For full information on how to use different options, check MSDN help file for CreateNamedPipe.
CNamedPipe creates a pipe like this:
m_hInPipe = CreateNamedPipe( GetRealPipeName(true).c_str(), //Gets the Pipename PIPE_ACCESS_INBOUND, //Server will read from this pipe //Client will write PIPE_WAIT, //Do not return Read/write calls //until read/write is complete 1, //allow only 1 instance of this pipe PIPE_BUF_SIZE, //Macro defined by me. PIPE_BUF_SIZE, //Macro defined by me. PIPE_TIMEOUT, //Macro defined by me. NULL); //Do not inherit security.
A client then connects to the pipe like this:
m_hOutPipe = CreateFile(GetRealPipeName(true).c_str(), //Gets the Pipename GENERIC_WRITE, //Client only writes to this pipe. 0, //Do not share this pipe with others. NULL, //Do not inherit security. OPEN_EXISTING, //Pipe must exist. FILE_ATTRIBUTE_NORMAL, //I have no special requirements on //file attributes NULL); //This parameter not used since pipe //created with PIPE_WAIT.
When using pipes it is important that the client opens the pipe with correct options. If the server will only read from the pipe, the client must open it as write only etc.
When reading and writing from pipes, one just uses the functions ReadFile and WriteFile. If the PIPE_WAIT flag is used, the ReadFile function will not return until something is written to the pipe and vice versa. Hence it is important to create a separate thread which tries to read from the pipe. This thread will be waiting until a WriteFile operation is made by the other side.
Features in CNamedPipe
bool Initialize(bool bAsServer, string szStopListnenCmd, void (WINAPI *fCallBack)(string buf))
Initialize must be called to create/open the pipes. The server must be initialized first. To Initialize the server, parameter bAsServer should be true (hence false when initializing the client). The szStopListnenCmd parameter is used to tell the listener thread to stop listen. When the listening thread receives a message identical to this parameter (case sensitive), the thread will exit its listening loop. The last parameter is a function pointer and this function is called for every completed successful ReadFile the listening thread performs. The buffer read will be the argument of the call.
SetPipeName and GetRealPipeName are used to set and get the used names. SetPipeName takes two arguments and makes a string like this:
GetRealPipeName uses the constructed string and adds "_IN" or "_OUT" depending on its argument. So the used names are:
Server reads from: \\<arg2>\PIPE\<arg1>_IN
Server writes to: \\<arg2>\PIPE\<arg1>_OUT
PipeTest - a demo of CNamedPipe
Only one client per server is possible. Server name must be the server computer's entry in DNS.
To see examples of the use of CNamedPipe, check the following functions in class CPipeTestDlg:
- OnBConnect - Creating pipes...
- OnBSend - Send a message to the pipe...
- funcCallBack - implementation of the callback function called by the CNamedPipe listener thread.
DownloadsDownload demo project - 18 Kb
Download source - 3 Kb