Skip to content

Commit

Permalink
Make sure HANDLE on Windows has correct size
Browse files Browse the repository at this point in the history
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 2.0 branch.
  • Loading branch information
jonathanslenders committed Sep 12, 2019
1 parent 2eb19a1 commit a0e5037
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 33 deletions.
10 changes: 5 additions & 5 deletions prompt_toolkit/eventloop/asyncio_win32.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def __init__(self, loop=None):
self.closed = False

# Maps win32 handles to their callbacks.
self._handle_callbacks = {}
self._handle_callbacks = {} # HANDLE fd to callback.

def close(self):
# Note: we should not close the asyncio loop itself, because that one
Expand Down Expand Up @@ -90,7 +90,7 @@ def default_exception_handler(self, context):
def add_win32_handle(self, handle, callback):
" Add a Win32 handle to the event loop. "
callback = wrap_in_current_context(callback)
self._handle_callbacks[handle] = callback
self._handle_callbacks[handle.value] = callback

# Add reader.
def ready():
Expand All @@ -104,7 +104,7 @@ def ready():
# (Use an executor for this, the Windows asyncio event loop doesn't
# allow us to wait for handles like stdin.)
def wait():
if self._handle_callbacks.get(handle) != callback:
if self._handle_callbacks.get(handle.value) != callback:
return

wait_for_handles([handle])
Expand All @@ -114,5 +114,5 @@ def wait():

def remove_win32_handle(self, handle):
" Remove a Win32 handle from the event loop. "
if handle in self._handle_callbacks:
del self._handle_callbacks[handle]
if handle.value in self._handle_callbacks:
del self._handle_callbacks[handle.value]
26 changes: 13 additions & 13 deletions prompt_toolkit/eventloop/win32.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def __init__(self, recognize_paste=True):
self._running = False

# Additional readers.
self._read_fds = {} # Maps fd to handler.
self._read_fds = {} # Maps HANDLE value to handler.

# Create inputhook context.
self._inputhook_context = None
Expand Down Expand Up @@ -86,9 +86,9 @@ def ready(wait):
self._inputhook_context.call_inputhook(ready, inputhook)

# Wait for the next event.
handle = self._ready_for_reading(INFINITE)
handle = self._ready_for_reading(INFINITE).value

if handle == self._event:
if handle == self._event.value:
# When the Windows Event has been trigger, process the messages in the queue.
windll.kernel32.ResetEvent(self._event)
self._process_queued_calls_from_executor()
Expand All @@ -110,7 +110,7 @@ def _ready_for_reading(self, timeout=INFINITE):
Return the handle that is ready for reading or `None` on timeout.
"""
handles = [self._event]
handles.extend(self._read_fds.keys())
handles.extend(HANDLE(fd) for fd in self._read_fds.keys())
return wait_for_handles(handles, timeout)

def close(self):
Expand Down Expand Up @@ -161,23 +161,23 @@ def add_reader(self, fd, callback):
" Start watching the file descriptor for read availability. "
callback = wrap_in_current_context(callback)

h = msvcrt.get_osfhandle(fd)
h = HANDLE(msvcrt.get_osfhandle(fd))
self.add_win32_handle(h, callback)

def remove_reader(self, fd):
" Stop watching the file descriptor for read availability. "
h = msvcrt.get_osfhandle(fd)
h = HANDLE(msvcrt.get_osfhandle(fd))
self.remove_win32_handle(h)

def add_win32_handle(self, handle, callback):
" Add a Win32 handle to the event loop. "
callback = wrap_in_current_context(callback)
self._read_fds[handle] = callback
self._read_fds[handle.value] = callback

def remove_win32_handle(self, handle):
" Remove a Win32 handle from the event loop. "
if handle in self._read_fds:
del self._read_fds[handle]
if handle.value in self._read_fds:
del self._read_fds[handle.value]


def wait_for_handles(handles, timeout=INFINITE):
Expand All @@ -187,7 +187,7 @@ def wait_for_handles(handles, timeout=INFINITE):
http://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v=vs.85).aspx
"""
assert isinstance(handles, list)
assert isinstance(handles, list) # List of `HANDLE` objects.
assert isinstance(timeout, int)

arrtype = HANDLE * len(handles)
Expand All @@ -200,7 +200,7 @@ def wait_for_handles(handles, timeout=INFINITE):
return None
else:
h = handle_array[ret]
return h
return HANDLE(h)


def create_win32_event():
Expand All @@ -209,9 +209,9 @@ def create_win32_event():
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682396(v=vs.85).aspx
"""
return windll.kernel32.CreateEventA(
return HANDLE(windll.kernel32.CreateEventA(
pointer(SECURITY_ATTRIBUTES()),
BOOL(True), # Manual reset event.
BOOL(False), # Initial state.
None # Unnamed event object.
)
))
8 changes: 4 additions & 4 deletions prompt_toolkit/input/win32.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import sys
from contextlib import contextmanager
from ctypes import pointer, windll
from ctypes.wintypes import DWORD
from ctypes.wintypes import DWORD, HANDLE

import six
from six.moves import range
Expand Down Expand Up @@ -181,10 +181,10 @@ def __init__(self, recognize_paste=True):
# When stdin is a tty, use that handle, otherwise, create a handle from
# CONIN$.
if sys.stdin.isatty():
self.handle = windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)
self.handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE))
else:
self._fdcon = os.open('CONIN$', os.O_RDWR | os.O_BINARY)
self.handle = msvcrt.get_osfhandle(self._fdcon)
self.handle = HANDLE(msvcrt.get_osfhandle(self._fdcon))

def close(self):
" Close fdcon. "
Expand Down Expand Up @@ -465,7 +465,7 @@ class raw_mode(object):
`raw_input` method of `.vt100_input`.
"""
def __init__(self, fileno=None):
self.handle = windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)
self.handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE))

def __enter__(self):
# Remember original mode.
Expand Down
16 changes: 8 additions & 8 deletions prompt_toolkit/output/win32.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
pointer,
windll,
)
from ctypes.wintypes import DWORD
from ctypes.wintypes import DWORD, HANDLE

import six

Expand Down Expand Up @@ -88,7 +88,7 @@ def __init__(self, stdout, use_complete_width=False):

self._buffer = []
self.stdout = stdout
self.hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
self.hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE))

self._in_alternate_screen = False
self._hidden = False
Expand Down Expand Up @@ -370,8 +370,8 @@ def enter_alternate_screen(self):
GENERIC_WRITE = 0x40000000

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

self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, handle)
self.hconsole = handle
Expand All @@ -382,23 +382,23 @@ def quit_alternate_screen(self):
Make stdout again the active buffer.
"""
if self._in_alternate_screen:
stdout = self._winapi(windll.kernel32.GetStdHandle, STD_OUTPUT_HANDLE)
stdout = HANDLE(self._winapi(windll.kernel32.GetStdHandle, STD_OUTPUT_HANDLE))
self._winapi(windll.kernel32.SetConsoleActiveScreenBuffer, stdout)
self._winapi(windll.kernel32.CloseHandle, self.hconsole)
self.hconsole = stdout
self._in_alternate_screen = False

def enable_mouse_support(self):
ENABLE_MOUSE_INPUT = 0x10
handle = windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)
handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE))

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

def disable_mouse_support(self):
ENABLE_MOUSE_INPUT = 0x10
handle = windll.kernel32.GetStdHandle(STD_INPUT_HANDLE)
handle = HANDLE(windll.kernel32.GetStdHandle(STD_INPUT_HANDLE))

original_mode = DWORD()
self._winapi(windll.kernel32.GetConsoleMode, handle, pointer(original_mode))
Expand All @@ -420,7 +420,7 @@ def win32_refresh_window(cls):
to a bug in the Windows Console. Sending a repaint request solves it.
"""
# Get console handle
handle = windll.kernel32.GetConsoleWindow()
handle = HANDLE(windll.kernel32.GetConsoleWindow())

RDW_INVALIDATE = 0x0001
windll.user32.RedrawWindow(handle, None, None, c_uint(RDW_INVALIDATE))
Expand Down
6 changes: 3 additions & 3 deletions prompt_toolkit/output/windows10.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import unicode_literals

from ctypes import byref, windll
from ctypes.wintypes import DWORD
from ctypes.wintypes import DWORD, HANDLE

from prompt_toolkit.renderer import Output
from prompt_toolkit.utils import is_windows
Expand All @@ -26,7 +26,7 @@ class Windows10_Output(object):
def __init__(self, stdout):
self.win32_output = Win32Output(stdout)
self.vt100_output = Vt100_Output(stdout, lambda: None)
self._hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
self._hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE))

def flush(self):
"""
Expand Down Expand Up @@ -68,7 +68,7 @@ def is_win_vt100_enabled():
if not is_windows():
return False

hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE))

# Get original console mode.
original_mode = DWORD(0)
Expand Down

0 comments on commit a0e5037

Please sign in to comment.