PageRenderTime 42ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/mercurial/win32.py

https://bitbucket.org/mirror/mercurial/
Python | 476 lines | 367 code | 63 blank | 46 comment | 29 complexity | 99f05a80b3d85e074c8a139156c52e2e MD5 | raw file
Possible License(s): GPL-2.0
  1. # win32.py - utility functions that use win32 API
  2. #
  3. # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
  4. #
  5. # This software may be used and distributed according to the terms of the
  6. # GNU General Public License version 2 or any later version.
  7. import ctypes, errno, os, subprocess, random
  8. _kernel32 = ctypes.windll.kernel32
  9. _advapi32 = ctypes.windll.advapi32
  10. _user32 = ctypes.windll.user32
  11. _BOOL = ctypes.c_long
  12. _WORD = ctypes.c_ushort
  13. _DWORD = ctypes.c_ulong
  14. _UINT = ctypes.c_uint
  15. _LONG = ctypes.c_long
  16. _LPCSTR = _LPSTR = ctypes.c_char_p
  17. _HANDLE = ctypes.c_void_p
  18. _HWND = _HANDLE
  19. _INVALID_HANDLE_VALUE = _HANDLE(-1).value
  20. # GetLastError
  21. _ERROR_SUCCESS = 0
  22. _ERROR_NO_MORE_FILES = 18
  23. _ERROR_INVALID_PARAMETER = 87
  24. _ERROR_INSUFFICIENT_BUFFER = 122
  25. # WPARAM is defined as UINT_PTR (unsigned type)
  26. # LPARAM is defined as LONG_PTR (signed type)
  27. if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
  28. _WPARAM = ctypes.c_ulong
  29. _LPARAM = ctypes.c_long
  30. elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
  31. _WPARAM = ctypes.c_ulonglong
  32. _LPARAM = ctypes.c_longlong
  33. class _FILETIME(ctypes.Structure):
  34. _fields_ = [('dwLowDateTime', _DWORD),
  35. ('dwHighDateTime', _DWORD)]
  36. class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
  37. _fields_ = [('dwFileAttributes', _DWORD),
  38. ('ftCreationTime', _FILETIME),
  39. ('ftLastAccessTime', _FILETIME),
  40. ('ftLastWriteTime', _FILETIME),
  41. ('dwVolumeSerialNumber', _DWORD),
  42. ('nFileSizeHigh', _DWORD),
  43. ('nFileSizeLow', _DWORD),
  44. ('nNumberOfLinks', _DWORD),
  45. ('nFileIndexHigh', _DWORD),
  46. ('nFileIndexLow', _DWORD)]
  47. # CreateFile
  48. _FILE_SHARE_READ = 0x00000001
  49. _FILE_SHARE_WRITE = 0x00000002
  50. _FILE_SHARE_DELETE = 0x00000004
  51. _OPEN_EXISTING = 3
  52. _FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
  53. # SetFileAttributes
  54. _FILE_ATTRIBUTE_NORMAL = 0x80
  55. _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
  56. # Process Security and Access Rights
  57. _PROCESS_QUERY_INFORMATION = 0x0400
  58. # GetExitCodeProcess
  59. _STILL_ACTIVE = 259
  60. class _STARTUPINFO(ctypes.Structure):
  61. _fields_ = [('cb', _DWORD),
  62. ('lpReserved', _LPSTR),
  63. ('lpDesktop', _LPSTR),
  64. ('lpTitle', _LPSTR),
  65. ('dwX', _DWORD),
  66. ('dwY', _DWORD),
  67. ('dwXSize', _DWORD),
  68. ('dwYSize', _DWORD),
  69. ('dwXCountChars', _DWORD),
  70. ('dwYCountChars', _DWORD),
  71. ('dwFillAttribute', _DWORD),
  72. ('dwFlags', _DWORD),
  73. ('wShowWindow', _WORD),
  74. ('cbReserved2', _WORD),
  75. ('lpReserved2', ctypes.c_char_p),
  76. ('hStdInput', _HANDLE),
  77. ('hStdOutput', _HANDLE),
  78. ('hStdError', _HANDLE)]
  79. class _PROCESS_INFORMATION(ctypes.Structure):
  80. _fields_ = [('hProcess', _HANDLE),
  81. ('hThread', _HANDLE),
  82. ('dwProcessId', _DWORD),
  83. ('dwThreadId', _DWORD)]
  84. _CREATE_NO_WINDOW = 0x08000000
  85. _SW_HIDE = 0
  86. class _COORD(ctypes.Structure):
  87. _fields_ = [('X', ctypes.c_short),
  88. ('Y', ctypes.c_short)]
  89. class _SMALL_RECT(ctypes.Structure):
  90. _fields_ = [('Left', ctypes.c_short),
  91. ('Top', ctypes.c_short),
  92. ('Right', ctypes.c_short),
  93. ('Bottom', ctypes.c_short)]
  94. class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
  95. _fields_ = [('dwSize', _COORD),
  96. ('dwCursorPosition', _COORD),
  97. ('wAttributes', _WORD),
  98. ('srWindow', _SMALL_RECT),
  99. ('dwMaximumWindowSize', _COORD)]
  100. _STD_ERROR_HANDLE = _DWORD(-12).value
  101. # CreateToolhelp32Snapshot, Process32First, Process32Next
  102. _TH32CS_SNAPPROCESS = 0x00000002
  103. _MAX_PATH = 260
  104. class _tagPROCESSENTRY32(ctypes.Structure):
  105. _fields_ = [('dwsize', _DWORD),
  106. ('cntUsage', _DWORD),
  107. ('th32ProcessID', _DWORD),
  108. ('th32DefaultHeapID', ctypes.c_void_p),
  109. ('th32ModuleID', _DWORD),
  110. ('cntThreads', _DWORD),
  111. ('th32ParentProcessID', _DWORD),
  112. ('pcPriClassBase', _LONG),
  113. ('dwFlags', _DWORD),
  114. ('szExeFile', ctypes.c_char * _MAX_PATH)]
  115. def __init__(self):
  116. super(_tagPROCESSENTRY32, self).__init__()
  117. self.dwsize = ctypes.sizeof(self)
  118. # types of parameters of C functions used (required by pypy)
  119. _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
  120. _DWORD, _DWORD, _HANDLE]
  121. _kernel32.CreateFileA.restype = _HANDLE
  122. _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
  123. _kernel32.GetFileInformationByHandle.restype = _BOOL
  124. _kernel32.CloseHandle.argtypes = [_HANDLE]
  125. _kernel32.CloseHandle.restype = _BOOL
  126. try:
  127. _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
  128. _kernel32.CreateHardLinkA.restype = _BOOL
  129. except AttributeError:
  130. pass
  131. _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
  132. _kernel32.SetFileAttributesA.restype = _BOOL
  133. _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
  134. _kernel32.OpenProcess.restype = _HANDLE
  135. _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
  136. _kernel32.GetExitCodeProcess.restype = _BOOL
  137. _kernel32.GetLastError.argtypes = []
  138. _kernel32.GetLastError.restype = _DWORD
  139. _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
  140. _kernel32.GetModuleFileNameA.restype = _DWORD
  141. _kernel32.CreateProcessA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p,
  142. ctypes.c_void_p, _BOOL, _DWORD, ctypes.c_void_p, _LPCSTR, ctypes.c_void_p,
  143. ctypes.c_void_p]
  144. _kernel32.CreateProcessA.restype = _BOOL
  145. _kernel32.ExitProcess.argtypes = [_UINT]
  146. _kernel32.ExitProcess.restype = None
  147. _kernel32.GetCurrentProcessId.argtypes = []
  148. _kernel32.GetCurrentProcessId.restype = _DWORD
  149. _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
  150. _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
  151. _kernel32.SetConsoleCtrlHandler.restype = _BOOL
  152. _kernel32.GetStdHandle.argtypes = [_DWORD]
  153. _kernel32.GetStdHandle.restype = _HANDLE
  154. _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
  155. _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
  156. _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
  157. _advapi32.GetUserNameA.restype = _BOOL
  158. _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
  159. _user32.GetWindowThreadProcessId.restype = _DWORD
  160. _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
  161. _user32.ShowWindow.restype = _BOOL
  162. _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
  163. _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
  164. _user32.EnumWindows.restype = _BOOL
  165. _kernel32.CreateToolhelp32Snapshot.argtypes = [_DWORD, _DWORD]
  166. _kernel32.CreateToolhelp32Snapshot.restype = _BOOL
  167. _kernel32.Process32First.argtypes = [_HANDLE, ctypes.c_void_p]
  168. _kernel32.Process32First.restype = _BOOL
  169. _kernel32.Process32Next.argtypes = [_HANDLE, ctypes.c_void_p]
  170. _kernel32.Process32Next.restype = _BOOL
  171. def _raiseoserror(name):
  172. err = ctypes.WinError()
  173. raise OSError(err.errno, '%s: %s' % (name, err.strerror))
  174. def _getfileinfo(name):
  175. fh = _kernel32.CreateFileA(name, 0,
  176. _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
  177. None, _OPEN_EXISTING, _FILE_FLAG_BACKUP_SEMANTICS, None)
  178. if fh == _INVALID_HANDLE_VALUE:
  179. _raiseoserror(name)
  180. try:
  181. fi = _BY_HANDLE_FILE_INFORMATION()
  182. if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
  183. _raiseoserror(name)
  184. return fi
  185. finally:
  186. _kernel32.CloseHandle(fh)
  187. def oslink(src, dst):
  188. try:
  189. if not _kernel32.CreateHardLinkA(dst, src, None):
  190. _raiseoserror(src)
  191. except AttributeError: # Wine doesn't support this function
  192. _raiseoserror(src)
  193. def nlinks(name):
  194. '''return number of hardlinks for the given file'''
  195. return _getfileinfo(name).nNumberOfLinks
  196. def samefile(path1, path2):
  197. '''Returns whether path1 and path2 refer to the same file or directory.'''
  198. res1 = _getfileinfo(path1)
  199. res2 = _getfileinfo(path2)
  200. return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
  201. and res1.nFileIndexHigh == res2.nFileIndexHigh
  202. and res1.nFileIndexLow == res2.nFileIndexLow)
  203. def samedevice(path1, path2):
  204. '''Returns whether path1 and path2 are on the same device.'''
  205. res1 = _getfileinfo(path1)
  206. res2 = _getfileinfo(path2)
  207. return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
  208. def testpid(pid):
  209. '''return True if pid is still running or unable to
  210. determine, False otherwise'''
  211. h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
  212. if h:
  213. try:
  214. status = _DWORD()
  215. if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
  216. return status.value == _STILL_ACTIVE
  217. finally:
  218. _kernel32.CloseHandle(h)
  219. return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
  220. def executablepath():
  221. '''return full path of hg.exe'''
  222. size = 600
  223. buf = ctypes.create_string_buffer(size + 1)
  224. len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
  225. if len == 0:
  226. raise ctypes.WinError
  227. elif len == size:
  228. raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
  229. return buf.value
  230. def getuser():
  231. '''return name of current user'''
  232. size = _DWORD(300)
  233. buf = ctypes.create_string_buffer(size.value + 1)
  234. if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
  235. raise ctypes.WinError
  236. return buf.value
  237. _signalhandler = []
  238. def setsignalhandler():
  239. '''Register a termination handler for console events including
  240. CTRL+C. python signal handlers do not work well with socket
  241. operations.
  242. '''
  243. def handler(event):
  244. _kernel32.ExitProcess(1)
  245. if _signalhandler:
  246. return # already registered
  247. h = _SIGNAL_HANDLER(handler)
  248. _signalhandler.append(h) # needed to prevent garbage collection
  249. if not _kernel32.SetConsoleCtrlHandler(h, True):
  250. raise ctypes.WinError
  251. def hidewindow():
  252. def callback(hwnd, pid):
  253. wpid = _DWORD()
  254. _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
  255. if pid == wpid.value:
  256. _user32.ShowWindow(hwnd, _SW_HIDE)
  257. return False # stop enumerating windows
  258. return True
  259. pid = _kernel32.GetCurrentProcessId()
  260. _user32.EnumWindows(_WNDENUMPROC(callback), pid)
  261. def termwidth():
  262. # cmd.exe does not handle CR like a unix console, the CR is
  263. # counted in the line length. On 80 columns consoles, if 80
  264. # characters are written, the following CR won't apply on the
  265. # current line but on the new one. Keep room for it.
  266. width = 79
  267. # Query stderr to avoid problems with redirections
  268. screenbuf = _kernel32.GetStdHandle(
  269. _STD_ERROR_HANDLE) # don't close the handle returned
  270. if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
  271. return width
  272. csbi = _CONSOLE_SCREEN_BUFFER_INFO()
  273. if not _kernel32.GetConsoleScreenBufferInfo(
  274. screenbuf, ctypes.byref(csbi)):
  275. return width
  276. width = csbi.srWindow.Right - csbi.srWindow.Left
  277. return width
  278. def _1stchild(pid):
  279. '''return the 1st found child of the given pid
  280. None is returned when no child is found'''
  281. pe = _tagPROCESSENTRY32()
  282. # create handle to list all processes
  283. ph = _kernel32.CreateToolhelp32Snapshot(_TH32CS_SNAPPROCESS, 0)
  284. if ph == _INVALID_HANDLE_VALUE:
  285. raise ctypes.WinError
  286. try:
  287. r = _kernel32.Process32First(ph, ctypes.byref(pe))
  288. # loop over all processes
  289. while r:
  290. if pe.th32ParentProcessID == pid:
  291. # return first child found
  292. return pe.th32ProcessID
  293. r = _kernel32.Process32Next(ph, ctypes.byref(pe))
  294. finally:
  295. _kernel32.CloseHandle(ph)
  296. if _kernel32.GetLastError() != _ERROR_NO_MORE_FILES:
  297. raise ctypes.WinError
  298. return None # no child found
  299. class _tochildpid(int): # pid is _DWORD, which always matches in an int
  300. '''helper for spawndetached, returns the child pid on conversion to string
  301. Does not resolve the child pid immediately because the child may not yet be
  302. started.
  303. '''
  304. def childpid(self):
  305. '''returns the child pid of the first found child of the process
  306. with this pid'''
  307. return _1stchild(self)
  308. def __str__(self):
  309. # run when the pid is written to the file
  310. ppid = self.childpid()
  311. if ppid is None:
  312. # race, child has exited since check
  313. # fall back to this pid. Its process will also have disappeared,
  314. # raising the same error type later as when the child pid would
  315. # be returned.
  316. return " %d" % self
  317. return str(ppid)
  318. def spawndetached(args):
  319. # No standard library function really spawns a fully detached
  320. # process under win32 because they allocate pipes or other objects
  321. # to handle standard streams communications. Passing these objects
  322. # to the child process requires handle inheritance to be enabled
  323. # which makes really detached processes impossible.
  324. si = _STARTUPINFO()
  325. si.cb = ctypes.sizeof(_STARTUPINFO)
  326. pi = _PROCESS_INFORMATION()
  327. env = ''
  328. for k in os.environ:
  329. env += "%s=%s\0" % (k, os.environ[k])
  330. if not env:
  331. env = '\0'
  332. env += '\0'
  333. args = subprocess.list2cmdline(args)
  334. # Not running the command in shell mode makes Python 2.6 hang when
  335. # writing to hgweb output socket.
  336. comspec = os.environ.get("COMSPEC", "cmd.exe")
  337. args = comspec + " /c " + args
  338. res = _kernel32.CreateProcessA(
  339. None, args, None, None, False, _CREATE_NO_WINDOW,
  340. env, os.getcwd(), ctypes.byref(si), ctypes.byref(pi))
  341. if not res:
  342. raise ctypes.WinError
  343. # _tochildpid because the process is the child of COMSPEC
  344. return _tochildpid(pi.dwProcessId)
  345. def unlink(f):
  346. '''try to implement POSIX' unlink semantics on Windows'''
  347. if os.path.isdir(f):
  348. # use EPERM because it is POSIX prescribed value, even though
  349. # unlink(2) on directories returns EISDIR on Linux
  350. raise IOError(errno.EPERM,
  351. "Unlinking directory not permitted: '%s'" % f)
  352. # POSIX allows to unlink and rename open files. Windows has serious
  353. # problems with doing that:
  354. # - Calling os.unlink (or os.rename) on a file f fails if f or any
  355. # hardlinked copy of f has been opened with Python's open(). There is no
  356. # way such a file can be deleted or renamed on Windows (other than
  357. # scheduling the delete or rename for the next reboot).
  358. # - Calling os.unlink on a file that has been opened with Mercurial's
  359. # posixfile (or comparable methods) will delay the actual deletion of
  360. # the file for as long as the file is held open. The filename is blocked
  361. # during that time and cannot be used for recreating a new file under
  362. # that same name ("zombie file"). Directories containing such zombie files
  363. # cannot be removed or moved.
  364. # A file that has been opened with posixfile can be renamed, so we rename
  365. # f to a random temporary name before calling os.unlink on it. This allows
  366. # callers to recreate f immediately while having other readers do their
  367. # implicit zombie filename blocking on a temporary name.
  368. for tries in xrange(10):
  369. temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
  370. try:
  371. os.rename(f, temp) # raises OSError EEXIST if temp exists
  372. break
  373. except OSError, e:
  374. if e.errno != errno.EEXIST:
  375. raise
  376. else:
  377. raise IOError(errno.EEXIST, "No usable temporary filename found")
  378. try:
  379. os.unlink(temp)
  380. except OSError:
  381. # The unlink might have failed because the READONLY attribute may heave
  382. # been set on the original file. Rename works fine with READONLY set,
  383. # but not os.unlink. Reset all attributes and try again.
  384. _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
  385. try:
  386. os.unlink(temp)
  387. except OSError:
  388. # The unlink might have failed due to some very rude AV-Scanners.
  389. # Leaking a tempfile is the lesser evil than aborting here and
  390. # leaving some potentially serious inconsistencies.
  391. pass
  392. def makedir(path, notindexed):
  393. os.mkdir(path)
  394. if notindexed:
  395. _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)