Search code examples
c++winapipipe

How to keep a pointer field constant throughout the lifetime of an object (handle to a pipe in c++)


I am trying to make a handler class for dealing with a pipe in c++. I may start by saying I'm used to c#, and quite new to c++ and the concept of pointers.

The only field in the class is a variable, of type HANDLE (void *), pipeHandle, which stores the value returned by CreateFile().

I have a method, ConnectPipe(), which uses CreateFile() to get the handle of the pipe, as well as some error checking. It stores the handle in pipeHandle and then finishes execution. This appears to be problematic, as after research and debugging I have found that the value of pipeHandle changes after ConnectPipe() finishes executing. I think this is due to the way that the pointer references something instantiated in ConnectPipe() which then gets deleted after ConnectPipe() finishes.

Code:

PipeManager.h:

#ifndef PIPE_MANAGER_H
#define PIPE_MANAGER_H

#include <Windows.h>
#include <string>

class PipeManager
{
private:
    HANDLE pipeHandle;
    void PipeConnect(LPCWSTR pipeFullName);
    void PipeReceive(BYTE* outBuffer);

public:
    PipeManager(std::string pipeName);
    void Testing();
};

#endif

PipeManager.cpp:

#include "PipeManager.h"
#include <iostream>

void PipeManager::PipeConnect(LPCWSTR pipeFullName)
{
    const int ATTEMPT_LIMIT = 10;
    int attempts = 0;

    do {
        if (attempts > ATTEMPT_LIMIT) {
            break;
        }
        pipeHandle = CreateFile(pipeFullName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
        if (pipeHandle == INVALID_HANDLE_VALUE) {
            std::cerr << "Failed to connect to the named pipe, error code " << GetLastError() << ". Trying again." << std::endl;
            attempts++;
        }
    } while (pipeHandle == INVALID_HANDLE_VALUE);
}

void PipeManager::PipeReceive(BYTE* outBuffer)
{
    DWORD read = 0; // Number of bytes read in each ReadFile() call
    int index = 0;
    do {
        if (!ReadFile(pipeHandle, outBuffer + index, 1, &read, NULL)) {
            std::cerr << "Failed to read from the named pipe, error code " << GetLastError() << std::endl;
            break;
        }
    } while (outBuffer[index - 1] != 0); // Stop if the most recent byte was null-termination.
}

// Constructor for a named pipe, yet to be connected to
PipeManager::PipeManager(std::string pipeName) {
    std::string fullName = "\\\\.\\pipe\\" + pipeName;
    PipeConnect(std::wstring(fullName.begin(), fullName.end()).c_str());
}

void PipeManager::Testing()
{
    BYTE buffer[1024];
    PipeReceive(buffer);
}

CPPClient.cpp (application entry point):

int main()
{
    //return HandlePipe("TestingPipe", false);
    PipeManager pipeManager = new PipeManager(std::string("TestingPipe"));
    pipeManager.Testing();
}

I have a pipe server running (written in c#) that creates "TestingPipe", that I know works and have tested. I have also managed to get a pipe client (written in c++) working but not inside a class - just a load of function calls and things in the main() method. When I run the above program, the c# server initially accepts the connection but then fails when the client tries to read saying "pipe is broken", and the c++ client fails with code 6 invalid handle when it tries to read from the pipe.

How can I keep pipeHandle constant throughout the lifetime of the object, or at least stop it changing after ConnectPipe finishes initialising?

Unless this isn't possible, in which case I'll have to reconnect to the pipe every time I want to read or write to it.

Edit: Removing code that wasn't relevant


Solution

  • Many thanks to Igor Tandetnik - in my main() function I had used the new keyword, which creates an object and copies it into a new object. It was this that caused the pipeHandle to change, not a referencing issue as I first thought.