What is the proper way of communicating with named pipes on Windows from Python? I've googled it, and can't find any packages that wrap this communication.
There are:
I need just to connect to an existing named pipe and read/write to it. I previously had only tried communication with serial port (using pySerial), and I'm surprised how little info I could find on named pipes in comparison to it. There's usually tons of guides for any purpose for Python.
I'll appreciate any help.
To create a FIFO(named pipe) and use it in Python, you can use the os. mkfifo(). But mkfifo fails with File exists exception if file already exists. In order to avoid that, you can put it in a try-except block.
Named Pipes is a Windows mechanism that enables two unrelated processes to exchange data between themselves, even if the processes are located on two different networks. It's very simar to client/server architecture as notions such as a named pipe server and a named pipe client exist.
Use the "Find -> Find Handle or DLL..." option and enter the pattern "\Device\NamedPipe\". It will show you which processes have which pipes open.
If you specify NULL, the named pipe gets a default security descriptor. The ACLs in the default security descriptor for a named pipe grant full control to the LocalSystem account, administrators, and the creator owner. They also grant read access to members of the Everyone group and the anonymous account.
In order to connect to an existing named pipe you can utilize the CreateFile
API provided through the pywin32
package. Since it took me a while to put a working base together here is an example client/server which works fine for me (python 3.6.5, pywin32 223 on Windows 10 Pro x64):
import time
import sys
import win32pipe, win32file, pywintypes
def pipe_server():
print("pipe server")
count = 0
pipe = win32pipe.CreateNamedPipe(
r'\\.\pipe\Foo',
win32pipe.PIPE_ACCESS_DUPLEX,
win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_WAIT,
1, 65536, 65536,
0,
None)
try:
print("waiting for client")
win32pipe.ConnectNamedPipe(pipe, None)
print("got client")
while count < 10:
print(f"writing message {count}")
# convert to bytes
some_data = str.encode(f"{count}")
win32file.WriteFile(pipe, some_data)
time.sleep(1)
count += 1
print("finished now")
finally:
win32file.CloseHandle(pipe)
def pipe_client():
print("pipe client")
quit = False
while not quit:
try:
handle = win32file.CreateFile(
r'\\.\pipe\Foo',
win32file.GENERIC_READ | win32file.GENERIC_WRITE,
0,
None,
win32file.OPEN_EXISTING,
0,
None
)
res = win32pipe.SetNamedPipeHandleState(handle, win32pipe.PIPE_READMODE_MESSAGE, None, None)
if res == 0:
print(f"SetNamedPipeHandleState return code: {res}")
while True:
resp = win32file.ReadFile(handle, 64*1024)
print(f"message: {resp}")
except pywintypes.error as e:
if e.args[0] == 2:
print("no pipe, trying again in a sec")
time.sleep(1)
elif e.args[0] == 109:
print("broken pipe, bye bye")
quit = True
if __name__ == '__main__':
if len(sys.argv) < 2:
print("need s or c as argument")
elif sys.argv[1] == "s":
pipe_server()
elif sys.argv[1] == "c":
pipe_client()
else:
print(f"no can do: {sys.argv[1]}")
Example output client
> python pipe_test.py c
pipe client
no pipe, trying again in a sec
no pipe, trying again in a sec
no pipe, trying again in a sec
message: (0, b'0')
message: (0, b'1')
message: (0, b'2')
message: (0, b'3')
message: (0, b'4')
message: (0, b'5')
message: (0, b'6')
message: (0, b'7')
message: (0, b'8')
message: (0, b'9')
broken pipe, bye bye
Example output server
> python pipe_test.py s
pipe server
waiting for client
got client
writing message 0
writing message 1
writing message 2
writing message 3
writing message 4
writing message 5
writing message 6
writing message 7
writing message 8
writing message 9
finished now
Obviously you'd need some error checking around the various calls but that should work.
Additional side note: A colleague of mine ran into trouble with the pipe being closed the moment the client tried to perform I/O on it (exception claiming that "all pipe instances are busy"). It turned out that he was using os.path.exists
in the client code to test whether the named pipe already existed before running CreateFile
on it. This somehow breaks the pipe. So using the approach above (CreateFile
wrapped in a try-except) is the safe way of trying to connect to a pipe until it has been created by the server end.
I have success with something like the following fragment. This code is derived from CaptureSetup/Pipes — Python on Windows — The Wireshark Wiki. It requires win32pipe
and win32file
from the pywin32
package.
# pipename should be of the form \\.\pipe\mypipename
pipe = win32pipe.CreateNamedPipe(
pipename,
win32pipe.PIPE_ACCESS_OUTBOUND,
win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_WAIT,
1, 65536, 65536,
300,
None)
try:
win32pipe.ConnectNamedPipe(pipe, None)
while True:
some_data = b'12345...'
win32file.WriteFile(pipe, some_data)
...
finally:
win32file.CloseHandle(pipe)
I don't know if it's 100% correct in the way it closes the pipe.
You referred to The Perils and Triumphs of Being a Geek: Named Pipes between C# and Python—Jonathon Reinhart. I tried it, but it wasn't able to create the named pipe. I wonder if that code only works to open a named pipe that has already been created by another process.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With