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