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