win32.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. # -*- test-case-name: twisted.python.test.test_win32 -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Win32 utilities.
  6. See also twisted.python.shortcut.
  7. @var O_BINARY: the 'binary' mode flag on Windows, or 0 on other platforms, so it
  8. may safely be OR'ed into a mask for os.open.
  9. """
  10. from __future__ import division, absolute_import
  11. import re
  12. import os
  13. try:
  14. import win32api
  15. import win32con
  16. except ImportError:
  17. pass
  18. from twisted.python.deprecate import deprecated
  19. from twisted.python.runtime import platform
  20. from incremental import Version
  21. # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/system_error_codes.asp
  22. ERROR_FILE_NOT_FOUND = 2
  23. ERROR_PATH_NOT_FOUND = 3
  24. ERROR_INVALID_NAME = 123
  25. ERROR_DIRECTORY = 267
  26. O_BINARY = getattr(os, "O_BINARY", 0)
  27. class FakeWindowsError(OSError):
  28. """
  29. Stand-in for sometimes-builtin exception on platforms for which it
  30. is missing.
  31. """
  32. try:
  33. WindowsError = WindowsError
  34. except NameError:
  35. WindowsError = FakeWindowsError
  36. @deprecated(Version("Twisted", 15, 3, 0))
  37. def getProgramsMenuPath():
  38. """
  39. Get the path to the Programs menu.
  40. Probably will break on non-US Windows.
  41. @return: the filesystem location of the common Start Menu->Programs.
  42. @rtype: L{str}
  43. """
  44. if not platform.isWindows():
  45. return "C:\\Windows\\Start Menu\\Programs"
  46. keyname = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders'
  47. hShellFolders = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE,
  48. keyname, 0, win32con.KEY_READ)
  49. return win32api.RegQueryValueEx(hShellFolders, 'Common Programs')[0]
  50. @deprecated(Version("Twisted", 15, 3, 0))
  51. def getProgramFilesPath():
  52. """Get the path to the Program Files folder."""
  53. keyname = 'SOFTWARE\\Microsoft\\Windows\\CurrentVersion'
  54. currentV = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE,
  55. keyname, 0, win32con.KEY_READ)
  56. return win32api.RegQueryValueEx(currentV, 'ProgramFilesDir')[0]
  57. _cmdLineQuoteRe = re.compile(r'(\\*)"')
  58. _cmdLineQuoteRe2 = re.compile(r'(\\+)\Z')
  59. def cmdLineQuote(s):
  60. """
  61. Internal method for quoting a single command-line argument.
  62. @param s: an unquoted string that you want to quote so that something that
  63. does cmd.exe-style unquoting will interpret it as a single argument,
  64. even if it contains spaces.
  65. @type s: C{str}
  66. @return: a quoted string.
  67. @rtype: C{str}
  68. """
  69. quote = ((" " in s) or ("\t" in s) or ('"' in s) or s == '') and '"' or ''
  70. return quote + _cmdLineQuoteRe2.sub(r"\1\1", _cmdLineQuoteRe.sub(r'\1\1\\"', s)) + quote
  71. def quoteArguments(arguments):
  72. """
  73. Quote an iterable of command-line arguments for passing to CreateProcess or
  74. a similar API. This allows the list passed to C{reactor.spawnProcess} to
  75. match the child process's C{sys.argv} properly.
  76. @param arglist: an iterable of C{str}, each unquoted.
  77. @return: a single string, with the given sequence quoted as necessary.
  78. """
  79. return ' '.join([cmdLineQuote(a) for a in arguments])
  80. class _ErrorFormatter(object):
  81. """
  82. Formatter for Windows error messages.
  83. @ivar winError: A callable which takes one integer error number argument
  84. and returns an L{exceptions.WindowsError} instance for that error (like
  85. L{ctypes.WinError}).
  86. @ivar formatMessage: A callable which takes one integer error number
  87. argument and returns a C{str} giving the message for that error (like
  88. L{win32api.FormatMessage}).
  89. @ivar errorTab: A mapping from integer error numbers to C{str} messages
  90. which correspond to those erorrs (like I{socket.errorTab}).
  91. """
  92. def __init__(self, WinError, FormatMessage, errorTab):
  93. self.winError = WinError
  94. self.formatMessage = FormatMessage
  95. self.errorTab = errorTab
  96. def fromEnvironment(cls):
  97. """
  98. Get as many of the platform-specific error translation objects as
  99. possible and return an instance of C{cls} created with them.
  100. """
  101. try:
  102. from ctypes import WinError
  103. except ImportError:
  104. WinError = None
  105. try:
  106. from win32api import FormatMessage
  107. except ImportError:
  108. FormatMessage = None
  109. try:
  110. from socket import errorTab
  111. except ImportError:
  112. errorTab = None
  113. return cls(WinError, FormatMessage, errorTab)
  114. fromEnvironment = classmethod(fromEnvironment)
  115. def formatError(self, errorcode):
  116. """
  117. Returns the string associated with a Windows error message, such as the
  118. ones found in socket.error.
  119. Attempts direct lookup against the win32 API via ctypes and then
  120. pywin32 if available), then in the error table in the socket module,
  121. then finally defaulting to C{os.strerror}.
  122. @param errorcode: the Windows error code
  123. @type errorcode: C{int}
  124. @return: The error message string
  125. @rtype: C{str}
  126. """
  127. if self.winError is not None:
  128. return self.winError(errorcode).strerror
  129. if self.formatMessage is not None:
  130. return self.formatMessage(errorcode)
  131. if self.errorTab is not None:
  132. result = self.errorTab.get(errorcode)
  133. if result is not None:
  134. return result
  135. return os.strerror(errorcode)
  136. formatError = _ErrorFormatter.fromEnvironment().formatError