Inter-Process Communications

Inter-process communication are methods that allow two running processes to exchange information. We already have one such tool. It is memory mapping. Pipes are another such tool. They represent a connection between two programs that is analygous to a phone connection

Pipes - a means of having one running process talk to another. They use ReadFile and WriteFile to communicate.

Anonymous Pipes - byte oriented, half-duplex (one-direction), and supports communications between processes that are related. We will not discuss in that they are relatively weak and need knowledge that we have skipped.

Named Pipes - full duplex, message oriented and allows network communications. (Network stuff will only work among WIN32 machines.) WIN32 named pipes are impressive in that they demonstrates most of the features that a pipe might have. The following is a list of features:

  1. Message oriented. This eliminates the need for using fixed length messages or writing your own control code. Can be stream oriented.
  2. Bi-directional. This implies two directional flow of information. Messages may be sent back and forth between the processes.
  3. There may be multiple instances of a named pipe. Means that multiple processes can use a separate pipe of the same name to communicate with a server.
  4. Can be used across a network.
  5. Communication controlled by a well-defined and relatively simple API.

Client/Server model - the developers of named pipes had a client/server model in mind. Discuss this concept in general. So, with named pipes, it is expected that the server creates the named pipe and handles all connects and requests. The client connects to an existing named pipe, makes requests and get responses.

The following is the system call used to create a named pipe. I have edited it from the help page. The server may make this call multiple times to create multiple instances of the named pipe. A client can connect to each instance.

HANDLE CreateNamedPipe(

LPCTSTR lpName, // pointer to pipe name

DWORD dwOpenMode, // pipe open mode

DWORD dwPipeMode, // pipe-specific modes

DWORD nMaxInstances, // maximum number of instances

DWORD nOutBufferSize, // output buffer size, in bytes

DWORD nInBufferSize, // input buffer size, in bytes

DWORD nDefaultTimeOut, // time-out time, in milliseconds

LPSECURITY_ATTRIBUTES lpSecurityAttributes // pointer to security attributes

);

Return Values

If the function succeeds, the return value is a handle to the server end of a named pipe instance. The server will use this in all other system calls to access the pipe.

If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error information, call GetLastError. The return value is ERROR_INVALID_PARAMETER if nMaxInstances is greater than PIPE_UNLIMITED_INSTANCES. This value is 255.

Parameters

lpName
Pointer to the null-terminated string that uniquely identifies the pipe. The string must have the following form:
\\.\pipe\pipename
The pipename part of the name can include any character other than a backslash, including numbers and special characters. The entire pipe name string can be up to 256 characters long. Pipe names are not case sensitive.  (This makes since considering the way that microsoft manages file names.
dwOpenMode
Specifies the pipe access mode, the overlapped mode (not discussed), the write-through mode (not discussed), and the security access mode (not discussed) of the pipe handle.
CreateNamedPipe fails if dwOpenMode specifies any flags other than those listed in the following tables.
This parameter must specify one of the following pipe access mode flags. The same mode must be specified for each instance of the pipe:

Mode
Description
PIPE_ACCESS_DUPLEX The pipe is bidirectional; both server and client processes can read from and write to the pipe. This mode gives the server the equivalent of GENERIC_READ | GENERIC_WRITE access to the pipe. The client can specify GENERIC_READ or GENERIC_WRITE, or both, when it connects to the pipe using the CreateFile function.
PIPE_ACCESS_INBOUND The flow of data in the pipe goes from client to server only. This mode gives the server the equivalent of GENERIC_READ access to the pipe. The client must specify GENERIC_WRITE access when connecting to the pipe.
PIPE_ACCESS_OUTBOUND The flow of data in the pipe goes from server to client only. This mode gives the server the equivalent of GENERIC_WRITE access to the pipe. The client must specify GENERIC_READ access when connecting to the pipe.

dwPipeMode
Specifies the type, read, and wait modes of the pipe handle.
One of the following type mode flags can be specified. The same type mode must be specified for each instance of the pipe. If you specify zero, the parameter defaults to byte-type mode.

Mode
Description
PIPE_TYPE_BYTE Data is written to the pipe as a stream of bytes. This mode cannot be used with PIPE_READMODE_MESSAGE.
PIPE_TYPE_MESSAGE Data is written to the pipe as a stream of messages. This mode can be used with either PIPE_READMODE_MESSAGE or PIPE_READMODE_BYTE.

One of the following read mode flags can be specified. Different instances of the same pipe can specify different read modes. If you specify zero, the parameter defaults to byte-read mode.

Mode
Description
PIPE_READMODE_BYTE Data is read from the pipe as a stream of bytes. This mode can be used with either PIPE_TYPE_MESSAGE or PIPE_TYPE_BYTE.
PIPE_READMODE_MESSAGE Data is read from the pipe as a stream of messages. This mode can be only used if PIPE_TYPE_MESSAGE is also specified.

One of the following wait mode flags can be specified. Different instances of the same pipe can specify different wait modes. If you specify zero, the parameter defaults to blocking mode.

Mode
Description
PIPE_WAIT Blocking mode is enabled. When the pipe handle is specified in the ReadFile, WriteFile, or ConnectNamedPipe function, the operations are not completed until there is data to read, all data is written, or a client is connected. Use of this mode can mean waiting indefinitely in some situations for a client process to perform an action.
PIPE_NOWAIT Nonblocking mode is enabled. In this mode, ReadFile, WriteFile, and ConnectNamedPipe always return immediately. Note that nonblocking mode is supported for compatibility with Microsoft LAN Manager version 2.0 and should not be used to achieve asynchronous I/O with named pipes.

nMaxInstances
Specifies the maximum number of instances that can be created for this pipe. The same number must be specified for all instances. Acceptable values are in the range 1 through PIPE_UNLIMITED_INSTANCES. If this parameter is PIPE_UNLIMITED_INSTANCES, the number of pipe instances that can be created is limited only by the availability of system resources.
nOutBufferSize
Specifies the number of bytes to reserve for the output buffer. 0 indicates the default size. This is advisory only. Meaning that the operating system can ignore your parameter.
nInBufferSize
Specifies the number of bytes to reserve for the input buffer. 0 indicates the default size. This is advisory only.
nDefaultTimeOut
Specifies the default time-out value for the WaitNamedPipe function. This is weird that one function specifies the time out for another.
lpSecurityAttributes
We will use NULL
NOTE: This system call is not supported on Window 9x.
Note: Both client and server must use the CreateNamedPipe call before there can be any communication.
 
Now that we have a pipe, what do we do with it?
Client side:
The client can access a pipe using the CreateFile call. Instead of a file name, the name of the pipe is used. Format for the name is:
\\servername\pipe\[path]pipename
If the server is on the same machine as the client, the client can specify the path:
\\.\pipe\[path]pipename
 
Server;
The server connects as many instances of the pipe as might be needed using CreateNamedPipe. The first call actually creates the pipe. It then can access data from any of the instances of the named pipe that the clients may attach to.
Problem: How does the server know which pipe to read from. One would hope that WaitForMultipleObjects would do the job. Bummer - This call does not work with pipe handles.
Can't just read from each pipe in succession in that the reads are blocking.
Solution: There is a call that lets you peek at the contents of each pipe. This is PeekNamedPipe. The following is the prototype:

BOOL PeekNamedPipe(

HANDLE hNamedPipe, // handle to pipe to copy from

LPVOID lpBuffer, // pointer to data buffer

DWORD nBufferSize, // size, in bytes, of data buffer

LPDWORD lpBytesRead, // pointer to number of bytes read

LPDWORD lpTotalBytesAvail, // pointer to total number of bytes available

LPDWORD lpBytesLeftThisMessage // pointer to unread bytes in this message

);

Parameters

hNamedPipe
Handle to the pipe. This parameter can be a handle to a named pipe instance, as returned by the CreateNamedPipe or CreateFile function, or it can be a handle to the read end of an anonymous pipe, as returned by the CreatePipe function. The handle must have GENERIC_READ access to the pipe.
lpBuffer
Pointer to a buffer that receives data read from the pipe. This parameter can be NULL if no data is to be read.
nBufferSize
Specifies the size, in bytes, of the buffer specified by the lpBuffer parameter. This parameter is ignored if lpBuffer is NULL.
lpBytesRead
Pointer to a 32-bit variable that receives the number of bytes read from the pipe. This parameter can be NULL if no data is to be read.
lpTotalBytesAvail
Pointer to a 32-bit variable that receives the total number of bytes available to be read from the pipe. This parameter can be NULL if no data is to be read. This parameter may be used to determine if there is data to be read in the pipe.
lpBytesLeftThisMessage
Pointer to a 32-bit variable that receives the number of bytes remaining in this message. This parameter will be zero for byte-type named pipes or for anonymous pipes. This parameter can be NULL if no data is to be read.

Return Values

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call GetLastError.

DisconnectNamedPipe

This is a function that is pretty vital to any processing using pipes. If the client disconnects, the pipe is not reuseable. Not until the server disconnects it.

The DisconnectNamedPipe function disconnects the server end of a named pipe instance from a client process.

BOOL DisconnectNamedPipe(

);

Parameters

hNamedPipe

Return Values

Remarks

 
Now, if we consider what we have thus far, it paints a grim picture. If there are to be multiple clients, the server must be constantly polling the pipes with the PeekNamedPipe function in order to determine when there is data to be read. Fortunately, we have a tool that will allow us to do better. Namely, threads. Discuss this.
Do Example on pipes.
Additional pipe functions. These will not be discussed in detail.
  1. GetNamedPipeHandleState - given a pipe's handle, this function gives info about the pipe including user name for client, number of instances of the pipe, and whether the pipe is blocking or non-blocking.
  2. SetNamedPipeHandleState - used to set some of the parameters accessed in the previous call. For example, we can set a pipe to be nonblocking.
  3. GetNamedPipeInfo - given the handle, gives more information about the pipe, including whether the handle is for the client or the server and buffer sizes.
  4. CallNamedPipe - client side function that does the following:
    1. Opens a pipe. (CreateFile)
    2. Writes to pipe. (WriteFile)
    3. Read from pipe. (ReadFile)
    4. Closes the pipe. (CloseHandle)
  5. TransactNamedPipe - client side function that does a write to followed by a read from a pipe.
  6. ConnectNamedPipe - server side function that waits for a client to connect to a given named pipe. This is used when there is only one instance of the pipe. Handy when we intend the client to make single request and disconnect.
  7. WaitNamedPipe - client side function that specifies the name of a named pipe. It will wait for an instance of that pipe to be available. This function also has a timeout. The client can then connect to the pipe with a CreateFile call. Why might the CreateFile call fail?