Report errors when writing to windows console fails

This commit is contained in:
Kovid Goyal 2014-02-11 07:26:31 +05:30
parent 3befc25576
commit 8ff7bf389a

View File

@ -104,16 +104,16 @@ class Detect(object):
import msvcrt import msvcrt
self.msvcrt = msvcrt self.msvcrt = msvcrt
self.file_handle = msvcrt.get_osfhandle(self.stream.fileno()) self.file_handle = msvcrt.get_osfhandle(self.stream.fileno())
from ctypes import windll, wintypes, byref, c_wchar_p, c_size_t, POINTER from ctypes import windll, wintypes, byref, c_wchar_p, c_size_t, POINTER, WinDLL
mode = wintypes.DWORD(0) mode = wintypes.DWORD(0)
f = windll.kernel32.GetConsoleMode f = windll.kernel32.GetConsoleMode
f.restype = wintypes.BOOL f.argtypes, f.restype = [wintypes.HANDLE, POINTER(wintypes.DWORD)], wintypes.BOOL
if f(self.file_handle, byref(mode)): if f(self.file_handle, byref(mode)):
# Stream is a console # Stream is a console
self.set_console = windll.kernel32.SetConsoleTextAttribute self.set_console = windll.kernel32.SetConsoleTextAttribute
self.wcslen = crt().wcslen self.wcslen = crt().wcslen
self.wcslen.argtypes, self.wcslen.restype = [c_wchar_p], c_size_t self.wcslen.argtypes, self.wcslen.restype = [c_wchar_p], c_size_t
self.write_console = windll.kernel32.WriteConsoleW self.write_console = WinDLL('kernel32', use_last_error=True).WriteConsoleW
self.write_console.argtypes = [wintypes.HANDLE, wintypes.c_wchar_p, wintypes.DWORD, POINTER(wintypes.DWORD), wintypes.LPVOID] self.write_console.argtypes = [wintypes.HANDLE, wintypes.c_wchar_p, wintypes.DWORD, POINTER(wintypes.DWORD), wintypes.LPVOID]
self.write_console.restype = wintypes.BOOL self.write_console.restype = wintypes.BOOL
self.is_console = True self.is_console = True
@ -125,17 +125,36 @@ class Detect(object):
if self.is_console: if self.is_console:
from ctypes import wintypes, byref, c_wchar_p from ctypes import wintypes, byref, c_wchar_p
written = wintypes.DWORD(0) written = wintypes.DWORD(0)
chunk = 1024 * 16 chunk = len(text)
while text: while text:
t, text = text[:chunk], text[chunk:] # WriteConsoleW fails for large strings since it depends on the amount of free heap t, text = text[:chunk], text[chunk:]
wt = c_wchar_p(t) wt = c_wchar_p(t)
if not self.write_console(self.file_handle, wt, self.wcslen(wt), byref(written), None) and chunk >= 128: if not self.write_console(self.file_handle, wt, self.wcslen(wt), byref(written), None):
# Older versions of windows can fail to write large strings
# to console with WriteConsoleW (seen it happen on Win XP)
import ctypes, winerror
err = ctypes.get_last_error()
if err == winerror.ERROR_NOT_ENOUGH_MEMORY and chunk >= 128:
# Retry with a smaller chunk size (give up if chunk < 128) # Retry with a smaller chunk size (give up if chunk < 128)
chunk = chunk // 2 chunk = chunk // 2
text = t + text text = t + text
continue
if err == winerror.ERROR_GEN_FAILURE:
# On newer windows, this happens when trying to write
# non-ascii chars to the console and the console is set
# to use raster fonts (the default). In this case
# rather than failing, write an informative error
# message and the asciized version of the text.
print ('Non-ASCII text detected. You must set your Console\'s font to'
' Lucida Console or Consolas or some other TrueType font to see this text', file=self.stream, end='')
from calibre.utils.filenames import ascii_text
print (ascii_text(t + text), file=self.stream, end='')
continue
raise ctypes.WinError(err)
_crt = None _crt = None
def crt(): def crt():
# We use the C runtime bundled with the calibre windows build
global _crt global _crt
if _crt is None: if _crt is None:
import glob, ctypes import glob, ctypes