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