Skip to content

Commit 4a653d5

Browse files
committed
fixed win32 create named pipe issue (replaced existing check in favor of try-n-create)
1 parent f67e140 commit 4a653d5

1 file changed

Lines changed: 45 additions & 41 deletions

File tree

src/namedpipe/_win32.py

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -30,39 +30,21 @@ def _wt(value: int) -> wintypes.DWORD:
3030
return wintypes.DWORD(value)
3131

3232

33-
def _name_pipe(kernel32) -> str:
33+
def _generate_pipe_path(name: str | None) -> str:
3434
"""return next available numeric pipe name
3535
36-
:return: "\\.\pipe\###" where ### is a unique number
36+
:return: "\\.\{name}" if name is given, else name is an auto-incremented integer
3737
"""
38+
3839
global id
3940

40-
notok = True
41-
while notok:
42-
name = rf"\\.\pipe\{id}"
41+
if name is None:
42+
pname = id
4343
id += 1
44+
else:
45+
pname = name
4446

45-
# make sure the pipe does not exist
46-
h = kernel32.CreateFileW(name, GENERIC_READ, 0, None, OPEN_EXISTING, 0, None)
47-
48-
if h == INVALID_HANDLE_VALUE:
49-
error_code = ctypes.get_last_error()
50-
51-
# ERROR_FILE_NOT_FOUND (2) means the pipe definitely does not exist
52-
if error_code == 2:
53-
notok = False
54-
elif error_code in (231, 5):
55-
# ERROR_ACCESS_DENIED (5) means it exists but your process lacks permissions
56-
# ERROR_PIPE_BUSY (231) means the pipe exists but all instances are occupied
57-
continue
58-
else:
59-
raise ctypes.WinError(error_code)
60-
else:
61-
# If it successfully opens (exists), close the handle
62-
kernel32.CloseHandle(h)
63-
notok = False
64-
65-
return name
47+
return rf"\\.\pipe\{pname}"
6648

6749

6850
def _win_error(code=None):
@@ -72,6 +54,9 @@ def _win_error(code=None):
7254

7355

7456
class NPopen:
57+
_path: str
58+
_pipe: int
59+
7560
def __init__(
7661
self,
7762
mode: Optional[str] = "r",
@@ -88,7 +73,6 @@ def __init__(
8873

8974
self.kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
9075
self.stream: Union[IO, None] = None # I/O stream of the pipe
91-
self._path = _name_pipe(self.kernel32) if name is None else rf"\\.\pipe\{name}"
9276
self._rd = any(mode and c in mode for c in "r+")
9377
self._wr = any(mode and c in mode for c in "wax+")
9478

@@ -134,20 +118,40 @@ def __init__(
134118
buffer_size = _wt(0)
135119
timeout = _wt(0)
136120

137-
# "open" named pipe
138-
h = self.kernel32.CreateNamedPipeW(
139-
self._path,
140-
access,
141-
pipe_mode,
142-
max_instances,
143-
buffer_size,
144-
buffer_size,
145-
timeout,
146-
None,
147-
)
148-
if h == INVALID_HANDLE_VALUE:
149-
raise _win_error()
150-
self._pipe = h
121+
def try_open_pipe(name: str | None) -> bool:
122+
123+
# "open" named pipe
124+
pipe_path = _generate_pipe_path(name)
125+
126+
h = self.kernel32.CreateNamedPipeW(
127+
pipe_path,
128+
access,
129+
pipe_mode,
130+
max_instances,
131+
buffer_size,
132+
buffer_size,
133+
timeout,
134+
None,
135+
)
136+
137+
if h == INVALID_HANDLE_VALUE:
138+
error_code = ctypes.get_last_error()
139+
140+
if name or error_code != 231:
141+
raise ctypes.WinError(error_code)
142+
else:
143+
# ERROR_PIPE_BUSY (231) means the pipe exists but all instances are occupied
144+
return True # already used
145+
146+
self._path = pipe_path
147+
self._pipe = h
148+
149+
return False # success
150+
151+
while try_open_pipe(name):
152+
# loop until success.
153+
# if name is given, runs only once
154+
...
151155

152156
@property
153157
def path(self):

0 commit comments

Comments
 (0)