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