Search code examples
c#c++windowsipcnamed-pipes

Named Pipe C# client can't connect to C++ server


I'm trying to get a C++ application to let a C# application know when a particular action happens. The way I'm trying to do this is via named pipes.

I've set up a named pipe server on the C++ app, which seems to be working (the named pipe gets created - it appears on the list retrieved by PipeList) and a named pipe client on the C# app, where it fails: First line of the C# client code gives "Pipe handle has not been set. Did your PipeStream implementation call InitializeHandle?" error, and line 2 throws "Access to the path is denied" exception.

Where am I going wrong?

C++ Server Code

CString namedPipeName = "\\\\.\\pipe\\TitleChangePipe";

HANDLE pipe = CreateNamedPipe(namedPipeName, PIPE_ACCESS_INBOUND , PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL);
if (pipe == INVALID_HANDLE_VALUE) {
    MessageBox(NULL, "Pipe Could Not be Established.", "Error: TCM", MB_ICONERROR);
    return -1;
}

char line[512]; DWORD numRead;

while (true)//just keep doing this
{
    numRead = 1;
    while ((numRead < 10 || numRead > 511) && numRead > 0)
    {
        if (!ReadFile(pipe, line, 512, &numRead, NULL) || numRead < 1) {//Blocking call
            CloseHandle(pipe);                                          //If something went wrong, reset pipe
            pipe = CreateNamedPipe(namedPipeName, PIPE_ACCESS_INBOUND , PIPE_WAIT, 1, 1024, 1024, 120 * 1000, NULL);
            ConnectNamedPipe(pipe, NULL);
            if (pipe == INVALID_HANDLE_VALUE) {
                MessageBox(NULL, "Pipe Could Not be Established.", "Error: TCM", MB_ICONERROR);
                return -1; }
            numRead = 1;
        }
    }
    line[numRead] = '\0';   //Terminate String
}   

CloseHandle(pipe);

C# Client Code

var client = new NamedPipeClientStream(".", "TitleChangePipe", PipeDirection.InOut);
client.Connect();
var reader = new StreamReader(client);
var writer = new StreamWriter(client);

while (true)
{
    var input = Console.ReadLine();
    if (String.IsNullOrEmpty(input))
         break;
    writer.WriteLine(input);
    writer.Flush();
    Console.WriteLine(reader.ReadLine());
}

Solution

  • The named pipe creation doesn't have the right parameters.

    First you want to read & write on the pipe, so the flag to use is : PIPE_ACCESS_DUPLEX

    Then, here you are sending messages in synchronous mode. Use these flags : PIPE_WAIT | PIPE_TYPE_MESSAGE

    Finally, you are allowing only one instance of this pipe on the machine. Obviously you need at least 2: One for the server one for the client. I would just use the unlimited flag: PIPE_UNLIMITED_INSTANCES

    HANDLE pipe = CreateNamedPipe(namedPipeName, PIPE_ACCESS_DUPLEX, \
                                  PIPE_WAIT | PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, \
                                  1024, 1024, 120 * 1000, NULL);
    

    After creating a pipe in the server you should wait against connections on this pipe before using it : https://msdn.microsoft.com/en-us/library/windows/desktop/aa365146(v=vs.85).aspx