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.
8
"""Utility functions that use win32 API.
10
Mark Hammond's win32all package allows better functionality on
11
Windows. This module overrides definitions in util.py. If not
12
available, import of this module will fail, and generic code will be
18
import errno, os, sys, pywintypes, win32con, win32file, win32process
19
import winerror, win32gui, win32console
20
import osutil, encoding
21
from win32com.shell import shell, shellcon
9
import ctypes, errno, os, struct, subprocess
11
_kernel32 = ctypes.windll.kernel32
14
_WORD = ctypes.c_ushort
15
_DWORD = ctypes.c_ulong
16
_LPCSTR = _LPSTR = ctypes.c_char_p
17
_HANDLE = ctypes.c_void_p
20
_INVALID_HANDLE_VALUE = -1
24
_ERROR_INVALID_PARAMETER = 87
25
_ERROR_INSUFFICIENT_BUFFER = 122
27
# WPARAM is defined as UINT_PTR (unsigned type)
28
# LPARAM is defined as LONG_PTR (signed type)
29
if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
30
_WPARAM = ctypes.c_ulong
31
_LPARAM = ctypes.c_long
32
elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
33
_WPARAM = ctypes.c_ulonglong
34
_LPARAM = ctypes.c_longlong
36
class _FILETIME(ctypes.Structure):
37
_fields_ = [('dwLowDateTime', _DWORD),
38
('dwHighDateTime', _DWORD)]
40
class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
41
_fields_ = [('dwFileAttributes', _DWORD),
42
('ftCreationTime', _FILETIME),
43
('ftLastAccessTime', _FILETIME),
44
('ftLastWriteTime', _FILETIME),
45
('dwVolumeSerialNumber', _DWORD),
46
('nFileSizeHigh', _DWORD),
47
('nFileSizeLow', _DWORD),
48
('nNumberOfLinks', _DWORD),
49
('nFileIndexHigh', _DWORD),
50
('nFileIndexLow', _DWORD)]
53
_FILE_SHARE_READ = 0x00000001
54
_FILE_SHARE_WRITE = 0x00000002
55
_FILE_SHARE_DELETE = 0x00000004
59
# Process Security and Access Rights
60
_PROCESS_QUERY_INFORMATION = 0x0400
66
_HKEY_CURRENT_USER = 0x80000001L
67
_HKEY_LOCAL_MACHINE = 0x80000002L
72
class _STARTUPINFO(ctypes.Structure):
73
_fields_ = [('cb', _DWORD),
74
('lpReserved', _LPSTR),
75
('lpDesktop', _LPSTR),
81
('dwXCountChars', _DWORD),
82
('dwYCountChars', _DWORD),
83
('dwFillAttribute', _DWORD),
85
('wShowWindow', _WORD),
86
('cbReserved2', _WORD),
87
('lpReserved2', ctypes.c_char_p),
88
('hStdInput', _HANDLE),
89
('hStdOutput', _HANDLE),
90
('hStdError', _HANDLE)]
92
class _PROCESS_INFORMATION(ctypes.Structure):
93
_fields_ = [('hProcess', _HANDLE),
95
('dwProcessId', _DWORD),
96
('dwThreadId', _DWORD)]
98
_DETACHED_PROCESS = 0x00000008
99
_STARTF_USESHOWWINDOW = 0x00000001
102
class _COORD(ctypes.Structure):
103
_fields_ = [('X', ctypes.c_short),
104
('Y', ctypes.c_short)]
106
class _SMALL_RECT(ctypes.Structure):
107
_fields_ = [('Left', ctypes.c_short),
108
('Top', ctypes.c_short),
109
('Right', ctypes.c_short),
110
('Bottom', ctypes.c_short)]
112
class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
113
_fields_ = [('dwSize', _COORD),
114
('dwCursorPosition', _COORD),
115
('wAttributes', _WORD),
116
('srWindow', _SMALL_RECT),
117
('dwMaximumWindowSize', _COORD)]
119
_STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12
121
def _raiseoserror(name):
122
err = ctypes.WinError()
123
raise OSError(err.errno, '%s: %s' % (name, err.strerror))
125
def _getfileinfo(name):
126
fh = _kernel32.CreateFileA(name, 0,
127
_FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
128
None, _OPEN_EXISTING, 0, None)
129
if fh == _INVALID_HANDLE_VALUE:
132
fi = _BY_HANDLE_FILE_INFORMATION()
133
if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
137
_kernel32.CloseHandle(fh)
23
139
def os_link(src, dst):
25
win32file.CreateHardLink(dst, src)
26
except pywintypes.error:
27
raise OSError(errno.EINVAL, 'target implements hardlinks improperly')
28
except NotImplementedError: # Another fake error win Win98
29
raise OSError(errno.EINVAL, 'Hardlinking not supported')
31
def _getfileinfo(pathname):
32
"""Return number of hardlinks for the given file."""
34
fh = win32file.CreateFile(pathname,
35
win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
36
None, win32file.OPEN_EXISTING, 0, None)
37
except pywintypes.error:
38
raise OSError(errno.ENOENT, 'The system cannot find the file specified')
40
return win32file.GetFileInformationByHandle(fh)
45
"""Return number of hardlinks for the given file."""
46
return _getfileinfo(pathname)[7]
140
if not _kernel32.CreateHardLinkA(dst, src, None):
144
'''return number of hardlinks for the given file'''
145
return _getfileinfo(name).nNumberOfLinks
48
147
def samefile(fpath1, fpath2):
49
"""Returns whether fpath1 and fpath2 refer to the same file. This is only
50
guaranteed to work for files, not directories."""
148
'''Returns whether fpath1 and fpath2 refer to the same file. This is only
149
guaranteed to work for files, not directories.'''
51
150
res1 = _getfileinfo(fpath1)
52
151
res2 = _getfileinfo(fpath2)
53
# Index 4 is the volume serial number, and 8 and 9 contain the file ID
54
return res1[4] == res2[4] and res1[8] == res2[8] and res1[9] == res2[9]
152
return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
153
and res1.nFileIndexHigh == res2.nFileIndexHigh
154
and res1.nFileIndexLow == res2.nFileIndexLow)
56
156
def samedevice(fpath1, fpath2):
57
"""Returns whether fpath1 and fpath2 are on the same device. This is only
58
guaranteed to work for files, not directories."""
157
'''Returns whether fpath1 and fpath2 are on the same device. This is only
158
guaranteed to work for files, not directories.'''
59
159
res1 = _getfileinfo(fpath1)
60
160
res2 = _getfileinfo(fpath2)
61
return res1[4] == res2[4]
161
return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
64
164
'''return True if pid is still running or unable to
65
165
determine, False otherwise'''
67
handle = win32api.OpenProcess(
68
win32con.PROCESS_QUERY_INFORMATION, False, pid)
70
status = win32process.GetExitCodeProcess(handle)
71
return status == win32con.STILL_ACTIVE
72
except pywintypes.error, details:
73
return details[0] != winerror.ERROR_INVALID_PARAMETER
166
h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
170
if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
171
return status.value == _STILL_ACTIVE
173
_kernel32.CloseHandle(h)
174
return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
76
176
def lookup_reg(key, valname=None, scope=None):
77
177
''' Look up a key/value name in the Windows registry.
82
182
a sequence of scopes to look up in order. Default (CURRENT_USER,
86
from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, \
185
adv = ctypes.windll.advapi32
92
scope = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE)
188
scope = (_HKEY_CURRENT_USER, _HKEY_LOCAL_MACHINE)
93
189
elif not isinstance(scope, (list, tuple)):
193
res = adv.RegOpenKeyExA(s, key, 0, _KEY_READ, ctypes.byref(kh))
194
if res != _ERROR_SUCCESS:
97
val = QueryValueEx(OpenKey(s, key), valname)[0]
98
# never let a Unicode string escape into the wild
99
return encoding.tolocal(val.encode('UTF-8'))
100
except EnvironmentError:
103
def system_rcpath_win32():
104
'''return default os-specific hgrc search path'''
105
filename = win32api.GetModuleFileName(0)
106
# Use mercurial.ini found in directory with hg.exe
107
progrc = os.path.join(os.path.dirname(filename), 'mercurial.ini')
108
if os.path.isfile(progrc):
110
# Use hgrc.d found in directory with hg.exe
111
progrcd = os.path.join(os.path.dirname(filename), 'hgrc.d')
112
if os.path.isdir(progrcd):
114
for f, kind in osutil.listdir(progrcd):
115
if f.endswith('.rc'):
116
rcpath.append(os.path.join(progrcd, f))
118
# else look for a system rcpath in the registry
120
value = win32api.RegQueryValue(
121
win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Mercurial')
123
for p in value.split(os.pathsep):
124
if p.lower().endswith('mercurial.ini'):
126
elif os.path.isdir(p):
127
for f, kind in osutil.listdir(p):
128
if f.endswith('.rc'):
129
rcpath.append(os.path.join(p, f))
131
except pywintypes.error:
134
def user_rcpath_win32():
135
'''return os-specific hgrc search path to the user dir'''
136
userdir = os.path.expanduser('~')
137
if sys.getwindowsversion()[3] != 2 and userdir == '~':
138
# We are on win < nt: fetch the APPDATA directory location and use
139
# the parent directory as the user home dir.
140
appdir = shell.SHGetPathFromIDList(
141
shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
142
userdir = os.path.dirname(appdir)
143
return [os.path.join(userdir, 'mercurial.ini'),
144
os.path.join(userdir, '.hgrc')]
199
buf = ctypes.create_string_buffer(size.value + 1)
200
res = adv.RegQueryValueExA(kh.value, valname, None,
201
byref(type), buf, byref(size))
202
if res != _ERROR_SUCCESS:
204
if type.value == _REG_SZ:
205
# never let a Unicode string escape into the wild
206
return encoding.tolocal(buf.value.encode('UTF-8'))
207
elif type.value == _REG_DWORD:
209
s = ctypes.string_at(byref(buf), struct.calcsize(fmt))
210
return struct.unpack(fmt, s)[0]
212
adv.RegCloseKey(kh.value)
214
def executable_path():
215
'''return full path of hg.exe'''
217
buf = ctypes.create_string_buffer(size + 1)
218
len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
220
raise ctypes.WinError()
222
raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
147
226
'''return name of current user'''
148
return win32api.GetUserName()
150
def set_signal_handler_win32():
151
"""Register a termination handler for console events including
227
adv = ctypes.windll.advapi32
229
buf = ctypes.create_string_buffer(size.value + 1)
230
if not adv.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
231
raise ctypes.WinError()
234
_SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
237
def set_signal_handler():
238
'''Register a termination handler for console events including
152
239
CTRL+C. python signal handlers do not work well with socket
155
242
def handler(event):
156
win32process.ExitProcess(1)
157
win32api.SetConsoleCtrlHandler(handler)
243
_kernel32.ExitProcess(1)
246
return # already registered
247
h = _SIGNAL_HANDLER(handler)
248
_signal_handler.append(h) # needed to prevent garbage collection
249
if not _kernel32.SetConsoleCtrlHandler(h, True):
250
raise ctypes.WinError()
252
_WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
159
254
def hidewindow():
160
def callback(*args, **kwargs):
162
wpid = win32process.GetWindowThreadProcessId(hwnd)[1]
164
win32gui.ShowWindow(hwnd, win32con.SW_HIDE)
166
pid = win32process.GetCurrentProcessId()
167
win32gui.EnumWindows(callback, pid)
255
user32 = ctypes.windll.user32
257
def callback(hwnd, pid):
259
user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
260
if pid == wpid.value:
261
user32.ShowWindow(hwnd, _SW_HIDE)
262
return False # stop enumerating windows
265
pid = _kernel32.GetCurrentProcessId()
266
user32.EnumWindows(_WNDENUMPROC(callback), pid)
171
# Query stderr to avoid problems with redirections
172
screenbuf = win32console.GetStdHandle(win32console.STD_ERROR_HANDLE)
173
if screenbuf is None:
176
window = screenbuf.GetConsoleScreenBufferInfo()['Window']
177
width = window.Right - window.Left
181
except pywintypes.error:
269
# cmd.exe does not handle CR like a unix console, the CR is
270
# counted in the line length. On 80 columns consoles, if 80
271
# characters are written, the following CR won't apply on the
272
# current line but on the new one. Keep room for it.
274
# Query stderr to avoid problems with redirections
275
screenbuf = _kernel32.GetStdHandle(
276
_STD_ERROR_HANDLE) # don't close the handle returned
277
if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
279
csbi = _CONSOLE_SCREEN_BUFFER_INFO()
280
if not _kernel32.GetConsoleScreenBufferInfo(
281
screenbuf, ctypes.byref(csbi)):
283
width = csbi.srWindow.Right - csbi.srWindow.Left
286
def spawndetached(args):
287
# No standard library function really spawns a fully detached
288
# process under win32 because they allocate pipes or other objects
289
# to handle standard streams communications. Passing these objects
290
# to the child process requires handle inheritance to be enabled
291
# which makes really detached processes impossible.
293
si.cb = ctypes.sizeof(_STARTUPINFO)
294
si.dwFlags = _STARTF_USESHOWWINDOW
295
si.wShowWindow = _SW_HIDE
297
pi = _PROCESS_INFORMATION()
301
env += "%s=%s\0" % (k, os.environ[k])
306
args = subprocess.list2cmdline(args)
307
# Not running the command in shell mode makes python26 hang when
308
# writing to hgweb output socket.
309
comspec = os.environ.get("COMSPEC", "cmd.exe")
310
args = comspec + " /c " + args
312
res = _kernel32.CreateProcessA(
313
None, args, None, None, False, _DETACHED_PROCESS,
314
env, os.getcwd(), ctypes.byref(si), ctypes.byref(pi))
316
raise ctypes.WinError()
318
return pi.dwProcessId