简体   繁体   English

使用命名管道时,有没有办法做类似readlines()的操作

[英]When working with a named pipe is there a way to do something like readlines()

Overall Goal: I am trying to read some progress data from a python exe to update the progress of the exe in another application 总体目标:我正在尝试从python exe读取一些进度数据,以在另一个应用程序中更新exe的进度

I have a python exe that is going to do some stuff, I want to be able to communicate the progress to another program. 我有一个要执行某些操作的python exe,我希望能够将进度传达给另一个程序。 Based on several other Q&A here I have been able to have my running application send progress data to a named pipe using the following code 基于这里的其他一些问答,我能够使用以下代码使正在运行的应用程序将进度数据发送到命名管道

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()

In short the example code writes the path of the each file that was globbed to the NamedPipe - this is useful and can be easily extended to more logging type events. 简而言之,示例代码将每个文件的路径写入到NamedPipe中-这很有用,并且可以轻松地扩展到更多日志类型事件。 However, the problem is trying to figure out how to read the content of the named pipe without knowing the size of each possible message. 但是,问题是试图弄清楚如何在不知道每个可能的消息大小的情况下读取命名管道的内容。 For example the first file could be named J:\\someDirectory\\a.htm, but the second could have 300 characters in the name. 例如,第一个文件可以命名为J:\\ someDirectory \\ a.htm,但是第二个文件可以包含300个字符。

So far the code I am using to read the contents of the pipe requires that I specify a buffer size 到目前为止,我用于读取管道内容的代码要求我指定缓冲区大小

First establish the connection 首先建立连接

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

and then I have been playing around with reading from the file 然后我一直在玩读文件

data = win32file.ReadFile(file_handle,128)

This generally works but I really want to read until I hit a newline character, do something with the content between when I started reading and the newline character and then repeat the process until I get to a line that has Process Complete in the line 通常这是可行的,但我真的很想读,直到我碰到换行符为止,在我开始阅读和换行符之间进行内容处理,然后重复该过程,直到行中出现Process Complete为止。

I have been struggling with how to read only until I find a newline character (\\n). 在寻找换行符(\\ n)之前,我一直在努力只阅读。 I basically want to read the file by lines and based on the content of the line do something (either display the line or shift the application focus). 我基本上想按行读取文件,并根据行的内容执行某些操作(显示行或转移应用程序焦点)。

Based on the suggestion provided by @meuh I am updating this because I think there is a dearth of examples, guidance in how to use pipes 根据@meuh提供的建议,我正在对此进行更新,因为我认为缺少示例,如何使用管道的指南

My server code 我的服务器代码

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

The Client code 客户端代码

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

I don't have windows but looking through the api it seems you should convert your client to message mode by adding after the CreateFile() the call: 我没有Windows,但是通过api看来,您应该通过在CreateFile()之后添加调用来将客户端转换为消息模式:

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

then each sufficiently long read will return a single message, ie what the other wrote in a single write. 那么每次足够长的读取将返回一条消息,即另一条消息在一次写入中所写的内容。 You already set PIPE_TYPE_MESSAGE when you created the pipe. 创建管道时,您已经设置了PIPE_TYPE_MESSAGE。

You could simply use an implementation of io.IOBase that would wrap the NamedPipe. 您可以简单地使用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

BEWARE : untested, but it should work after fixing the eventual typos. 注意:未经测试,但在修正最终的错字后应该可以使用。

You could then do: 然后,您可以执行以下操作:

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

You could use the msvcrt module and open to turn the pipe into a file object. 您可以使用msvcrt模块并open以将管道变成文件对象。

Sending code 发送代码

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)

Receiving code 接收码

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)

Some notes. 一些注意事项。

  • I've only tested this with python 3.4, but I believe you may be using python 2.x. 我仅使用python 3.4进行了测试,但是我相信您可能正在使用python2.x。
  • Python seems to get weird if you try to close both the file object and the pipe..., so I've only used the file object (by using the with block) 如果您尝试同时关闭文件对象和管道,Python似乎会变得很奇怪...,所以我只使用了文件对象(通过使用with块)
  • I've only created the file objects to read on one end and write on the other. 我只创建了要在一端读取并在另一端写入的文件对象。 You can of course make the file objects duplex by 您当然可以通过以下方式使文件对象成为双工
    • Creating the file descriptors ( read_fd and write_fd ) with the os.O_RDWR flag 使用os.O_RDWR标志创建文件描述符( read_fdwrite_fd
    • Creating the file objects in in "r+" mode rather than "r" or "w" "r+"模式而不是"r""w"模式创建文件对象
    • Going back to creating the pipe with the win32pipe.PIPE_ACCESS_DUPLEX flag 返回到使用win32pipe.PIPE_ACCESS_DUPLEX标志创建管道
    • Going back to creating the file handle object with the win32file.GENERIC_READ | win32file.GENERIC_WRITE 返回到使用win32file.GENERIC_READ | win32file.GENERIC_WRITE创建文件句柄对象win32file.GENERIC_READ | win32file.GENERIC_WRITE win32file.GENERIC_READ | win32file.GENERIC_WRITE flags. win32file.GENERIC_READ | win32file.GENERIC_WRITE标志。

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

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