Implementation Of InterProcessCommunication - NamedPipes Using C++ And C#

Introduction

In general, inter-process communication is a mechanism that allows processes to communicate with each other. The IPC methods vary depending on the operating systems we rely on.  Here, I have used named pipes for the implementation purpose where we will be sending data from a C++ process to C# process.

Creating a NamedPipe

Install/enable the required DLL's in Visual Studio. The code below is used to create a pipe NamedPipeServer.cs in C#.

Importing the kernel32.dll for creating pipe. Pipe will be listening for the incoming data immediately once the connection has been established.

using System;
using Microsoft.Win32.SafeHandles;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
using System.Collections;
namespace CSNamedPipe {
    public class NamedPipeServer {
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern SafeFileHandle CreateNamedPipe(String pipeName, uint dwOpenMode, uint dwPipeMode, uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize, uint nDefaultTimeOut, IntPtr lpSecurityAttributes);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern int ConnectNamedPipe(SafeFileHandle hNamedPipe, IntPtr lpOverlapped);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern int DisconnectNamedPipe(SafeFileHandle hNamedPipe);
        public
        const uint DUPLEX = (0x00000003);
        public
        const uint FILE_FLAG_OVERLAPPED = (0x40000000);
        public class Client {
            public SafeFileHandle handle;
            public FileStream stream;
        }
        public
        const int BUFFER_SIZE = 100;
        public Client clientse = null;
        public string pipeName;
        Thread listenThread;
        SafeFileHandle clientHandle;
        public NamedPipeServer(string PName) {
            pipeName = PName;
        }
        public void Start() {
            this.listenThread = new Thread(new ThreadStart(ListenForClients));
            this.listenThread.Start();
            //ListenForClients();
            //Read();
        }
        private void ListenForClients() {
            while (true) {
                clientHandle = CreateNamedPipe(this.pipeName, DUPLEX | FILE_FLAG_OVERLAPPED, 0, 255, BUFFER_SIZE, BUFFER_SIZE, 0, IntPtr.Zero);
                //could not create named pipe
                if (clientHandle.IsInvalid) return;
                int success = ConnectNamedPipe(clientHandle, IntPtr.Zero);
                //could not connect client
                if (success == 0) return;
                clientse = new Client();
                clientse.handle = clientHandle;
                clientse.stream = new FileStream(clientse.handle, FileAccess.Read, BUFFER_SIZE, true);
                Thread readThread = new Thread(new ThreadStart(Read));
                readThread.Start();
            }
        }
        private void Read() {
            string temp;
            Queue q = new Queue();
            byte[] buffer = null;
            ASCIIEncoding encoder = new ASCIIEncoding();
            while (true) {
                int bytesRead = 0;
                try {
                    buffer = new byte[BUFFER_SIZE];
                    bytesRead = clientse.stream.Read(buffer, 0, BUFFER_SIZE);
                } catch {
                    Console.WriteLine("read error");
                    break;
                }
                //client has disconnected
                if (bytesRead == 0) break;
                int ReadLength = 0;
                for (int i = 0; i < BUFFER_SIZE; i++) {
                    if (buffer[i].ToString("x2") != "cc") {
                        ReadLength++;
                    } else break;
                }
                if (ReadLength > 0) {
                    byte[] Rc = new byte[ReadLength];
                    Buffer.BlockCopy(buffer, 0, Rc, 0, ReadLength);
                    temp = encoder.GetString(Rc, 0, ReadLength);
                    q.Enqueue(temp);
                    Console.WriteLine("Element Inserted :" + temp);
                    Console.WriteLine("Current queue");
                    foreach(string c in q) {
                        Console.Write(c + " ");
                    }
                    //buffer.Initialize();
                }
            }
            clientse.stream.Close();
            clientse.handle.Close();
        }
    }
}

After creating the namedpipe, the next one is to start the pipe using program.cs as below

using System;
using System.Text;
namespace CSNamedPipe {
    class Program {
        static void Main(string[] args) {
            NamedPipeServer PServer1 = new NamedPipeServer(@ "\\.\pipe\myNamedPipe1");
            PServer1.Start();
        }
    }
}

Pipe has been established and it's open now to share data between processes. Run the program/execute the .exe in the debug folder(as below).

Sending data from C++ Process 

The code below is used to send data from C++ to the C# process using the pipe we created in C#. - hd_client.cpp.

Elements are sent from C++ process and those elements are captured in C# Process and stored in a Queue.

#include <stdio.h>
#include <windows.h>
#include<iostream>
using namespace std;

HANDLE hPipe1;
BOOL Finished;

int main(int argc, char * argv[]) {
    char buf[100];
    LPTSTR Pipename = TEXT("\\\\.\\pipe\\myNamedPipe1");
    DWORD cbWritten;
    DWORD dwBytesToWrite = (DWORD) strlen(buf);
    BOOL Write_St = TRUE;
    Finished = FALSE;
    hPipe1 = CreateFile(Pipename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
    if ((hPipe1 == NULL || hPipe1 == INVALID_HANDLE_VALUE)) {
        cout << "Could not open the pipe ";
    } else {
        do {
            cout << "Enter the element to be added: ";
            cin >> buf;
            if (strcmp(buf, "quit") == 0) Write_St = FALSE;
            else {
                WriteFile(hPipe1, buf, dwBytesToWrite, & cbWritten, NULL);
                memset(buf, 0xCC, 100);
            }
        } while (Write_St);
        CloseHandle(hPipe1);
        Finished = TRUE;
    }
    getchar();
}

The below image shows that elements are being sent from a C++ process and it gets captured and stored in a queue using the named pipes in a C# Process.

Conclusion

The implementation of InterProcessCommunication(namedpipe) between C++ and C# has been successfully explained. We have seen how to send data from a C++ Process to C# Process and then performed some operation (storing the data into a queue) using that captured data. Try implementing it using the source code. Do reach out in case of any queries.