Windows DuplicateHandle 命名管道句柄奇怪错误 183“文件已存在”

Posted

技术标签:

【中文标题】Windows DuplicateHandle 命名管道句柄奇怪错误 183“文件已存在”【英文标题】:Windows DuplicateHandle named pipe handle strange error 183 "file already exists" 【发布时间】:2021-04-22 08:32:30 【问题描述】:

我遇到了 DuplicateHandle (Win32) 的问题。我尝试复制命名管道句柄,但总是收到错误 183“文件已存在”。我不明白这个错误信息,因为我尝试创建一个文件句柄的副本,而新的文件句柄以前不存在。 (是否需要覆盖起始值?)这是我的电话:

return DuplicateHandle (MeshellProcessHandle, sourcehandle, HelperProcess, targethandle, 0, TRUE, DUPLICATE_SAME_ACCESS) != 0;

为了理解我在做什么,我必须更广泛地解释一下:我正在为命令行程序 cmd.exe 开发一个方便的编辑器前端。这个项目已经在对 Win32 非常熟悉的 OS/2 操作系统上运行良好,因为从历史的角度来看,这两个操作系统是一起开发的,直到一年前完成,微软和 IBM 走了不同的路。 这个程序的实现非常棘手:有一个窗口前端编辑器程序。该程序为 stderr、stdout 和 stderr 创建命名管道,但从相反的角度来看(cmd.exe 的输出是编辑器的输入)。由于不同会话之间的通信有限,我不得不编写一个“cmd 帮助程序”,它是一个小型命令行程序,包含多个 API 调用并与 cmd.exe 程序在同一个会话中运行。助手通过命令行参数获取编辑器进程 ID,并打开由窗口编辑器程序创建的现有管道,然后将 stdin/stdout/stderr 重定向到管道。助手通过“OpenProcess”API 调用从编辑器进程 ID 中获取编辑器的进程句柄。然后助手执行 cmd.exe,它自动继承 stdin/stdout/stderr 句柄,现在 cmd.exe 写入和读取管道。 另一种选择是在不使用 DuplicateHandle 的情况下将完整的管道名称解析为 cmd.exe,但我希望尽可能接近我的解决方案,该解决方案已经在 OS/2 操作系统上运行良好。

【问题讨论】:

您可能在错误的时间致电GetLastError。我怀疑这个 api 返回的STATUS_OBJECT_NAME_COLLISION 不要解释(你认为)你做了什么。改为显示minimal reproducible example。 是的,如果我在 API 调用后立即使用 GetLastError(),我会得到另一个结果。现在显示错误消息 5 Access denied。我在窗口主程序中的 API 调用中添加了 CreateNamedPipe SECURITY_ATTRIBUTES.bInheritHandle = TRUE,但是应该将标准输入/输出重定向到现有命名管道的助手程序中的 DuplicateHandle 调用仍然显示错误 5。有什么想法吗? 我还没有解决这个问题。我假设已经单独启动的帮助程序仍然没有复制命名管道文件句柄的权限。 【参考方案1】:

我仍然不确定为什么我没有复制管道句柄的访问权限。但我找到了另一个解决方案:我的辅助控制台程序启动子进程(cmd.exe),在这个子进程中,我想使用命名管道而不是 stdin/stdout/stderr - 这就是我想使用的原因重复句柄。 Windows 在使用启动子进程时提供了一个方便的解决方案 创建过程(...) 使用 CreateProcess,您必须始终在 STARTUPINFO 结构中保存参数。您可以设置三个句柄变量来将 stdin/stdout/stderr 重定向到三个命名管道 cmd_std*:

STARTUPINFO StartupInfo;
HFILE cmd_stdout, cmd_stdin, cmd_stderr;

//Open the existing pipes with CreateFile

//Start child process program

    StartupInfo.hStdOutput = &cmd_stdout;
    StartupInfo.hStdInput = &cmd_stdin;
    StartupInfo.hStdError = &cmd_stderr;

    CreateProcess (..., &StartupInfo, ...);

这个解决方案的代码比使用 DuplicateHandle 的变体少得多,因为我还必须保存和恢复原始文件句柄,所以这种方式替换了 9 个 DuplicateHandle 调用。

【讨论】:

【参考方案2】:

我现在对更改进行了编程,但我上面描述的新代码不起作用。如果我将 CreateProcess 中的“句柄继承标志”设置为 TRUE,则不会执行 cmd.exe,但 CreateProcess 返回 TRUE (=OK)。这是我的详细代码:

STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInformation;
HFILE cmd_stdout, cmd_stdin, cmd_stderr;
char *pprogstr, *pargstr;

//Open the existing pipes with CreateFile

//Start child process program cmd.exe
    StartupInfo.hStdOutput = &cmd_stdout;
    StartupInfo.hStdInput = &cmd_stdin;
    StartupInfo.hStdError = &cmd_stderr;
    StartupInfo.dwFlags =  STARTF_USESTDHANDLES;
    pprogstr = "C:\\WINDOWS\\system32\\cmd.exe";
    pargstr = "/K";
CreateProcess (
        pprogstr,   // pointer to name of executable module
        pargstr,    // pointer to command line string
        NULL,       // pointer to process security attributes
        NULL,       // pointer to thread security attributes
        TRUE,       // handle inheritance flag
        0,          // creation flags
        NULL,       // pointer to new environment block
        NULL,       // pointer to current directory name
        &StartupInfo,        // pointer to STARTUPINFO
        &ProcessInformation) // pointer to PROCESS_INFORMATION

有什么想法吗?

【讨论】:

观察到的行为是:cmd.exe 被执行,但似乎崩溃或立即结束。我必须先复制手柄吗?但是所有 DuplicateHandle 调用都以 LastError 5 access denied 结束。 这是一个来自MSN的示例:docs.microsoft.com/en-us/windows/win32/procthread/…用CreateFile打开现有管道后,需要定义管道句柄继承:SetHandleInformation(cmd_stdin, HANDLE_FLAG_INHERIT, 0) 用于三个管道stdin ,标准输出,标准错误。但是孩子在使用 CreateProcess 执行后仍然会出现问题。

以上是关于Windows DuplicateHandle 命名管道句柄奇怪错误 183“文件已存在”的主要内容,如果未能解决你的问题,请参考以下文章

vc++ 怎么结束 别的进程里面的一个线程?

开始-运行命今高手进入!

Linux中另一个进程的重复文件描述符(没有sendmsg)

在 Windows 7 上将文件重命名为 md5 sum + 扩展名(使用 CMD 或 PowerShell 2013)

WINDOWS 批处理命COPY 合并多个文件的问题

夺命雷公狗---linux NO:30 linux之通过X-Shell在windows上传文件到服务器上