简体   繁体   中英

Windows: Check existence of named pipe

I am writing a C++ program that makes use of named pipes on Windows. I can create and work with them quite fine. The only piece missing to the puzzle is a function to check for a pipe's existence.

Coming from the Unix world I originally tried std::filesystem::exists("\\\\.\\pipe\\myPipe") but this is not reliable and often errors with ERROR_PIPE_BUSY .

While searching for an alternative way to check for a pipe's existence, I stumbled upon this issue on GitHub (Boost process) and from there I take it that Boos process circumvents the problem by using a special naming scheme and a counter and then keeping track of that internally (only seems to work for pipes created via Boost process though).

Furthermore according to How can I get a list of all open named pipes in Windows? it seems that there are ways to list the existing named pipes. These solutions are not using C++ though and I did not find a way to port that over.

After having read the documentation of CreateNamedPipe , I now assembled the following solution to my problem:

bool NamedPipe::exists(const std::filesystem::path &pipePath) {
    if (pipePath.parent_path() != "\\\\.\\pipe") {
        // This can't be a pipe, so it also can't exist
        return false;
    }

    // Attempt to create a pipe with FILE_FLAG_FIRST_INSTANCE so that the creation will fail
    // if the pipe already exists
    HANDLE pipeHandle = CreateNamedPipe(pipePath.string().c_str(),
                                        PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE,
                                        PIPE_TYPE_BYTE | PIPE_WAIT,
                                        1,   // # of allowed pipe instances
                                        0,   // Size of outbound buffer
                                        0,   // Size of inbound buffer
                                        0,   // Use default wait time
                                        NULL // Use default security attributes
    );

    if (pipeHandle == INVALID_HANDLE_VALUE) {
        // Creation has failed
        // It has failed (most likely) due to there alredy existing a pipe with
        // that name
        return true;
    } else {
        // The creation has succeeded
        if(!CloseHandle(pipeHandle)) {
            throw PipeException< DWORD >(GetLastError(), "CheckExistance");
        }

        return false;
    }
}

However attempting to create a named pipe only to check whether there already exists one with that name already seems like a lot of unnecessary overhead. Furthermore I am unsure of whether this solution is universally applicable or only works if the pipe tested for was also created with FILE_FLAG_FIRST_PIPE_INSTANCE .

Therefore my question is: Is there a better way to check whether a named pipe with the given name already exists in Windows?

std::filesystem::exists("\\\\.\\pipe\\myPipe") returning ERROR_PIPE_BUSY means it is using CreateFile() to actually connect to the pipe. It is not unreasonable for an exists() implementation to attempt to open the requested file to check its existance.

Per the CreateFile() documentation :

If there is at least one active pipe instance but there are no available listener pipes on the server, which means all pipe instances are currently connected, CreateFile fails with ERROR_PIPE_BUSY .

Which means the pipe does technically exist, it is not ready to receive a new client at that moment.

In the link you provided , many of the solutions provided suggest using .NET's System.IO.Directory.GetFiles() method to iterate though the contents of "\\.\pipe\" . This answer shows how that call translates into Win32 API calls using FindFirstFile() and FindNextFile() . You can easily do the same API calls in C++, eg:

bool NamedPipe::exists(const std::filesystem::path &pipePath)
{
    std::string pipeName = pipePath.string();
    if ((pipeName.size() < 10) ||
        (pipeName.compare(0, 9, "\\\\.\\pipe\\") != 0) ||
        (pipeName.find('\\', 9) != std::string::npos))
    {
        // This can't be a pipe, so it also can't exist
        return false;
    }
    pipeName.erase(0, 9);

    WIN32_FIND_DATA fd;
    DWORD dwErrCode;

    HANDLE hFind = FindFirstFileA("\\\\.\\pipe\\*", &fd);
    if (hFind == INVALID_HANDLE_VALUE)
    {
        dwErrCode = GetLastError();
    }
    else
    {
        do
        {
            if (pipeName == fd.cFileName)
            {
                FindClose(hFind);
                return true;
            }
        }
        while (FindNextFileA(hFind, &fd));

        dwErrCode = GetLastError();
        FindClose(hFind);
    }

    if ((dwErrCode != ERROR_FILE_NOT_FOUND) &&
        (dwErrCode != ERROR_NO_MORE_FILES))
    {
        throw PipeException< DWORD >(dwErrCode, "CheckExistance");
    }

    return false;
}

UPDATE : or, using std::wstring with Unicode APIs instead, since the filesystem is natively Unicode on Windows:

bool NamedPipe::exists(const std::filesystem::path &pipePath)
{
    std::wstring pipeName = pipePath;
    if ((pipeName.size() < 10) ||
        (pipeName.compare(0, 9, L"\\\\.\\pipe\\") != 0) ||
        (pipeName.find(L'\\', 9) != std::string::npos))
    {
        // This can't be a pipe, so it also can't exist
        return false;
    }
    pipeName.erase(0, 9);

    WIN32_FIND_DATAW fd;
    DWORD dwErrCode;

    HANDLE hFind = FindFirstFileW(L"\\\\.\\pipe\\*", &fd);
    if (hFind == INVALID_HANDLE_VALUE)
    {
        dwErrCode = GetLastError();
    }
    else
    {
        do
        {
            if (pipeName == fd.cFileName)
            {
                FindClose(hFind);
                return true;
            }
        }
        while (FindNextFileW(hFind, &fd));

        dwErrCode = GetLastError();
        FindClose(hFind);
    }

    if ((dwErrCode != ERROR_FILE_NOT_FOUND) &&
        (dwErrCode != ERROR_NO_MORE_FILES))
    {
        throw PipeException< DWORD >(dwErrCode, "CheckExistance");
    }

    return false;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM