Click here to Skip to main content
15,392,679 members
Articles / Programming Languages / VC++
Tip/Trick
Posted 28 Oct 2014

Stats

38.6K views
4.7K downloads
30 bookmarked

C++ Wrapper for Pipe-based IPC in Windows

Rate me:
Please Sign up or sign in to vote.
4.30/5 (9 votes)
28 Oct 2014CPOL3 min read
C++ Wrapper for Pipe-based IPC in Windows

Introduction

This tip aims to provide a C++ wrapper class for facilitating interprocess communication (IPC) using pipes in Windows. The pipe client and pipe server are implemented as CPipeClient and CPipeServer classes, respectively. The classes wrap the underlying Windows API functions to ease the process of reading and writing to pipe. Also, an event-based mechanism is implemented to handle pipe related events. Named pipe is used to allow full-duplex communication between client and server processes.

Background

Although the Windows API has the ReadFile/WriteFile functions for reading/writing data to the pipe, this article on top of it provides an event-based mechanism to ease the pipe communication. Also, all the pipe related events are processed in a separate thread. The PipeClient and PipeServer classes can be easily integrated into any project.

Using the code

Two classes form the wrapper, mainly CPipeClient and CPipeServer. CPipeClient class implements all the logic required for the pipe client and the CPipeServer class implements the pipe server logic. Some part of the code in these classes will look similar, which highlights the commonality of basic pipe communication. In order to use the code, all that is required is creating an instance of CPipeServer and CPipeClient.

Pipe server is first started and waits for a pipe client to get connected. Once a pipe client is connected, the pipe server sends a welcome message to the client. The client then sends a close message to the pipe server. Subsequently, both pipe server and pipe client close. For the purpose of illustration here, only two messages are passed, but one can pass any number of messages and what to do when a message is received and when to close the pipe depends on the application logic.

All data passed between the pipe client and pipe server is of wstring type so one can easily handle unicode data.

CPipeClient is implemented as follows:

C++
class CPipeClient
{
public:

    /**
     * Constructor
     * @paramIn sName: Pipe name
     */
    CPipeClient(std::wstring& sName);

    /**
     * Destructor
     */
    virtual ~CPipeClient(void);

    /**
     * Get event ID
     * @paramOut: Event ID
     */
    int GetEvent() const;

    /**
     * Set event ID
     * @paramIn: Event ID
     */
    void SetEvent(int nEventID);

    /**
     * Get handle of the thread that processes pipe I/O events
     * @paramOut: Thread HANDLE
     */
    HANDLE GetThreadHandle();

    /**
     * Get pipe handle
     * @paramOut: Pipe HANDLE
     */
    HANDLE GetPipeHandle();

    /**
     * Write data to buffer
     * @paramIn sData: wstring data to be copied to buffer
     */
    void SetData(std::wstring& sData);

    /**
     * Read data from buffer
     * @paramOut: Data will be copied from buffer
     */
    void GetData(std::wstring& sData);

    /**
     * Connect pipe client to pipe server
     */
    void ConnectToServer();

    /**
     * Invoked whenever there is a pipe event
     * @paramIn nEvent: Event type received
     */
    void OnEvent(int nEvent);

    /**
     * Thread callback function which processes the pipe I/O events
     * @paramIn param: CPipeClient object
     */
    static UINT32 __stdcall PipeThreadProc(void* param); 

    /**
     * Close the pipe client
     */
    void Close();

    /**
     * Read data from pipe. This is a blocking call.
     * @return: true if success else false
     */
    bool Read();

    /**
     * Writes data from buffer into the pipe
     * @return: true if success else false
     */
    bool Write();

private:
    /**
     * Default constructor
     */
    CPipeClient(void);

    /**
     * Initializes pipe client. A thread is created and starts running
     */
    void Init();

    /**
     * Starts the pipe thread
     */
    void Run();

    const std::wstring m_sPipeName; // Pipe name
    HANDLE m_hPipe;                 // Pipe handle
    HANDLE m_hThread;               // Pipe thread
    int    m_nEvent;                // Pipe event
    wchar_t* m_buffer;              // Buffer to hold data

};

 

CPipeServer class is implemented as follows:

C++
class CPipeServer
{
public:

    /**
     * Constructor
     * @paramIn sName: Pipe name
     */
    CPipeServer(std::wstring& sName);

    /**
     * Destructor
     */
    virtual ~CPipeServer(void);

    /**
     * Get event ID
     * @paramOut: Event ID
     */
    int GetEvent() const;

    /**
     * Set event ID
     * @paramIn: Event ID
     */
    void SetEvent(int nEventID);

    /**
     * Get handle of the thread that processes pipe I/O events
     * @paramOut: Thread HANDLE
     */
    HANDLE GetThreadHandle();

    /**
     * Get pipe handle
     * @paramOut: Pipe HANDLE
     */
    HANDLE GetPipeHandle();

    /**
     * Write data to buffer
     * @paramIn sData: wstring data to be copied to buffer
     */
    void SetData(std::wstring& sData);

    /**
     * Read data from buffer
     * @paramOut: Data will be copied from buffer
     */
    void GetData(std::wstring& sData);

    /**
     * Invoked whenever there is a pipe event
     * @paramIn nEvent: Event type received
     */
    void OnEvent(int nEvent);

    /**
     * Thread callback function which processes the pipe I/O events
     * @paramIn param: CPipeClient object
     */
    static UINT32 __stdcall PipeThreadProc(void*); 

    /**
     * Wait for a pipe client to get connected
     */
    void WaitForClient();

    /**
     * Close the pipe client
     */
    void Close();

    /**
     * Read data from pipe. This is a blocking call.
     * @return: true if success else false
     */
    bool Read();

    /**
     * Writes data from buffer into the pipe
     * @return: true if success else false
     */
    bool Write();

private:

    /**
     * Default constructor
     */
    CPipeServer(void);

    /**
     * Initializes pipe client. A thread is created and starts running
     */
    void Init();

    /**
     * Starts the pipe thread
     */
    void Run();

    const std::wstring m_sPipeName; // Pipe name
    HANDLE m_hPipe;                 // Pipe handle
    HANDLE m_hThread;               // Pipe thread
    int    m_nEvent;                // Pipe event
    wchar_t* m_buffer;              // Buffer to hold data

};

In both the classes the function PipeThreadProc is the thread callback function which handles the pipe events. This function loops until a pipe is closed or there is a pipe error. The function OnEvent is invoked whenever a pipe event is received. Thus, application logic can be handled from OnEvent function based on the type of message received.

To write data to pipe all one has to do is set the data using SetData function and SetEvent to AU_IOWRITE. Data to be written or read is put in the m_buffer member of the class.

All constants used are defined in the file PipeConst.h.

PipeClient.cpp and PipeServer.cpp demonstrates how the above classes can be used.

Pipe server driver code looks as follows:

C++
int _tmain(int argc, _TCHAR* argv[])
{
    _setmode(_fileno(stdout), _O_U16TEXT);
    std::wcout << _T("---------------------Pipe Server--------------------") << std::endl;
    std::wstring sPipeName(PIPENAME);
    CPipeServer* pServer = new CPipeServer(sPipeName);
    ::WaitForSingleObject(pServer->GetThreadHandle(), INFINITE);
    delete pServer;
    pServer = NULL;

    return 0;
}

Pipe client driver code looks as follows:

C++
int _tmain(int argc, _TCHAR* argv[])
{
    _setmode(_fileno(stdout), _O_U8TEXT);
    std::wcout << _T("---------------------Pipe Client--------------------") << std::endl;
    std::wstring sPipeName(PIPENAME);
    CPipeClient* pClient = new CPipeClient(sPipeName);
    ::WaitForSingleObject(pClient->GetThreadHandle(), INFINITE);
    delete pClient;
    pClient = NULL;
    return 0;
}

Following is the output when pipe server is first run and then the pipe client.

Pipe server ouput

Pipe client output

 

 

Points of Interest

The number of instances of pipe server can be controlled inside the Init function. The function call ::CreateNamedPipe has a setting called PIPE_UNLIMITED_INSTANCES to control it. Currently, it defaults to 255. Also, the buffer size has been limited to 1024. One can provide any size depending on the limitation of the underlying system.

History

Update output image.

 

Download Links

Pipe Server

Pipe Client

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

bvinothraj
Software Developer (Senior) Mindfire Solutions
India India
No Biography provided

Comments and Discussions

 
QuestionDownload link are down Pin
Member 1147652118-May-17 6:20
MemberMember 1147652118-May-17 6:20 
QuestionNamed pipes may be blocked. Pin
KLSmith30-Oct-14 6:09
MemberKLSmith30-Oct-14 6:09 
SuggestionDownload links are broken Pin
Giri Ganji28-Oct-14 21:18
MemberGiri Ganji28-Oct-14 21:18 
GeneralRe: Download links are broken Pin
bvinothraj29-Oct-14 3:12
Memberbvinothraj29-Oct-14 3:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.