简体   繁体   English

Windows:检查命名 pipe 的存在

[英]Windows: Check existence of named pipe

I am writing a C++ program that makes use of named pipes on Windows.我正在编写一个使用 Windows 上的命名管道的 C++ 程序。 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.唯一缺少的部分是 function 用于检查管道是否存在。

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 .来自 Unix 世界,我最初尝试std::filesystem::exists("\\\\.\\pipe\\myPipe")但这并不可靠,并且经常与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).在寻找检查管道是否存在的替代方法时,我在 GitHub(Boost 进程)上偶然发现了 这个问题,从那里我认为 Boos 进程通过使用特殊的命名方案和计数器来规避问题,然后跟踪在内部(虽然似乎只适用于通过 Boost 进程创建的管道)。

Furthermore according to How can I get a list of all open named pipes in Windows?此外,根据如何获取 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.这些解决方案虽然没有使用 C++,但我没有找到将其移植过来的方法。

After having read the documentation of CreateNamedPipe , I now assembled the following solution to my problem:在阅读了CreateNamedPipe 的文档后,我现在组装了以下解决方案来解决我的问题:

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.然而,试图创建一个命名的 pipe 只是为了检查是否已经存在一个具有该名称的名称似乎已经有很多不必要的开销。 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 .此外,我不确定此解决方案是否普遍适用或仅在测试的 pipe 也是使用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?因此我的问题是:有没有更好的方法来检查 Windows 中是否已经存在具有给定名称的命名 pipe ?

std::filesystem::exists("\\\\.\\pipe\\myPipe") returning ERROR_PIPE_BUSY means it is using CreateFile() to actually connect to the pipe. std::filesystem::exists("\\\\.\\pipe\\myPipe")返回ERROR_PIPE_BUSY意味着它正在使用CreateFile()实际连接到 pipe。 It is not unreasonable for an exists() implementation to attempt to open the requested file to check its existance. exists()实现尝试打开请求的文件以检查其存在并不是不合理的。

Per the CreateFile() documentation :根据CreateFile()文档

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 .如果至少有一个活动的 pipe 实例但服务器上没有可用的侦听器管道,这意味着所有 pipe 实例当前都已连接,则CreateFile失败并ERROR_PIPE_BUSY

Which means the pipe does technically exist, it is not ready to receive a new client at that moment.这意味着 pipe 在技术上确实存在,此时它还没有准备好接收新客户。

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\" .您提供的链接中,提供的许多解决方案建议使用 .NET 的System.IO.Directory.GetFiles()方法来遍历"\\.\pipe\"的内容。 This answer shows how that call translates into Win32 API calls using FindFirstFile() and FindNextFile() .此答案显示该调用如何使用FindFirstFile()FindNextFile()转换为 Win32 API 调用。 You can easily do the same API calls in C++, eg:您可以在 C++ 中轻松执行相同的 API 调用,例如:

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:更新:或者,使用带有 Unicode API 的std::wstring代替,因为文件系统在 Windows 上本机是 Unicode:

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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