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