Spiria logo.

Sharing data between Processes in Windows: Named Pipe and Shared Memory

May 30, 2016.

Shared data is a fast way to communicate between parent and child processes. Depending on the size of the shared data, you can choose either named pipe or named shared memory. The following examples illustrate both cases and show how to use event objects to synchronize data reading and writing between processes. Let’s assume a scenario whereby the parent process sends a small section of data to the child process, and based on the data, the child process produces a big block of data to send back to the parent process.

Shared data is a fast way to communicate between parent and child processes. Depending on the size of the shared data, you can choose either named pipe or named shared memory. The following examples illustrate both cases and show how to use event objects to synchronize data reading and writing between processes.

Let’s assume a scenario whereby the parent process sends a small section of data to the child process, and based on the data, the child process produces a big block of data to send back to the parent process.

1. To share a small section of data: named pipe

A pipe is a section of shared memory that processes use for communication. The named pipe, which could be one-way or duplex (two-way) can offer both reading and writing services for the processes.

To send data from the parent process to the child process, the parent process creates the pipe file using the pipe name, then writes the data onto the pipe file. Meanwhile, to receive the data from the parent process, the child process creates the pipe file using the same pipe name, and reads the data from the pipe file.

But when does the child process read the data? We can use event objects to synchronize the reading and writing between the parent and child processes. Two event objects, eventToChild and eventFromChild, are created in the parent process. The parent process uses eventToChild to notify the child process that the data is going to be sent, while the child process uses eventFromChild to notify the parent process. Here is the code for the parent process:

Here is the code example for the parent process:

char* pipeName =” \\\\.\\pipe\\”;
char* eventToChildName = “event_to_child”;
char* eventFromChildName = “event_from_child”;

//Create the name pipe by the pipe name;
Handle namedPipe = CreateNamedPipe(pipeName,
			PIPE_ACCESS_DUPLEX +FILE_FLAG_FIRST_PIPE_INSTANCE,
			PIPE_TYPE_MESSAGE + PIPE_WAIT + PIPE_READMODE_MESSAGE, 
			PIPE_UNLIMITED_INSTANCES, 
			100, 
			100, 
			100, 
			nullptr);

//Create the event to child, where eventSecurity is the pointer to 
//SECURITY_ATTRIBUTES structure. 
Handle eventToChild = CreateEvent(&eventSecurity,
				false, 
				false, 
				eventToChildName );
//Create the event from child, the child process uses it to notify the parent process.
Handle eventFromChild = CreateEvent(&eventSecurity,false, false, eventFromChildName );

//notify the child process
if (!SetEvent(eventToChild))
	return;	

//Write the data to the named pipe
DWORD writtenSize;
if (!WriteFile(namedPipe, data, sizeof(data), & writtenSize, nullptr) || writtenSize!= sizeof(data))
	return;	

Now, let’s turn to the child process. The child process also creates the named pipe handle, say “hPipe”, and opens “event to child” and “event from child” based on the same event names. After waiting for “event to child” to be signalled by the parent process, the child process reads the data from the named pipe file:

//waiting for the event sent from the parent process
DWORD wait = WaitForSingleObject( eventToChild, INFINITE );
if(wait != WAIT_OBJECT_0 )
{
	//handling error code
}

//continuously reading the data from the named pipe
bool res = false;
while (1)
	{
	res = ReadFile( hPipe, lpBuffer, nNumberOfBytesToRead, & lpNumberOfBytesRead, nullptr) ;
	if( !res )
		break;
	}

2. To share big data: named shared-memory

After reading the data from the name pipe, let’s assume that the child process generates a big block of data to share with the parent process. The fastest way to handle this is to create a file mapping object, map the file object into memory space, notify the parent process of the file mapping handle and data size, and then dump the data to the mapped buffer for the parent process to read. The parent process locates the memory section using the mapped file handle and the buffer size, which are written in the named pipe, to read the data in the mapped buffer directly.

Here is the code for the child process:

Handle hMapFile = CreateFileMapping(
				INVALID_HANDLE_VALUE,
				NULL,
				PAGE_READWRITE,
				0, 
				bufferSize,
				nullptr);	
 
	if ( hMapFile == nullptr) 
		return;

	// map the file into memory
	LPSTR mappedBuffer = (LPSTR) MapViewOfFile(
					hMapFile,
					FILE_MAP_ALL_ACCESS,
					0,
					0,
					0 );

//notify the parent process to receive the mapped file handle and the buffer size    
(!SetEvent( eventFromChild))
	return;

//After notifying, write the data to the named pipe
DWORD buffer[2];
buffer[0] = (DWORD)hMapFile;
buffer[1] = bufferSize;
if (WriteFile(hPipe, buffer, sizeof buffer, &written, NULL ) || ( written != sizeof buffer ) ))
	return;

//here we can wait for the parent process to return a message that 
//it has received the data and is ready to read the buffer data.

//dump the data 
memcpy(destination, mappedBuffer, bufferSize);
…

On the other hand, after receiving the mapped file handle “childhMapFile” and the buffer size from the child process, the parent process can use the “DuplicateHandle()” function to duplicate the handle “hMapFile”, and then map the file into current process memory. Here is the code:

if ( DuplicateHandle( childProcess, childhMapFile, currentProcess, & hMapFile, 0, false, DUPLICATE_SAME_ACCESS ) )

	// map the file into our process memory
	LPSTR hMappedBuffer = (LPSTR) MapViewOfFile(
				hMapFile,
				FILE_MAP_ALL_ACCESS,
				0,
				0,
				0);
}

3. Conclusion

Shared data is one of several ways for processes to communicate. Named pipe and shared memory are used in different circumstances. The two processes access the same named pipe by name, and access the shared memory by the mapped file handle. Proper use of event objects and wait functions to control the timing of shared data reading and writing will ensure process synchronization.