【问题标题】:When working with a named pipe is there a way to do something like readlines()使用命名管道时,是否有办法执行 readlines() 之类的操作
【发布时间】:2015-08-18 05:26:49
【问题描述】:

总体目标:我正在尝试从 python exe 中读取一些进度数据,以更新另一个应用程序中 exe 的进度

我有一个 python exe 将做一些事情,我希望能够将进度传达给另一个程序。基于此处的其他几个问答,我已经能够让我正在运行的应用程序使用以下代码将进度数据发送到命名管道

import win32pipe
import win32file
import glob
test_files = glob.glob('J:\\someDirectory\\*.htm')
# test_files has two items a.htm and b.htm
p = win32pipe.CreateNamedPipe(r'\\.\pipe\wfsr_pipe',
                          win32pipe.PIPE_ACCESS_DUPLEX,
                          win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT,
                          1,65536,65536,300,None)
# the following line is the server-side function for accepting a connection
# see the following SO question and answer
""" http://stackoverflow.com/questions/1749001/named-pipes-between-c-sharp-and-python
"""
win32pipe.ConnectNamedPipe(p, None)
for each in testFiles:
    win32file.WriteFile(p,each + '\n')
#send final message
win32file.WriteFile(p,'Process Complete')
# close the connection
p.close()

简而言之,示例代码将每个文件的路径写入 NamedPipe - 这很有用,可以轻松扩展到更多日志记录类型的事件。但是,问题是试图弄清楚如何在不知道每个可能消息的大小的情况下读取命名管道的内容。例如,第一个文件可以命名为 J:\someDirectory\a.htm,但第二个文件的名称可以包含 300 个字符。

到目前为止,我用来读取管道内容的代码要求我指定缓冲区大小

首先建立连接

file_handle = win32file.CreateFile("\\\\.\\pipe\\wfsr_pipe",
                          win32file.GENERIC_READ | win32file.GENERIC_WRITE,
                          0, None,
                          win32file.OPEN_EXISTING,
                          0, None)

然后我一直在玩从文件中读取数据

data = win32file.ReadFile(file_handle,128)

这通常有效,但我真的很想阅读,直到我遇到换行符,对我开始阅读和换行符之间的内容做一些事情,然后重复这个过程,直到我到达一个有 Process Complete 的行线

在找到换行符 (\n) 之前,我一直在为如何阅读而苦苦挣扎。我基本上想逐行读取文件并根据该行的内容做一些事情(显示该行或转移应用程序焦点)。

根据@meuh 提供的建议,我正在更新此内容,因为我认为缺少有关如何使用管道的示例和指导

我的服务器代码

import win32pipe
import win32file
import glob
import os

p = win32pipe.CreateNamedPipe(r'\\.\pipe\wfsr_pipe',
                          win32pipe.PIPE_ACCESS_DUPLEX,
                          win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT,
                          1,65536,65536,300,None)
# the following line is the server-side function for accepting a connection
# see the following SO question and answer
""" http://stackoverflow.com/questions/1749001/named-pipes-between-c-sharp-and-python
"""
win32pipe.ConnectNamedPipe(p, None)
for file_id in glob.glob('J:\\level1\\level2\\level3\\*'):
    for filer_id in glob.glob(file_id + os.sep + '*'):
        win32file.WriteFile(p,filer_id)    
#send final message
win32file.WriteFile(p,'Process Complete')

# close the connection
p.close()  #still not sure if this should be here, I need more testing
# I think the client can close p

客户端代码

import win32pipe
import win32file

file_handle = win32file.CreateFile("\\\\.\\pipe\\wfsr_pipe",
                               win32file.GENERIC_READ |
                               win32file.GENERIC_WRITE,
                               0, None,win32file.OPEN_EXISTING,0, None)
# this is the key, setting readmode to MESSAGE
win32pipe.SetNamedPipeHandleState(file_handle,
                              win32pipe.PIPE_READMODE_MESSAGE, None, None)
# for testing purposes I am just going to write the messages to a file
out_ref = open('e:\\testpipe.txt','w')

dstring = ''  # need some way to know that the messages are complete
while dstring != 'Process Complete':
    # setting the blocksize at 4096 to make sure it can handle any message I
    # might anticipate
    data = win32file.ReadFile(file_handle,4096)
# data is a tuple, the first position seems to always be 0 but need to find 
# the docs to help understand what determines the value, the second is the 
# message
    dstring = data[1]
    out_ref.write(dstring + '\n')

out_ref.close() # got here so close my testfile
file_handle.close() # close the file_handle

【问题讨论】:

  • 也许你可以建立一个约定,发送进程发送一个固定长度的整数,表示行长,然后是行?

标签: python named-pipes pywin32


【解决方案1】:

我没有 Windows,但通过 api 看起来你应该转换 通过在 CreateFile() 调用之后添加您的客户端到消息模式:

win32pipe.SetNamedPipeHandleState(file_handle,
    win32pipe.PIPE_READMODE_MESSAGE, None, None)

然后每个足够长的读取将返回一条消息,即其他人在一次写入中所写的内容。您在创建管道时已经设置了 PIPE_TYPE_MESSAGE。

【讨论】:

  • 非常好和简单 - 谢谢你。我完成这项工作的关键是阅读你所说的一些通用长度,足够长以获取消息,因为管道能够发出消息
【解决方案2】:

您可以简单地使用 io.IOBase 的实现来包装 NamedPipe。

class PipeIO(io.RawIOBase):
    def __init__(self, handle):
        self.handle = handle
    def read(self, n):
        if (n == 0): return ""
        elif n == -1: return self.readall()
        data = win32file.ReadFile(self.file_handle,n)
        return data
    def readinto(self, b):
        data = self.read(len(b))
        for i in range(len(data)):
            b[i] = data[i]
        return len(data)
    def readall(self):
        data = ""
        while True:
            chunk = win32file.ReadFile(self.file_handle,10240)
            if (len(chunk) == 0): return data
            data += chunk

注意:未经测试,但在修复最终的拼写错误后应该可以工作。

你可以这样做:

with PipeIO(file_handle) as fd:
    for line in fd:
        # process a line

【讨论】:

    【解决方案3】:

    您可以使用msvcrt 模块和open 将管道转换为文件对象。

    发送代码

    import win32pipe
    import os
    import msvcrt
    from io import open
    
    pipe = win32pipe.CreateNamedPipe(r'\\.\pipe\wfsr_pipe',
                              win32pipe.PIPE_ACCESS_OUTBOUND,
                              win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT,
                              1,65536,65536,300,None)
    # wait for another process to connect
    win32pipe.ConnectNamedPipe(pipe, None)
    # get a file descriptor to write to
    write_fd = msvcrt.open_osfhandle(pipe, os.O_WRONLY)
    with open(write_fd, "w") as writer:
        # now we have a file object that we can write to in a standard way
        for i in range(0, 10):
            # create "a\n" in the first iteration, "bb\n" in the second and so on
            text = chr(ord("a") + i) * (i + 1) + "\n"
            writer.write(text)
    

    接收代码

    import win32file
    import os
    import msvcrt
    from io import open
    
    handle = win32file.CreateFile(r"\\.\pipe\wfsr_pipe",
                              win32file.GENERIC_READ,
                              0, None,
                              win32file.OPEN_EXISTING,
                              0, None)
    read_fd = msvcrt.open_osfhandle(handle, os.O_RDONLY)
    
    with open(read_fd, "r") as reader:
        # now we have a file object with the readlines and other file api methods
        lines = reader.readlines()
    
    print(lines)
    

    一些笔记。

    • 我只用 python 3.4 测试过这个,但我相信你可能使用的是 python 2.x。
    • 如果您尝试关闭文件对象和管道,Python 似乎会变得很奇怪...,所以我只使用了文件对象(通过使用 with 块)
    • 我只创建了文件对象,以便在一端读取并在另一端写入。您当然可以通过以下方式使文件对象双工
      • 使用os.O_RDWR 标志创建文件描述符(read_fdwrite_fd
      • "r+" 模式而不是"r""w" 模式创建文件对象
      • 回到使用win32pipe.PIPE_ACCESS_DUPLEX 标志创建管道
      • 回到创建带有win32file.GENERIC_READ | win32file.GENERIC_WRITE 标志的文件句柄对象。

    【讨论】:

    • 感谢您的努力 - 但我在编写代码时收到 TypeError,所以当我测试它时,'write_fd = msvcrt.open_osfhandle(pipe, os.O_WRONLY)' write_fd 是一个整数,在第一种情况下它的值为 3
    • 我的错误。我有一段时间没有使用 Python 2。为了澄清,write_fd 应该是一个 int。要在 python 2 中打开它,请使用 os.fdopenio.open
    • 我现在正在工作,虽然 help(msvcrt.open_osfhandle) 谢谢,此评论与您的​​编辑交叉
    • 我得到了这个工作,这对我很有帮助,因为我学到了很多东西,我标记了但给了第一个解决方案的解决方案,因为它更简单
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-16
    • 1970-01-01
    • 2023-03-30
    • 2018-10-28
    相关资源
    最近更新 更多