Skip to content

Commit 064c9e1

Browse files
Make sure HANDLE on Windows has a correct size
Previously, the type of various HANDLEs are native Python integer types. The ctypes library will treat them as 4-byte integer when used in function arguments. On 64-bit Windows, HANDLE is 8-byte and usually a small integer. Depending on whether the extra 4 bytes are zero-ed out or not, things can happen to work, or break. This patch adds explicit type casts so ctypes uses 8-byte integers for HANDLEs on 64-bit Windows. Thanks to @quark-zju for providing this patch to the 1.0 branch. This patch contains similar changes for the the 3.0 branch.
1 parent d0003a0 commit 064c9e1

File tree

4 files changed

+41
-30
lines changed

4 files changed

+41
-30
lines changed

prompt_toolkit/eventloop/win32.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,22 @@
1414
INFINITE = -1
1515

1616

17-
def wait_for_handles(handles: List[int], timeout: int = INFINITE) -> Optional[HANDLE]:
17+
def wait_for_handles(handles: List[HANDLE], timeout: int = INFINITE) -> Optional[HANDLE]:
1818
"""
1919
Waits for multiple handles. (Similar to 'select') Returns the handle which is ready.
2020
Returns `None` on timeout.
2121
http://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v=vs.85).aspx
22+
23+
Note that handles should be a list of `HANDLE` objects, not integers. See
24+
this comment in the patch by @quark-zju for the reason why:
25+
26+
''' Make sure HANDLE on Windows has a correct size
27+
28+
Previously, the type of various HANDLEs are native Python integer
29+
types. The ctypes library will treat them as 4-byte integer when used
30+
in function arguments. On 64-bit Windows, HANDLE is 8-byte and usually
31+
a small integer. Depending on whether the extra 4 bytes are zero-ed out
32+
or not, things can happen to work, or break. '''
2233
"""
2334
arrtype = HANDLE * len(handles)
2435
handle_array = arrtype(*handles)
@@ -30,17 +41,17 @@ def wait_for_handles(handles: List[int], timeout: int = INFINITE) -> Optional[HA
3041
return None
3142
else:
3243
h = handle_array[ret]
33-
return h
44+
return HANDLE(h)
3445

3546

3647
def create_win32_event():
3748
"""
3849
Creates a Win32 unnamed Event .
3950
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682396(v=vs.85).aspx
4051
"""
41-
return windll.kernel32.CreateEventA(
52+
return HANDLE(windll.kernel32.CreateEventA(
4253
pointer(SECURITY_ATTRIBUTES()),
4354
BOOL(True), # Manual reset event.
4455
BOOL(False), # Initial state.
4556
None # Unnamed event object.
46-
)
57+
))

prompt_toolkit/input/win32.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from asyncio import get_event_loop
66
from contextlib import contextmanager
77
from ctypes import pointer, windll
8-
from ctypes.wintypes import DWORD
8+
from ctypes.wintypes import DWORD, HANDLE
99
from typing import Callable, ContextManager, Dict, Iterable, Optional
1010

1111
from prompt_toolkit.eventloop import run_in_executor_with_context
@@ -43,7 +43,7 @@ def __init__(self) -> None:
4343

4444
@property
4545
@abstractmethod
46-
def handle(self) -> int:
46+
def handle(self) -> HANDLE:
4747
pass
4848

4949

@@ -100,7 +100,7 @@ def close(self) -> None:
100100
self.console_input_reader.close()
101101

102102
@property
103-
def handle(self) -> int:
103+
def handle(self) -> HANDLE:
104104
return self.console_input_reader.handle
105105

106106

@@ -192,12 +192,12 @@ def __init__(self, recognize_paste: bool = True) -> None:
192192

193193
# When stdin is a tty, use that handle, otherwise, create a handle from
194194
# CONIN$.
195-
self.handle: int
195+
self.handle: HANDLE
196196
if sys.stdin.isatty():
197-
self.handle = windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)
197+
self.handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE))
198198
else:
199199
self._fdcon = os.open('CONIN$', os.O_RDWR | os.O_BINARY)
200-
self.handle = msvcrt.get_osfhandle(self._fdcon)
200+
self.handle = HANDLE(msvcrt.get_osfhandle(self._fdcon))
201201

202202
def close(self) -> None:
203203
" Close fdcon. "
@@ -428,12 +428,12 @@ class _Win32Handles:
428428
def __init__(self) -> None:
429429
self._handle_callbacks: Dict[int, Callable[[], None]] = {}
430430

431-
def add_win32_handle(self, handle: int, callback: Callable[[], None]) -> None:
431+
def add_win32_handle(self, handle: HANDLE, callback: Callable[[], None]) -> None:
432432
"""
433433
Add a Win32 handle to the event loop.
434434
"""
435435
loop = get_event_loop()
436-
self._handle_callbacks[handle] = callback
436+
self._handle_callbacks[handle.value] = callback
437437

438438
# Add reader.
439439
def ready() -> None:
@@ -447,20 +447,20 @@ def ready() -> None:
447447
# (Use an executor for this, the Windows asyncio event loop doesn't
448448
# allow us to wait for handles like stdin.)
449449
def wait() -> None:
450-
if self._handle_callbacks.get(handle) != callback:
450+
if self._handle_callbacks.get(handle.value) != callback:
451451
return
452452

453453
wait_for_handles([handle])
454454
loop.call_soon_threadsafe(ready)
455455

456456
run_in_executor_with_context(wait, loop=loop)
457457

458-
def remove_win32_handle(self, handle: int) -> None:
458+
def remove_win32_handle(self, handle: HANDLE) -> None:
459459
"""
460460
Remove a Win32 handle from the event loop.
461461
"""
462-
if handle in self._handle_callbacks:
463-
del self._handle_callbacks[handle]
462+
if handle.value in self._handle_callbacks:
463+
del self._handle_callbacks[handle.value]
464464

465465

466466
@contextmanager
@@ -475,7 +475,7 @@ def attach_win32_input(input: _Win32InputBase, callback: Callable[[], None]):
475475
handle = input.handle
476476

477477
# Add reader.
478-
previous_callback = win32_handles._handle_callbacks.get(handle)
478+
previous_callback = win32_handles._handle_callbacks.get(handle.value)
479479
win32_handles.add_win32_handle(handle, callback)
480480

481481
try:
@@ -492,7 +492,7 @@ def detach_win32_input(input: _Win32InputBase):
492492
win32_handles = input.win32_handles
493493
handle = input.handle
494494

495-
previous = win32_handles._handle_callbacks.get(handle)
495+
previous = win32_handles._handle_callbacks.get(handle.value)
496496
if previous:
497497
win32_handles.remove_win32_handle(handle)
498498

@@ -514,7 +514,7 @@ class raw_mode:
514514
`raw_input` method of `.vt100_input`.
515515
"""
516516
def __init__(self, fileno=None):
517-
self.handle = windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)
517+
self.handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE))
518518

519519
def __enter__(self):
520520
# Remember original mode.

prompt_toolkit/output/win32.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
pointer,
1010
windll,
1111
)
12-
from ctypes.wintypes import DWORD
12+
from ctypes.wintypes import DWORD, HANDLE
1313
from typing import Dict, List, TextIO, Tuple
1414

1515
from prompt_toolkit.data_structures import Size
@@ -86,7 +86,7 @@ def __init__(self, stdout: TextIO, use_complete_width: bool = False) -> None:
8686

8787
self._buffer: List[str] = []
8888
self.stdout = stdout
89-
self.hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
89+
self.hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE))
9090

9191
self._in_alternate_screen = False
9292
self._hidden = False
@@ -367,8 +367,8 @@ def enter_alternate_screen(self) -> None:
367367
GENERIC_WRITE = 0x40000000
368368

369369
# Create a new console buffer and activate that one.
370-
handle = self._winapi(windll.kernel32.CreateConsoleScreenBuffer, GENERIC_READ|GENERIC_WRITE,
371-
DWORD(0), None, DWORD(1), None)
370+
handle = HANDLE(self._winapi(windll.kernel32.CreateConsoleScreenBuffer, GENERIC_READ|GENERIC_WRITE,
371+
DWORD(0), None, DWORD(1), None))
372372

373373
self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, handle)
374374
self.hconsole = handle
@@ -379,23 +379,23 @@ def quit_alternate_screen(self) -> None:
379379
Make stdout again the active buffer.
380380
"""
381381
if self._in_alternate_screen:
382-
stdout = self._winapi(windll.kernel32.GetStdHandle, STD_OUTPUT_HANDLE)
382+
stdout = HANDLE(self._winapi(windll.kernel32.GetStdHandle, STD_OUTPUT_HANDLE))
383383
self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, stdout)
384384
self._winapi(windll.kernel32.CloseHandle, self.hconsole)
385385
self.hconsole = stdout
386386
self._in_alternate_screen = False
387387

388388
def enable_mouse_support(self) -> None:
389389
ENABLE_MOUSE_INPUT = 0x10
390-
handle = windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)
390+
handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE))
391391

392392
original_mode = DWORD()
393393
self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode))
394394
self._winapi(windll.kernel32.SetConsoleMode, handle, original_mode.value | ENABLE_MOUSE_INPUT)
395395

396396
def disable_mouse_support(self) -> None:
397397
ENABLE_MOUSE_INPUT = 0x10
398-
handle = windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)
398+
handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE))
399399

400400
original_mode = DWORD()
401401
self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode))
@@ -417,7 +417,7 @@ def win32_refresh_window(cls) -> None:
417417
to a bug in the Windows Console. Sending a repaint request solves it.
418418
"""
419419
# Get console handle
420-
handle = windll.kernel32.GetConsoleWindow()
420+
handle = HANDLE(windll.kernel32.GetConsoleWindow())
421421

422422
RDW_INVALIDATE = 0x0001
423423
windll.user32.RedrawWindow(handle, None, None, c_uint(RDW_INVALIDATE))

prompt_toolkit/output/windows10.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from ctypes import byref, windll
2-
from ctypes.wintypes import DWORD
2+
from ctypes.wintypes import DWORD, HANDLE
33
from typing import Any, TextIO
44

55
from prompt_toolkit.data_structures import Size
@@ -26,7 +26,7 @@ class Windows10_Output:
2626
def __init__(self, stdout: TextIO) -> None:
2727
self.win32_output = Win32Output(stdout)
2828
self.vt100_output = Vt100_Output(stdout, lambda: Size(0, 0))
29-
self._hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
29+
self._hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE))
3030

3131
def flush(self) -> None:
3232
"""
@@ -68,7 +68,7 @@ def is_win_vt100_enabled() -> bool:
6868
if not is_windows():
6969
return False
7070

71-
hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
71+
hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE))
7272

7373
# Get original console mode.
7474
original_mode = DWORD(0)

0 commit comments

Comments
 (0)