Improved error handling and cosmetics for ctypes calls on Windows.

Signed-off-by: Daira Hopwood <daira@jacaranda.org>
This commit is contained in:
Daira Hopwood 2015-12-28 20:27:35 +00:00
parent 56bf458355
commit e88e07a278
2 changed files with 62 additions and 28 deletions

View File

@ -4,6 +4,11 @@ Futz with files like a pro.
import sys, exceptions, os, stat, tempfile, time, binascii import sys, exceptions, os, stat, tempfile, time, binascii
if sys.platform == "win32":
from ctypes import WINFUNCTYPE, WinError, windll, POINTER, byref, c_ulonglong, \
create_unicode_buffer, get_last_error
from ctypes.wintypes import BOOL, DWORD, LPCWSTR, LPWSTR
from twisted.python import log from twisted.python import log
from pycryptopp.cipher.aes import AES from pycryptopp.cipher.aes import AES
@ -335,16 +340,11 @@ def to_windows_long_path(path):
have_GetDiskFreeSpaceExW = False have_GetDiskFreeSpaceExW = False
if sys.platform == "win32": if sys.platform == "win32":
from ctypes import WINFUNCTYPE, windll, POINTER, byref, c_ulonglong, create_unicode_buffer, \
get_last_error
from ctypes.wintypes import BOOL, DWORD, LPCWSTR, LPWSTR
# <http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188%28v=vs.85%29.aspx> # <http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188%28v=vs.85%29.aspx>
GetEnvironmentVariableW = WINFUNCTYPE( GetEnvironmentVariableW = WINFUNCTYPE(
DWORD, DWORD, LPCWSTR, LPWSTR, DWORD,
LPCWSTR, LPWSTR, DWORD,
use_last_error=True use_last_error=True
)(("GetEnvironmentVariableW", windll.kernel32)) )(("GetEnvironmentVariableW", windll.kernel32))
try: try:
# <http://msdn.microsoft.com/en-us/library/aa383742%28v=VS.85%29.aspx> # <http://msdn.microsoft.com/en-us/library/aa383742%28v=VS.85%29.aspx>
@ -352,10 +352,9 @@ if sys.platform == "win32":
# <http://msdn.microsoft.com/en-us/library/aa364937%28VS.85%29.aspx> # <http://msdn.microsoft.com/en-us/library/aa364937%28VS.85%29.aspx>
GetDiskFreeSpaceExW = WINFUNCTYPE( GetDiskFreeSpaceExW = WINFUNCTYPE(
BOOL, BOOL, LPCWSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER,
LPCWSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER,
use_last_error=True use_last_error=True
)(("GetDiskFreeSpaceExW", windll.kernel32)) )(("GetDiskFreeSpaceExW", windll.kernel32))
have_GetDiskFreeSpaceExW = True have_GetDiskFreeSpaceExW = True
except Exception: except Exception:
@ -403,8 +402,8 @@ def windows_getenv(name):
err = get_last_error() err = get_last_error()
if err == ERROR_ENVVAR_NOT_FOUND: if err == ERROR_ENVVAR_NOT_FOUND:
return None return None
raise OSError("Windows error %d attempting to read size of environment variable %r" raise OSError("WinError: %s\n attempting to read size of environment variable %r"
% (err, name)) % (WinError(err), name))
if n == 1: if n == 1:
# Avoid an ambiguity between a zero-length string and an error in the return value of the # Avoid an ambiguity between a zero-length string and an error in the return value of the
# call to GetEnvironmentVariableW below. # call to GetEnvironmentVariableW below.
@ -416,8 +415,8 @@ def windows_getenv(name):
err = get_last_error() err = get_last_error()
if err == ERROR_ENVVAR_NOT_FOUND: if err == ERROR_ENVVAR_NOT_FOUND:
return None return None
raise OSError("Windows error %d attempting to read environment variable %r" raise OSError("WinError: %s\n attempting to read environment variable %r"
% (err, name)) % (WinError(err), name))
if retval >= n: if retval >= n:
raise OSError("Unexpected result %d (expected less than %d) from GetEnvironmentVariableW attempting to read environment variable %r" raise OSError("Unexpected result %d (expected less than %d) from GetEnvironmentVariableW attempting to read environment variable %r"
% (retval, n, name)) % (retval, n, name))
@ -459,8 +458,8 @@ def get_disk_stats(whichdir, reserved_space=0):
byref(n_total), byref(n_total),
byref(n_free_for_root)) byref(n_free_for_root))
if retval == 0: if retval == 0:
raise OSError("Windows error %d attempting to get disk statistics for %r" raise OSError("WinError: %s\n attempting to get disk statistics for %r"
% (get_last_error(), whichdir)) % (WinError(get_last_error()), whichdir))
free_for_nonroot = n_free_for_nonroot.value free_for_nonroot = n_free_for_nonroot.value
total = n_total.value total = n_total.value
free_for_root = n_free_for_root.value free_for_root = n_free_for_root.value

View File

@ -9,13 +9,18 @@ def initialize():
done = True done = True
import codecs, re import codecs, re
from ctypes import WINFUNCTYPE, windll, POINTER, byref, c_int from ctypes import WINFUNCTYPE, WinError, windll, POINTER, byref, c_int, get_last_error
from ctypes.wintypes import BOOL, HANDLE, DWORD, UINT, LPWSTR, LPCWSTR, LPVOID from ctypes.wintypes import BOOL, HANDLE, DWORD, UINT, LPWSTR, LPCWSTR, LPVOID
from allmydata.util import log from allmydata.util import log
from allmydata.util.encodingutil import canonical_encoding from allmydata.util.encodingutil import canonical_encoding
# <https://msdn.microsoft.com/en-us/library/ms680621%28VS.85%29.aspx> # <https://msdn.microsoft.com/en-us/library/ms680621%28VS.85%29.aspx>
SetErrorMode = WINFUNCTYPE(UINT, UINT)(("SetErrorMode", windll.kernel32)) SetErrorMode = WINFUNCTYPE(
UINT, UINT,
use_last_error=True
)(("SetErrorMode", windll.kernel32))
SEM_FAILCRITICALERRORS = 0x0001 SEM_FAILCRITICALERRORS = 0x0001
SEM_NOOPENFILEERRORBOX = 0x8000 SEM_NOOPENFILEERRORBOX = 0x8000
@ -50,13 +55,27 @@ def initialize():
# <https://msdn.microsoft.com/en-us/library/ms683167(VS.85).aspx> # <https://msdn.microsoft.com/en-us/library/ms683167(VS.85).aspx>
# BOOL WINAPI GetConsoleMode(HANDLE hConsole, LPDWORD lpMode); # BOOL WINAPI GetConsoleMode(HANDLE hConsole, LPDWORD lpMode);
GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(("GetStdHandle", windll.kernel32)) GetStdHandle = WINFUNCTYPE(
HANDLE, DWORD,
use_last_error=True
)(("GetStdHandle", windll.kernel32))
STD_OUTPUT_HANDLE = DWORD(-11) STD_OUTPUT_HANDLE = DWORD(-11)
STD_ERROR_HANDLE = DWORD(-12) STD_ERROR_HANDLE = DWORD(-12)
GetFileType = WINFUNCTYPE(DWORD, DWORD)(("GetFileType", windll.kernel32))
GetFileType = WINFUNCTYPE(
DWORD, DWORD,
use_last_error=True
)(("GetFileType", windll.kernel32))
FILE_TYPE_CHAR = 0x0002 FILE_TYPE_CHAR = 0x0002
FILE_TYPE_REMOTE = 0x8000 FILE_TYPE_REMOTE = 0x8000
GetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))(("GetConsoleMode", windll.kernel32))
GetConsoleMode = WINFUNCTYPE(
BOOL, HANDLE, POINTER(DWORD),
use_last_error=True
)(("GetConsoleMode", windll.kernel32))
INVALID_HANDLE_VALUE = DWORD(-1).value INVALID_HANDLE_VALUE = DWORD(-1).value
def not_a_console(handle): def not_a_console(handle):
@ -88,11 +107,14 @@ def initialize():
real_stderr = False real_stderr = False
if real_stdout or real_stderr: if real_stdout or real_stderr:
# <https://msdn.microsoft.com/en-us/library/windows/desktop/ms687401%28v=vs.85%29.aspx>
# BOOL WINAPI WriteConsoleW(HANDLE hOutput, LPWSTR lpBuffer, DWORD nChars, # BOOL WINAPI WriteConsoleW(HANDLE hOutput, LPWSTR lpBuffer, DWORD nChars,
# LPDWORD lpCharsWritten, LPVOID lpReserved); # LPDWORD lpCharsWritten, LPVOID lpReserved);
WriteConsoleW = WINFUNCTYPE(BOOL, HANDLE, LPWSTR, DWORD, POINTER(DWORD), LPVOID) \ WriteConsoleW = WINFUNCTYPE(
(("WriteConsoleW", windll.kernel32)) BOOL, HANDLE, LPWSTR, DWORD, POINTER(DWORD), LPVOID,
use_last_error=True
)(("WriteConsoleW", windll.kernel32))
class UnicodeOutput: class UnicodeOutput:
def __init__(self, hConsole, stream, fileno, name): def __init__(self, hConsole, stream, fileno, name):
@ -139,8 +161,10 @@ def initialize():
# There is a shorter-than-documented limitation on the length of the string # There is a shorter-than-documented limitation on the length of the string
# passed to WriteConsoleW (see #1232). # passed to WriteConsoleW (see #1232).
retval = WriteConsoleW(self._hConsole, text, min(remaining, 10000), byref(n), None) retval = WriteConsoleW(self._hConsole, text, min(remaining, 10000), byref(n), None)
if retval == 0 or n.value == 0: if retval == 0:
raise IOError("WriteConsoleW returned %r, n.value = %r" % (retval, n.value)) raise IOError("WriteConsoleW failed with WinError: %s" % (WinError(get_last_error()),))
if n.value == 0:
raise IOError("WriteConsoleW returned %r, n.value = 0" % (retval,))
remaining -= n.value remaining -= n.value
if remaining == 0: break if remaining == 0: break
text = text[n.value:] text = text[n.value:]
@ -169,12 +193,23 @@ def initialize():
_complain("exception %r while fixing up sys.stdout and sys.stderr" % (e,)) _complain("exception %r while fixing up sys.stdout and sys.stderr" % (e,))
# This works around <http://bugs.python.org/issue2128>. # This works around <http://bugs.python.org/issue2128>.
GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32))
CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int)) \ # <https://msdn.microsoft.com/en-us/library/windows/desktop/ms683156%28v=vs.85%29.aspx>
(("CommandLineToArgvW", windll.shell32)) GetCommandLineW = WINFUNCTYPE(
LPWSTR,
use_last_error=True
)(("GetCommandLineW", windll.kernel32))
# <https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391%28v=vs.85%29.aspx>
CommandLineToArgvW = WINFUNCTYPE(
POINTER(LPWSTR), LPCWSTR, POINTER(c_int),
use_last_error=True
)(("CommandLineToArgvW", windll.shell32))
argc = c_int(0) argc = c_int(0)
argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc)) argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc))
if argv_unicode is None:
raise WinError(get_last_error())
# Because of <http://bugs.python.org/issue8775> (and similar limitations in # Because of <http://bugs.python.org/issue8775> (and similar limitations in
# twisted), the 'bin/tahoe' script cannot invoke us with the actual Unicode arguments. # twisted), the 'bin/tahoe' script cannot invoke us with the actual Unicode arguments.