Windows 命名管道:当 reader 关闭其末端而无需写入数据时,在编写器端检测 Python

Windows named pipe: detect in Python on writer side when reader has closed its end without having to write data

我的应用程序将数据流式传输(写入)到命名管道中,调用应用程序时将管道名称指定为 CLI 参数。流数据是不规则的,其中可能没有任何数据可以通过管道发送。

我想检测从管道读取的其他进程何时结束,以便快速释放我的应用程序为流式传输分配的资源。我现在的问题是甚至在没有向管道写入任何内容的情况下检测到管道的读取端已经关闭。

由于流数据格式是固定的,不允许空写入或 ping,即使我没有任何流数据,我也不能简单地尝试写入一些管道数据以查看管道 reader 是否可用仍在阅读。

我在 Linux 上有一个可行的解决方案,不幸的是它在 Windows 上不起作用,因为 Windows 命名管道不能在 select() 中统一处理喜欢 Linux。在 Linux,我只是检查管道的写入端是否变为可读,因为这表示管道错误,然后关闭管道并释放我分配的资源。

在 Windows,这是不可能的。我已经打开了写作的管道:

fifo = open('//./pipe/somepipe', 'wb')

尝试从管道 fifo.read() 不起作用(正如预期的那样)并立即抛出 OSException.

正如我所说,我不能尝试写一些empty/nul; fifo.write(b'') 什么都不做,甚至根本不戳管子以获得可写性。

在 Windows 上有什么方法可以测试命名管道的写入端以查看 reader(客户端)是否仍然连接?

正如@eryksun 上面指出的那样,实际上可以直接使用 Win32 API WriteFile 编写探测零长度字节字符串:如果管道关闭,那么这将 return "no success" (false/0),如果管道还活着,那么"success" (true/!=0)。正是我所要求的。因为我发现这比使用 NtQueryInformationFile 更简单,所以我现在使用空写方法;这是一个简化的例子:

import ctypes
from ctypes import byref, c_ulong, c_char_p
import msvcrt
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

fifo = open('//./pipe/somepipe', 'wb')
data = b''
written = c_ulong(0)
if not kernel32.WriteFile(
        msvcrt.get_osfhandle(fifo.fileno()),
        c_char_p(data), 0, 
        byref(written), 
        None):
    last_error = ctypes.get_last_error()
    if last_error in (
            0x000000E8,  # ERROR_NO_DATA
            # enable as required: 0x000000E9,  # ERROR_PIPE_NOT_CONNECTED
    ):
        # pipe broken
        pass
    else:
        # something else is wrong...
        pass
else:
    # pipe still okay
    pass

有用的资源: