##// END OF EJS Templates
win32: specify _CREATE_NO_WINDOW on spawndetached()...
Adrian Buehlmann -
r17050:e780fb37 default
parent child Browse files
Show More
@@ -1,396 +1,396 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 _DETACHED_PROCESS = 0x00000008
100 _CREATE_NO_WINDOW = 0x08000000
101 101 _STARTF_USESHOWWINDOW = 0x00000001
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 # types of parameters of C functions used (required by pypy)
124 124
125 125 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
126 126 _DWORD, _DWORD, _HANDLE]
127 127 _kernel32.CreateFileA.restype = _HANDLE
128 128
129 129 _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
130 130 _kernel32.GetFileInformationByHandle.restype = _BOOL
131 131
132 132 _kernel32.CloseHandle.argtypes = [_HANDLE]
133 133 _kernel32.CloseHandle.restype = _BOOL
134 134
135 135 try:
136 136 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
137 137 _kernel32.CreateHardLinkA.restype = _BOOL
138 138 except AttributeError:
139 139 pass
140 140
141 141 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
142 142 _kernel32.SetFileAttributesA.restype = _BOOL
143 143
144 144 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
145 145 _kernel32.OpenProcess.restype = _HANDLE
146 146
147 147 _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
148 148 _kernel32.GetExitCodeProcess.restype = _BOOL
149 149
150 150 _kernel32.GetLastError.argtypes = []
151 151 _kernel32.GetLastError.restype = _DWORD
152 152
153 153 _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
154 154 _kernel32.GetModuleFileNameA.restype = _DWORD
155 155
156 156 _kernel32.CreateProcessA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p,
157 157 ctypes.c_void_p, _BOOL, _DWORD, ctypes.c_void_p, _LPCSTR, ctypes.c_void_p,
158 158 ctypes.c_void_p]
159 159 _kernel32.CreateProcessA.restype = _BOOL
160 160
161 161 _kernel32.ExitProcess.argtypes = [_UINT]
162 162 _kernel32.ExitProcess.restype = None
163 163
164 164 _kernel32.GetCurrentProcessId.argtypes = []
165 165 _kernel32.GetCurrentProcessId.restype = _DWORD
166 166
167 167 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
168 168 _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
169 169 _kernel32.SetConsoleCtrlHandler.restype = _BOOL
170 170
171 171 _kernel32.GetStdHandle.argtypes = [_DWORD]
172 172 _kernel32.GetStdHandle.restype = _HANDLE
173 173
174 174 _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
175 175 _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
176 176
177 177 _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
178 178 _advapi32.GetUserNameA.restype = _BOOL
179 179
180 180 _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
181 181 _user32.GetWindowThreadProcessId.restype = _DWORD
182 182
183 183 _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
184 184 _user32.ShowWindow.restype = _BOOL
185 185
186 186 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
187 187 _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
188 188 _user32.EnumWindows.restype = _BOOL
189 189
190 190 def _raiseoserror(name):
191 191 err = ctypes.WinError()
192 192 raise OSError(err.errno, '%s: %s' % (name, err.strerror))
193 193
194 194 def _getfileinfo(name):
195 195 fh = _kernel32.CreateFileA(name, 0,
196 196 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
197 197 None, _OPEN_EXISTING, _FILE_FLAG_BACKUP_SEMANTICS, None)
198 198 if fh == _INVALID_HANDLE_VALUE:
199 199 _raiseoserror(name)
200 200 try:
201 201 fi = _BY_HANDLE_FILE_INFORMATION()
202 202 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
203 203 _raiseoserror(name)
204 204 return fi
205 205 finally:
206 206 _kernel32.CloseHandle(fh)
207 207
208 208 def oslink(src, dst):
209 209 try:
210 210 if not _kernel32.CreateHardLinkA(dst, src, None):
211 211 _raiseoserror(src)
212 212 except AttributeError: # Wine doesn't support this function
213 213 _raiseoserror(src)
214 214
215 215 def nlinks(name):
216 216 '''return number of hardlinks for the given file'''
217 217 return _getfileinfo(name).nNumberOfLinks
218 218
219 219 def samefile(path1, path2):
220 220 '''Returns whether path1 and path2 refer to the same file or directory.'''
221 221 res1 = _getfileinfo(path1)
222 222 res2 = _getfileinfo(path2)
223 223 return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
224 224 and res1.nFileIndexHigh == res2.nFileIndexHigh
225 225 and res1.nFileIndexLow == res2.nFileIndexLow)
226 226
227 227 def samedevice(path1, path2):
228 228 '''Returns whether path1 and path2 are on the same device.'''
229 229 res1 = _getfileinfo(path1)
230 230 res2 = _getfileinfo(path2)
231 231 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
232 232
233 233 def testpid(pid):
234 234 '''return True if pid is still running or unable to
235 235 determine, False otherwise'''
236 236 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
237 237 if h:
238 238 try:
239 239 status = _DWORD()
240 240 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
241 241 return status.value == _STILL_ACTIVE
242 242 finally:
243 243 _kernel32.CloseHandle(h)
244 244 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
245 245
246 246 def executablepath():
247 247 '''return full path of hg.exe'''
248 248 size = 600
249 249 buf = ctypes.create_string_buffer(size + 1)
250 250 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
251 251 if len == 0:
252 252 raise ctypes.WinError
253 253 elif len == size:
254 254 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
255 255 return buf.value
256 256
257 257 def getuser():
258 258 '''return name of current user'''
259 259 size = _DWORD(300)
260 260 buf = ctypes.create_string_buffer(size.value + 1)
261 261 if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
262 262 raise ctypes.WinError
263 263 return buf.value
264 264
265 265 _signalhandler = []
266 266
267 267 def setsignalhandler():
268 268 '''Register a termination handler for console events including
269 269 CTRL+C. python signal handlers do not work well with socket
270 270 operations.
271 271 '''
272 272 def handler(event):
273 273 _kernel32.ExitProcess(1)
274 274
275 275 if _signalhandler:
276 276 return # already registered
277 277 h = _SIGNAL_HANDLER(handler)
278 278 _signalhandler.append(h) # needed to prevent garbage collection
279 279 if not _kernel32.SetConsoleCtrlHandler(h, True):
280 280 raise ctypes.WinError
281 281
282 282 def hidewindow():
283 283
284 284 def callback(hwnd, pid):
285 285 wpid = _DWORD()
286 286 _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
287 287 if pid == wpid.value:
288 288 _user32.ShowWindow(hwnd, _SW_HIDE)
289 289 return False # stop enumerating windows
290 290 return True
291 291
292 292 pid = _kernel32.GetCurrentProcessId()
293 293 _user32.EnumWindows(_WNDENUMPROC(callback), pid)
294 294
295 295 def termwidth():
296 296 # cmd.exe does not handle CR like a unix console, the CR is
297 297 # counted in the line length. On 80 columns consoles, if 80
298 298 # characters are written, the following CR won't apply on the
299 299 # current line but on the new one. Keep room for it.
300 300 width = 79
301 301 # Query stderr to avoid problems with redirections
302 302 screenbuf = _kernel32.GetStdHandle(
303 303 _STD_ERROR_HANDLE) # don't close the handle returned
304 304 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
305 305 return width
306 306 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
307 307 if not _kernel32.GetConsoleScreenBufferInfo(
308 308 screenbuf, ctypes.byref(csbi)):
309 309 return width
310 310 width = csbi.srWindow.Right - csbi.srWindow.Left
311 311 return width
312 312
313 313 def spawndetached(args):
314 314 # No standard library function really spawns a fully detached
315 315 # process under win32 because they allocate pipes or other objects
316 316 # to handle standard streams communications. Passing these objects
317 317 # to the child process requires handle inheritance to be enabled
318 318 # which makes really detached processes impossible.
319 319 si = _STARTUPINFO()
320 320 si.cb = ctypes.sizeof(_STARTUPINFO)
321 321 si.dwFlags = _STARTF_USESHOWWINDOW
322 322 si.wShowWindow = _SW_HIDE
323 323
324 324 pi = _PROCESS_INFORMATION()
325 325
326 326 env = ''
327 327 for k in os.environ:
328 328 env += "%s=%s\0" % (k, os.environ[k])
329 329 if not env:
330 330 env = '\0'
331 331 env += '\0'
332 332
333 333 args = subprocess.list2cmdline(args)
334 334 # Not running the command in shell mode makes python26 hang when
335 335 # writing to hgweb output socket.
336 336 comspec = os.environ.get("COMSPEC", "cmd.exe")
337 337 args = comspec + " /c " + args
338 338
339 339 res = _kernel32.CreateProcessA(
340 None, args, None, None, False, _DETACHED_PROCESS,
340 None, args, None, None, False, _CREATE_NO_WINDOW,
341 341 env, os.getcwd(), ctypes.byref(si), ctypes.byref(pi))
342 342 if not res:
343 343 raise ctypes.WinError
344 344
345 345 return pi.dwProcessId
346 346
347 347 def unlink(f):
348 348 '''try to implement POSIX' unlink semantics on Windows'''
349 349
350 350 # POSIX allows to unlink and rename open files. Windows has serious
351 351 # problems with doing that:
352 352 # - Calling os.unlink (or os.rename) on a file f fails if f or any
353 353 # hardlinked copy of f has been opened with Python's open(). There is no
354 354 # way such a file can be deleted or renamed on Windows (other than
355 355 # scheduling the delete or rename for the next reboot).
356 356 # - Calling os.unlink on a file that has been opened with Mercurial's
357 357 # posixfile (or comparable methods) will delay the actual deletion of
358 358 # the file for as long as the file is held open. The filename is blocked
359 359 # during that time and cannot be used for recreating a new file under
360 360 # that same name ("zombie file"). Directories containing such zombie files
361 361 # cannot be removed or moved.
362 362 # A file that has been opened with posixfile can be renamed, so we rename
363 363 # f to a random temporary name before calling os.unlink on it. This allows
364 364 # callers to recreate f immediately while having other readers do their
365 365 # implicit zombie filename blocking on a temporary name.
366 366
367 367 for tries in xrange(10):
368 368 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
369 369 try:
370 370 os.rename(f, temp) # raises OSError EEXIST if temp exists
371 371 break
372 372 except OSError, e:
373 373 if e.errno != errno.EEXIST:
374 374 raise
375 375 else:
376 376 raise IOError, (errno.EEXIST, "No usable temporary filename found")
377 377
378 378 try:
379 379 os.unlink(temp)
380 380 except OSError:
381 381 # The unlink might have failed because the READONLY attribute may heave
382 382 # been set on the original file. Rename works fine with READONLY set,
383 383 # but not os.unlink. Reset all attributes and try again.
384 384 _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
385 385 try:
386 386 os.unlink(temp)
387 387 except OSError:
388 388 # The unlink might have failed due to some very rude AV-Scanners.
389 389 # Leaking a tempfile is the lesser evil than aborting here and
390 390 # leaving some potentially serious inconsistencies.
391 391 pass
392 392
393 393 def makedir(path, notindexed):
394 394 os.mkdir(path)
395 395 if notindexed:
396 396 _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
General Comments 0
You need to be logged in to leave comments. Login now