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