1
# Copyright (C) 2006, 2007 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Win32-specific helper functions
19
Only one dependency: ctypes should be installed.
28
from bzrlib import cmdline
31
if sys.platform == 'win32':
32
_major,_minor,_build,_platform,_text = sys.getwindowsversion()
35
# The operating system platform.
36
# This member can be one of the following values.
37
# ========================== ======================================
39
# -------------------------- --------------------------------------
40
# VER_PLATFORM_WIN32_NT The operating system is Windows Vista,
41
# 2 Windows Server "Longhorn",
42
# Windows Server 2003, Windows XP,
43
# Windows 2000, or Windows NT.
45
# VER_PLATFORM_WIN32_WINDOWS The operating system is Windows Me,
46
# 1 Windows 98, or Windows 95.
47
# ========================== ======================================
51
# don't care about real Windows name, just to force safe operations
57
# We can cope without it; use a separate variable to help pyflakes
64
if winver == 'Windows 98':
65
create_buffer = ctypes.create_string_buffer
68
create_buffer = ctypes.create_unicode_buffer
82
# pulling in win32com.shell is a bit of overhead, and normally we don't need
83
# it as ctypes is preferred and common. lazy_imports and "optional"
84
# modules don't work well, so we do our own lazy thing...
85
has_win32com_shell = None # Set to True or False once we know for sure...
87
# Special Win32 API constants
88
# Handles of std streams
89
WIN32_STDIN_HANDLE = -10
90
WIN32_STDOUT_HANDLE = -11
91
WIN32_STDERR_HANDLE = -12
93
# CSIDL constants (from MSDN 2003)
94
CSIDL_APPDATA = 0x001A # Application Data folder
95
CSIDL_LOCAL_APPDATA = 0x001c# <user name>\Local Settings\Application Data (non roaming)
96
CSIDL_PERSONAL = 0x0005 # My Documents folder
98
# from winapi C headers
101
MAX_COMPUTERNAME_LENGTH = 31
103
# Registry data type ids
108
def debug_memory_win32api(message='', short=True):
109
"""Use trace.note() to dump the running memory info."""
110
from bzrlib import trace
112
class PROCESS_MEMORY_COUNTERS_EX(ctypes.Structure):
113
"""Used by GetProcessMemoryInfo"""
114
_fields_ = [('cb', ctypes.c_ulong),
115
('PageFaultCount', ctypes.c_ulong),
116
('PeakWorkingSetSize', ctypes.c_size_t),
117
('WorkingSetSize', ctypes.c_size_t),
118
('QuotaPeakPagedPoolUsage', ctypes.c_size_t),
119
('QuotaPagedPoolUsage', ctypes.c_size_t),
120
('QuotaPeakNonPagedPoolUsage', ctypes.c_size_t),
121
('QuotaNonPagedPoolUsage', ctypes.c_size_t),
122
('PagefileUsage', ctypes.c_size_t),
123
('PeakPagefileUsage', ctypes.c_size_t),
124
('PrivateUsage', ctypes.c_size_t),
126
cur_process = ctypes.windll.kernel32.GetCurrentProcess()
127
mem_struct = PROCESS_MEMORY_COUNTERS_EX()
128
ret = ctypes.windll.psapi.GetProcessMemoryInfo(cur_process,
129
ctypes.byref(mem_struct),
130
ctypes.sizeof(mem_struct))
132
trace.note('Failed to GetProcessMemoryInfo()')
134
info = {'PageFaultCount': mem_struct.PageFaultCount,
135
'PeakWorkingSetSize': mem_struct.PeakWorkingSetSize,
136
'WorkingSetSize': mem_struct.WorkingSetSize,
137
'QuotaPeakPagedPoolUsage': mem_struct.QuotaPeakPagedPoolUsage,
138
'QuotaPagedPoolUsage': mem_struct.QuotaPagedPoolUsage,
139
'QuotaPeakNonPagedPoolUsage': mem_struct.QuotaPeakNonPagedPoolUsage,
140
'QuotaNonPagedPoolUsage': mem_struct.QuotaNonPagedPoolUsage,
141
'PagefileUsage': mem_struct.PagefileUsage,
142
'PeakPagefileUsage': mem_struct.PeakPagefileUsage,
143
'PrivateUsage': mem_struct.PrivateUsage,
147
# win32process does not return PrivateUsage, because it doesn't use
148
# PROCESS_MEMORY_COUNTERS_EX (it uses the one without _EX).
149
proc = win32process.GetCurrentProcess()
150
info = win32process.GetProcessMemoryInfo(proc)
152
trace.note('Cannot debug memory on win32 without ctypes'
156
trace.note('WorkingSize %7dKB'
157
'\tPeakWorking %7dKB\t%s',
158
info['WorkingSetSize'] / 1024,
159
info['PeakWorkingSetSize'] / 1024,
163
trace.note('%s', message)
164
trace.note('WorkingSize %8d KB', info['WorkingSetSize'] / 1024)
165
trace.note('PeakWorking %8d KB', info['PeakWorkingSetSize'] / 1024)
166
trace.note('PagefileUsage %8d KB', info.get('PagefileUsage', 0) / 1024)
167
trace.note('PeakPagefileUsage %8d KB', info.get('PeakPagefileUsage', 0) / 1024)
168
trace.note('PrivateUsage %8d KB', info.get('PrivateUsage', 0) / 1024)
169
trace.note('PageFaultCount %8d', info.get('PageFaultCount', 0))
172
def get_console_size(defaultx=80, defaulty=25):
173
"""Return size of current console.
175
This function try to determine actual size of current working
176
console window and return tuple (sizex, sizey) if success,
177
or default size (defaultx, defaulty) otherwise.
181
return (defaultx, defaulty)
183
# To avoid problem with redirecting output via pipe
184
# we need to use stderr instead of stdout
185
h = ctypes.windll.kernel32.GetStdHandle(WIN32_STDERR_HANDLE)
186
csbi = ctypes.create_string_buffer(22)
187
res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
190
(bufx, bufy, curx, cury, wattr,
191
left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
192
sizex = right - left + 1
193
sizey = bottom - top + 1
194
return (sizex, sizey)
196
return (defaultx, defaulty)
199
def _get_sh_special_folder_path(csidl):
200
"""Call SHGetSpecialFolderPathW if available, or return None.
202
Result is always unicode (or None).
206
SHGetSpecialFolderPath = \
207
ctypes.windll.shell32.SHGetSpecialFolderPathW
208
except AttributeError:
211
buf = ctypes.create_unicode_buffer(MAX_PATH)
212
if SHGetSpecialFolderPath(None,buf,csidl,0):
215
global has_win32com_shell
216
if has_win32com_shell is None:
218
from win32com.shell import shell
219
has_win32com_shell = True
221
has_win32com_shell = False
222
if has_win32com_shell:
223
# still need to bind the name locally, but this is fast.
224
from win32com.shell import shell
226
return shell.SHGetSpecialFolderPath(0, csidl, 0)
228
# possibly E_NOTIMPL meaning we can't load the function pointer,
229
# or E_FAIL meaning the function failed - regardless, just ignore it
234
def get_appdata_location():
235
"""Return Application Data location.
236
Return None if we cannot obtain location.
238
Windows defines two 'Application Data' folders per user - a 'roaming'
239
one that moves with the user as they logon to different machines, and
240
a 'local' one that stays local to the machine. This returns the 'roaming'
241
directory, and thus is suitable for storing user-preferences, etc.
243
Returned value can be unicode or plain string.
244
To convert plain string to unicode use
245
s.decode(osutils.get_user_encoding())
246
(XXX - but see bug 262874, which asserts the correct encoding is 'mbcs')
248
appdata = _get_sh_special_folder_path(CSIDL_APPDATA)
252
appdata = os.environ.get('APPDATA')
255
# if we fall to this point we on win98
256
# at least try C:/WINDOWS/Application Data
257
windir = os.environ.get('windir')
259
appdata = os.path.join(windir, 'Application Data')
260
if os.path.isdir(appdata):
262
# did not find anything
266
def get_local_appdata_location():
267
"""Return Local Application Data location.
268
Return the same as get_appdata_location() if we cannot obtain location.
270
Windows defines two 'Application Data' folders per user - a 'roaming'
271
one that moves with the user as they logon to different machines, and
272
a 'local' one that stays local to the machine. This returns the 'local'
273
directory, and thus is suitable for caches, temp files and other things
274
which don't need to move with the user.
276
Returned value can be unicode or plain string.
277
To convert plain string to unicode use
278
s.decode(osutils.get_user_encoding())
279
(XXX - but see bug 262874, which asserts the correct encoding is 'mbcs')
281
local = _get_sh_special_folder_path(CSIDL_LOCAL_APPDATA)
284
# Vista supplies LOCALAPPDATA, but XP and earlier do not.
285
local = os.environ.get('LOCALAPPDATA')
288
return get_appdata_location()
291
def get_home_location():
292
"""Return user's home location.
293
Assume on win32 it's the <My Documents> folder.
294
If location cannot be obtained return system drive root,
297
Returned value can be unicode or plain string.
298
To convert plain string to unicode use
299
s.decode(osutils.get_user_encoding())
301
home = _get_sh_special_folder_path(CSIDL_PERSONAL)
304
# try for HOME env variable
305
home = os.path.expanduser('~')
308
# at least return windows root directory
309
windir = os.environ.get('windir')
311
return os.path.splitdrive(windir)[0] + '/'
312
# otherwise C:\ is good enough for 98% users
317
"""Return user name as login name.
318
If name cannot be obtained return None.
320
Returned value can be unicode or plain string.
321
To convert plain string to unicode use
322
s.decode(osutils.get_user_encoding())
326
advapi32 = ctypes.windll.advapi32
327
GetUserName = getattr(advapi32, 'GetUserName'+suffix)
328
except AttributeError:
331
buf = create_buffer(UNLEN+1)
332
n = ctypes.c_int(UNLEN+1)
333
if GetUserName(buf, ctypes.byref(n)):
335
# otherwise try env variables
336
return os.environ.get('USERNAME', None)
339
# 1 == ComputerNameDnsHostname, which returns "The DNS host name of the local
340
# computer or the cluster associated with the local computer."
341
_WIN32_ComputerNameDnsHostname = 1
344
"""Return host machine name.
345
If name cannot be obtained return None.
347
:return: A unicode string representing the host name. On win98, this may be
348
a plain string as win32 api doesn't support unicode.
352
return win32api.GetComputerNameEx(_WIN32_ComputerNameDnsHostname)
353
except (NotImplementedError, win32api.error):
354
# NotImplemented will happen on win9x...
358
kernel32 = ctypes.windll.kernel32
359
except AttributeError:
360
pass # Missing the module we need
362
buf = create_buffer(MAX_COMPUTERNAME_LENGTH+1)
363
n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
365
# Try GetComputerNameEx which gives a proper Unicode hostname
366
GetComputerNameEx = getattr(kernel32, 'GetComputerNameEx'+suffix,
368
if (GetComputerNameEx is not None
369
and GetComputerNameEx(_WIN32_ComputerNameDnsHostname,
370
buf, ctypes.byref(n))):
373
# Try GetComputerName in case GetComputerNameEx wasn't found
374
# It returns the NETBIOS name, which isn't as good, but still ok.
375
# The first GetComputerNameEx might have changed 'n', so reset it
376
n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
377
GetComputerName = getattr(kernel32, 'GetComputerName'+suffix,
379
if (GetComputerName is not None
380
and GetComputerName(buf, ctypes.byref(n))):
382
# otherwise try env variables, which will be 'mbcs' encoded
383
# on Windows (Python doesn't expose the native win32 unicode environment)
385
# http://msdn.microsoft.com/en-us/library/aa246807.aspx
386
# environment variables should always be encoded in 'mbcs'.
388
return os.environ['COMPUTERNAME'].decode("mbcs")
393
def _ensure_unicode(s):
394
if s and type(s) != unicode:
395
from bzrlib import osutils
396
s = s.decode(osutils.get_user_encoding())
400
def get_appdata_location_unicode():
401
return _ensure_unicode(get_appdata_location())
403
def get_home_location_unicode():
404
return _ensure_unicode(get_home_location())
406
def get_user_name_unicode():
407
return _ensure_unicode(get_user_name())
409
def get_host_name_unicode():
410
return _ensure_unicode(get_host_name())
413
def _ensure_with_dir(path):
414
if not os.path.split(path)[0] or path.startswith(u'*') or path.startswith(u'?'):
415
return u'./' + path, True
419
def _undo_ensure_with_dir(path, corrected):
427
def glob_one(possible_glob):
428
"""Same as glob.glob().
430
work around bugs in glob.glob()
431
- Python bug #1001604 ("glob doesn't return unicode with ...")
432
- failing expansion for */* with non-iso-8859-* chars
434
corrected_glob, corrected = _ensure_with_dir(possible_glob)
435
glob_files = glob.glob(corrected_glob)
438
# special case to let the normal code path handle
439
# files that do not exist, etc.
440
glob_files = [possible_glob]
442
glob_files = [_undo_ensure_with_dir(elem, corrected)
443
for elem in glob_files]
444
return [elem.replace(u'\\', u'/') for elem in glob_files]
447
def glob_expand(file_list):
448
"""Replacement for glob expansion by the shell.
450
Win32's cmd.exe does not do glob expansion (eg ``*.py``), so we do our own
453
:param file_list: A list of filenames which may include shell globs.
454
:return: An expanded list of filenames.
456
Introduced in bzrlib 0.18.
460
expanded_file_list = []
461
for possible_glob in file_list:
462
expanded_file_list.extend(glob_one(possible_glob))
463
return expanded_file_list
466
def get_app_path(appname):
467
"""Look up in Windows registry for full path to application executable.
468
Typically, applications create subkey with their basename
469
in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\
471
:param appname: name of application (if no filename extension
472
is specified, .exe used)
473
:return: full path to aplication executable from registry,
474
or appname itself if nothing found.
479
if not os.path.splitext(basename)[1]:
480
basename = appname + '.exe'
483
hkey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
484
'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\' +
486
except EnvironmentError:
491
path, type_id = _winreg.QueryValueEx(hkey, '')
495
_winreg.CloseKey(hkey)
497
if type_id == REG_SZ:
499
if type_id == REG_EXPAND_SZ and has_win32api:
500
fullpath = win32api.ExpandEnvironmentStrings(path)
501
if len(fullpath) > 1 and fullpath[0] == '"' and fullpath[-1] == '"':
502
fullpath = fullpath[1:-1] # remove quotes around value
507
def set_file_attr_hidden(path):
508
"""Set file attributes to hidden if possible"""
510
if winver != 'Windows 98':
511
SetFileAttributes = win32file.SetFileAttributesW
513
SetFileAttributes = win32file.SetFileAttributes
515
SetFileAttributes(path, win32file.FILE_ATTRIBUTE_HIDDEN)
516
except pywintypes.error, e:
517
from bzrlib import trace
518
trace.mutter('Unable to set hidden attribute on %r: %s', path, e)
521
def command_line_to_argv(command_line, wildcard_expansion=True,
522
single_quotes_allowed=False):
523
"""Convert a Unicode command line into a list of argv arguments.
525
This optionally does wildcard expansion, etc. It is intended to make
526
wildcards act closer to how they work in posix shells, versus how they
527
work by default on Windows. Quoted arguments are left untouched.
529
:param command_line: The unicode string to split into an arg list.
530
:param wildcard_expansion: Whether wildcard expansion should be applied to
531
each argument. True by default.
532
:param single_quotes_allowed: Whether single quotes are accepted as quoting
533
characters like double quotes. False by
535
:return: A list of unicode strings.
537
s = cmdline.Splitter(command_line, single_quotes_allowed=single_quotes_allowed)
538
# Now that we've split the content, expand globs if necessary
539
# TODO: Use 'globbing' instead of 'glob.glob', this gives us stuff like
542
for is_quoted, arg in s:
543
if is_quoted or not glob.has_magic(arg) or not wildcard_expansion:
546
args.extend(glob_one(arg))
550
if has_ctypes and winver != 'Windows 98':
551
def get_unicode_argv():
552
prototype = ctypes.WINFUNCTYPE(ctypes.c_wchar_p)
553
GetCommandLineW = prototype(("GetCommandLineW",
554
ctypes.windll.kernel32))
555
command_line = GetCommandLineW()
556
if command_line is None:
557
raise ctypes.WinError()
558
# Skip the first argument, since we only care about parameters
559
argv = command_line_to_argv(command_line)[1:]
560
if getattr(sys, 'frozen', None) is None:
561
# Invoked via 'python.exe' which takes the form:
562
# python.exe [PYTHON_OPTIONS] C:\Path\bzr [BZR_OPTIONS]
563
# we need to get only BZR_OPTIONS part,
564
# We already removed 'python.exe' so we remove everything up to and
565
# including the first non-option ('-') argument.
566
for idx in xrange(len(argv)):
567
if argv[idx][:1] != '-':
572
get_unicode_argv = None