Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Windows IPC

0.00/5 (No votes)
22 Jun 2006 1  
A brief introduction to Windows IPC mechanisms.

Introduction

There are many options available for Inter-Process Communication (IPC) on Windows. I will try to list them out, along with some of the advantages and disadvantages of using them. This article is pretty much a beginner�s article.

Definitions of IPC

I found some formal definitions of Inter-Process Communication, on the internet:

  • Inter-Process Communication, which in short is known as IPC, deals mainly with the techniques and mechanisms that facilitate communication between processes.
  • A capability supported by some operating systems that allows one process to communicate with another process. The processes can be running on the same computer, or on different computers connected through a network.

Background

The design of a product or project of decent size and complication requires that two or more processes communicate.

There are many options using which this communication can be accomplished. For example, if your processes are using message queues, then you can use the message WM_COPYDATA to transfer data between the processes. The Windows Clipboard can also be used.

COM/DCOM/RPC technologies can be used for IPC. These technologies are out of the scope of this article.

A few other mechanisms that I will highlight in this article are:

  • Shared Memory (memory mapped object).
  • Named Pipe.
  • WinSock.
  • Mailslot.

It is important to know about Windows Synchronization objects when working with IPC. Almost every time some IPC mechanism is used, it is used along with some inter-process synchronization mechanism.

The following are the synchronization objects that can be used to synchronize between processes:

  • Mutex
  • Semaphore
  • Events

There is one more synchronization object � Critical Section. However, a Critical Section is used for synchronization within the threads of a single process. It can�t be used for synchronization between processes. A detailed discussion of Windows Synchronization is out of the scope of this article.

Using the code

Shared Memory

Following are some of the Win32 APIs that are used when working with shared memory (memory mapped objects):

  • CreateFileMapping()
  • MapViewOfFile()
  • UnMapViewOfFile()
  • CloseHandle()

Notes on Shared Memory:

  • Source code is platform dependent.
  • Can only be used for processes that are on the same computer.
  • Once a memory mapped object is mapped into the process area, using it is very straightforward and easy.
  • Perhaps the fastest IPC mechanism.

The source code demonstrating the use of memory mapped objects can be found on my other article: Synchronization of multiple reader and writer processes using Win32 APIs.

Named Pipe

Naming an object helps the processes to share object handles. Names are case sensitive. Named objects are kernel objects. Kernel objects are process specific, and the process needs to be created or opened before using them.

Pipes are FIFO in behavior (First-In, First-Out). Pipes are of two types � Anonymous Pipes and Named Pipes.

An Anonymous Pipe is created using the CreatePipe() API. An anonymous pipe is local, and cannot be used to communicate over a network. An anonymous pipe is unnamed, one-way, and generally used to transfer data from a parent process to a child process.

Named pipes can be one-way or duplex. Named pipes will work between processes on the same computer as well as between processes across the network.

Following are some of the Win32 APIs that are used when working with Named Pipes:

  • CreateNamedPipe()
  • ConnectNamedPipe()
  • WaitNamedPipe()
  • DisconnectNamedPipe()
  • ReadFile()
  • WriteFile()
  • CloseHandle()

A Named Pipe name needs to be in the following format:

  • For named pipe server - \\.\pipe\PipeName
  • For named pipe client - \\ComputerName\pipe\PipeName

The source code for a Named Pipe Server can be found in NPServer.zip, and for the client in NPClient.zip, both are downloadable from the links provided on the top of this article.

The code for a named pipe server is displayed below:

#include "stdafx.h"

#include "windows.h"


//Name given to the pipe

#define g_szPipeName "\\\\.\\Pipe\\MyNamedPipe"
//Pipe name format - \\.\pipe\pipename


#define BUFFER_SIZE 1024 //1k

#define ACK_MESG_RECV "Message received successfully"

int main(int argc, char* argv[])
{
     HANDLE hPipe;
     
     hPipe = CreateNamedPipe( 
          g_szPipeName,             // pipe name 

          PIPE_ACCESS_DUPLEX,       // read/write access 

          PIPE_TYPE_MESSAGE |       // message type pipe 

          PIPE_READMODE_MESSAGE |   // message-read mode 

          PIPE_WAIT,                // blocking mode 

          PIPE_UNLIMITED_INSTANCES, // max. instances  

          BUFFER_SIZE,              // output buffer size 

          BUFFER_SIZE,              // input buffer size 

          NMPWAIT_USE_DEFAULT_WAIT, // client time-out 

          NULL);                    // default security attribute 

     
     if (INVALID_HANDLE_VALUE == hPipe) 
     {
          printf("\nError occurred while " 
                 "creating the pipe: %d", GetLastError()); 
          return 1;  //Error

     }
     else
     {
          printf("\nCreateNamedPipe() was successful.");
     }
     
     printf("\nWaiting for client connection...");
     
     //Wait for the client to connect

     BOOL bClientConnected = ConnectNamedPipe(hPipe, NULL);
     
     if (FALSE == bClientConnected)
     {
          printf("\nError occurred while connecting" 
                 " to the client: %d", GetLastError()); 
          CloseHandle(hPipe);
          return 1;  //Error

     }
     else
     {
          printf("\nConnectNamedPipe() was successful.");
     }
     
     char szBuffer;
     DWORD cbBytes;
     
     //We are connected to the client.

     //To communicate with the client 

     //we will use ReadFile()/WriteFile() 

     //on the pipe handle - hPipe

     
     //Read client message

     BOOL bResult = ReadFile( 
          hPipe,                // handle to pipe 

          szBuffer,             // buffer to receive data 

          sizeof(szBuffer),     // size of buffer 

          &cbBytes,             // number of bytes read 

          NULL);                // not overlapped I/O 

     
     if ( (!bResult) || (0 == cbBytes)) 
     {
          printf("\nError occurred while reading " 
                 "from the client: %d", GetLastError()); 
          CloseHandle(hPipe);
          return 1;  //Error

     }
     else
     {
          printf("\nReadFile() was successful.");
     }
     
     printf("\nClient sent the following message: %s", szBuffer);
     
     strcpy(szBuffer, ACK_MESG_RECV);
     
     //Reply to client

     bResult = WriteFile( 
          hPipe,                // handle to pipe 

          szBuffer,             // buffer to write from 

          strlen(szBuffer)+1,   // number of bytes to write, include the NULL 

          &cbBytes,             // number of bytes written 

          NULL);                // not overlapped I/O 

     
     if ( (!bResult) || (strlen(szBuffer)+1 != cbBytes))
     {
          printf("\nError occurred while writing" 
                 " to the client: %d", GetLastError()); 
          CloseHandle(hPipe);
          return 1;  //Error

     }
     else
     {
          printf("\nWriteFile() was successful.");
     }
     
     CloseHandle(hPipe);
     return 0; //Success

}

The code for a named pipe client is displayed below:

#include "stdafx.h"

#include "windows.h"


//Name given to the pipe

#define g_szPipeName "\\\\.\\Pipe\\MyNamedPipe"
//Pipe name format - \\servername\pipe\pipename

//This pipe is for server on the same computer, 

//however, pipes can be used to

//connect to a remote server


#define BUFFER_SIZE 1024 //1k

#define ACK_MESG_RECV "Message received successfully"

int main(int argc, char* argv[])
{
     HANDLE hPipe;
     
     //Connect to the server pipe using CreateFile()

     hPipe = CreateFile( 
          g_szPipeName,   // pipe name 

          GENERIC_READ |  // read and write access 

          GENERIC_WRITE, 
          0,              // no sharing 

          NULL,           // default security attributes

          OPEN_EXISTING,  // opens existing pipe 

          0,              // default attributes 

          NULL);          // no template file 

     
     if (INVALID_HANDLE_VALUE == hPipe) 
     {
          printf("\nError occurred while connecting" 
                 " to the server: %d", GetLastError()); 
          //One might want to check whether the server pipe is busy

          //This sample will error out if the server pipe is busy

          //Read on ERROR_PIPE_BUSY and WaitNamedPipe() for that

          return 1;  //Error

     }
     else
     {
          printf("\nCreateFile() was successful.");
     }
     
     //We are done connecting to the server pipe, 

     //we can start communicating with 

     //the server using ReadFile()/WriteFile() 

     //on handle - hPipe

     
     char szBuffer;
     
     printf("\nEnter a message to be sent to the server: ");
     gets(szBuffer);
     
     DWORD cbBytes;
     
     //Send the message to server

     BOOL bResult = WriteFile( 
          hPipe,                // handle to pipe 

          szBuffer,             // buffer to write from 

          strlen(szBuffer)+1,   // number of bytes to write, include the NULL

          &cbBytes,             // number of bytes written 

          NULL);                // not overlapped I/O 

     
     if ( (!bResult) || (strlen(szBuffer)+1 != cbBytes))
     {
          printf("\nError occurred while writing" 
                 " to the server: %d", GetLastError()); 
          CloseHandle(hPipe);
          return 1;  //Error

     }
     else
     {
          printf("\nWriteFile() was successful.");
     }
     
     //Read server response

     bResult = ReadFile( 
          hPipe,                // handle to pipe 

          szBuffer,             // buffer to receive data 

          sizeof(szBuffer),     // size of buffer 

          &cbBytes,             // number of bytes read 

          NULL);                // not overlapped I/O 

     
     if ( (!bResult) || (0 == cbBytes)) 
     {
          printf("\nError occurred while reading" 
                 " from the server: %d", GetLastError()); 
          CloseHandle(hPipe);
          return 1;  //Error

     }
     else
     {
          printf("\nReadFile() was successful.");
     }
     
     printf("\nServer sent the following message: %s", szBuffer);
     
     CloseHandle(hPipe);
     return 0; //Success

}

Notes on Named Pipe:

  • The source code is platform dependent.
  • A named pipe is easy to use.
  • A named pipe works across the network.

WinSock

WinSock provides very high level networking capabilities. It supports TCP/IP (the most widely used protocol), along with many other protocols � AppleTalk, DECNet, IPX/SPX etc.

WinSock supports Berkeley sockets, along with many other Windows specific extensions.

Following are some of the WinSock calls:

  • socket()
  • bind()
  • listen()
  • accept()
  • connect()
  • send()
  • recv()

Notes on WinSock:

  • Widely used, and works on the same computer as well as across networks. Moreover, it can be used across various platforms and protocols.
  • Using WinSock requires a knowledge of relatively advanced networking concepts. The source code demonstrating the use of a TCP/IP based one-to-one client and server can be found on my other article: A simple application using I/O Completion Port and WinSock.

Mailslot

Mailslot is used for one way inter-process communications. There is a Mailslot server which will be read-only; it will just read the client sent messages. The clients will be write-only clients, sending messages to the server. Mailslot messages can be of around 400 bytes only.

Mailslot can broadcast messages in a domain. If processes in a domain create a mailslot with the same name, then a message that is sent to that mailslot is sent to all of these processes.

Following are some of the Win32 APIs that are used when working with Mailslot:

  • CreateMailSlot()
  • GetMailslotInfo()
  • SetMailslotInfo()
  • ReadFile()
  • WriteFile()
  • CloseHandle()

A Mailslot name needs to be in the following format:

  • \\ComputerName\mailslot\[path\]name
  • \\DomainName\mailslot\[path\]name
  • \\*\mailslot\[path\]name

Source code for a Mailslot server can be found in MSServer.zip, and for a client in MSClient.zip � download these from the links on the top of this article.

The code for a Mailslot server is displayed below:

#include "stdafx.h"

#include "windows.h"


//Name given to the Mailslot

#define g_szMailslot "\\\\.\\mailslot\\MyMailSlot"
//Mailslot name format - \\.\mailslot\mailslotname


#define BUFFER_SIZE 1024 //1k


int main(int argc, char* argv[])
{
     HANDLE hMailslot;
     
     hMailslot = CreateMailslot( 
          g_szMailslot,             // mailslot name

          BUFFER_SIZE,              // input buffer size 

          MAILSLOT_WAIT_FOREVER,    // no timeout

          NULL);                    // default security attribute 

     
     if (INVALID_HANDLE_VALUE == hMailslot) 
     {
          printf("\nError occurred while" 
                 " creating the mailslot: %d", GetLastError()); 
          return 1;  //Error

     }
     else
     {
          printf("\nCreateMailslot() was successful.");
     }
     
     //Mailslot is one-way communication

     //Server will only read

     //Using ReadFile()

     char szBuffer;
     DWORD cbBytes;
     BOOL bResult;
     
     printf("\nWaiting for client connection...");
     
     while(1) //Infinite, till user terminates the console app

     {
          //Read client message

          bResult = ReadFile( 
               hMailslot,            // handle to mailslot 

               szBuffer,             // buffer to receive data 

               sizeof(szBuffer),     // size of buffer 

               &cbBytes,             // number of bytes read 

               NULL);                // not overlapped I/O 

          
          if ( (!bResult) || (0 == cbBytes)) 
          {
               printf("\nError occurred while reading" 
                      " from the client: %d", GetLastError()); 
               CloseHandle(hMailslot);
               return 1;  //Error

          }
          else
          {
               printf("\nReadFile() was successful.");
          }
          
          printf("\nClient sent the following message: %s", szBuffer);
     }
     
     CloseHandle(hMailslot);
     return 0; //Success

}

The code for a Mailslot client is displayed below:

#include "stdafx.h"

#include "windows.h"


//Name given to the Mailslot

#define g_szMailslot "\\\\.\\mailslot\\MyMailSlot"
//Mailslot name format - \\.\mailslot\mailslotname

//This mailslot is for server on the same computer, 

//however, mailslots can be used to

//connect to a remote server


#define BUFFER_SIZE 1024 //1k


int main(int argc, char* argv[])
{
     HANDLE hMailslot;
     
     //Connect to the server mailslot using CreateFile()

     hMailslot = CreateFile( 
          g_szMailslot,          // mailslot name 

          GENERIC_WRITE,         // mailslot write only 

          FILE_SHARE_READ,       // required for mailslots

          NULL,                  // default security attributes

          OPEN_EXISTING,         // opens existing mailslot 

          FILE_ATTRIBUTE_NORMAL, // normal attributes 

          NULL);                 // no template file 

     
     if (INVALID_HANDLE_VALUE == hMailslot) 
     {
          printf("\nError occurred while connecting" 
                 " to the server: %d", GetLastError()); 
          return 1;  //Error

     }
     else
     {
          printf("\nCreateFile() was successful.");
     }
     
     //We are done connecting to the mailslot, 

     //Mailslot communication is one-way, 

     //client will just write to mailslot

     //Using WriteFile()

     
     char szBuffer;
     
     printf("\nEnter a message to be sent to the server: ");
     gets(szBuffer);
     
     DWORD cbBytes;
     
     //Send the message to server

     BOOL bResult = WriteFile( 
          hMailslot,            // handle to mailslot 

          szBuffer,             // buffer to write from 

          strlen(szBuffer)+1,   // number of bytes to write, include the NULL

          &cbBytes,             // number of bytes written 

          NULL);                // not overlapped I/O 

     
     if ( (!bResult) || (strlen(szBuffer)+1 != cbBytes))
     {
          printf("\nError occurred while writing" 
                 " to the server: %d", GetLastError()); 
          CloseHandle(hMailslot);
          return 1;  //Error

     }
     else
     {
          printf("\nWriteFile() was successful.");
     }
     
     CloseHandle(hMailslot);
     return 0; //Success

}

Notes on Mailslot

  • Source code is platform dependent.
  • Mailslot provides one-way communication only.
  • Size of message is limited to around 400 bytes.
  • Mailslot supports broadcasting.
  • Works across a network.
  • Mailslot is easy to use.

Conclusion

To conclude, this article is a brief introduction to Windows IPC mechanisms. I have tried to list the options available, and provided some code to help readers get a feel of how to use Windows IPC mechanisms. The article is not very exhaustive or detailed. The reader will need to refer to other detailed material on this topic to understand Windows IPC in a much better way.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here