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