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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
17
"""Win32-specific helper functions
 
 
19
Only one dependency: ctypes should be installed.
 
 
28
if sys.platform == 'win32':
 
 
29
    _major,_minor,_build,_platform,_text = sys.getwindowsversion()
 
 
32
    #   The operating system platform.
 
 
33
    #   This member can be one of the following values.
 
 
34
    #   ==========================  ======================================
 
 
36
    #   --------------------------  --------------------------------------
 
 
37
    #   VER_PLATFORM_WIN32_NT       The operating system is Windows Vista,
 
 
38
    #   2                           Windows Server "Longhorn",
 
 
39
    #                               Windows Server 2003, Windows XP,
 
 
40
    #                               Windows 2000, or Windows NT.
 
 
42
    #   VER_PLATFORM_WIN32_WINDOWS  The operating system is Windows Me,
 
 
43
    #   1                           Windows 98, or Windows 95.
 
 
44
    #   ==========================  ======================================
 
 
48
        # don't care about real Windows name, just to force safe operations
 
 
54
# We can cope without it; use a separate variable to help pyflakes
 
 
61
    if winver == 'Windows 98':
 
 
62
        create_buffer = ctypes.create_string_buffer
 
 
65
        create_buffer = ctypes.create_unicode_buffer
 
 
78
# pulling in win32com.shell is a bit of overhead, and normally we don't need
 
 
79
# it as ctypes is preferred and common.  lazy_imports and "optional"
 
 
80
# modules don't work well, so we do our own lazy thing...
 
 
81
has_win32com_shell = None # Set to True or False once we know for sure...
 
 
83
# Special Win32 API constants
 
 
84
# Handles of std streams
 
 
85
WIN32_STDIN_HANDLE = -10
 
 
86
WIN32_STDOUT_HANDLE = -11
 
 
87
WIN32_STDERR_HANDLE = -12
 
 
89
# CSIDL constants (from MSDN 2003)
 
 
90
CSIDL_APPDATA = 0x001A      # Application Data folder
 
 
91
CSIDL_LOCAL_APPDATA = 0x001c# <user name>\Local Settings\Application Data (non roaming)
 
 
92
CSIDL_PERSONAL = 0x0005     # My Documents folder
 
 
94
# from winapi C headers
 
 
97
MAX_COMPUTERNAME_LENGTH = 31
 
 
100
def get_console_size(defaultx=80, defaulty=25):
 
 
101
    """Return size of current console.
 
 
103
    This function try to determine actual size of current working
 
 
104
    console window and return tuple (sizex, sizey) if success,
 
 
105
    or default size (defaultx, defaulty) otherwise.
 
 
109
        return (defaultx, defaulty)
 
 
111
    # To avoid problem with redirecting output via pipe
 
 
112
    # need to use stderr instead of stdout
 
 
113
    h = ctypes.windll.kernel32.GetStdHandle(WIN32_STDERR_HANDLE)
 
 
114
    csbi = ctypes.create_string_buffer(22)
 
 
115
    res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
 
 
118
        (bufx, bufy, curx, cury, wattr,
 
 
119
        left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
 
 
120
        sizex = right - left + 1
 
 
121
        sizey = bottom - top + 1
 
 
122
        return (sizex, sizey)
 
 
124
        return (defaultx, defaulty)
 
 
127
def _get_sh_special_folder_path(csidl):
 
 
128
    """Call SHGetSpecialFolderPathW if available, or return None.
 
 
130
    Result is always unicode (or None).
 
 
134
            SHGetSpecialFolderPath = \
 
 
135
                ctypes.windll.shell32.SHGetSpecialFolderPathW
 
 
136
        except AttributeError:
 
 
139
            buf = ctypes.create_unicode_buffer(MAX_PATH)
 
 
140
            if SHGetSpecialFolderPath(None,buf,csidl,0):
 
 
143
    global has_win32com_shell
 
 
144
    if has_win32com_shell is None:
 
 
146
            from win32com.shell import shell
 
 
147
            has_win32com_shell = True
 
 
149
            has_win32com_shell = False
 
 
150
    if has_win32com_shell:
 
 
151
        # still need to bind the name locally, but this is fast.
 
 
152
        from win32com.shell import shell
 
 
154
            return shell.SHGetSpecialFolderPath(0, csidl, 0)
 
 
156
            # possibly E_NOTIMPL meaning we can't load the function pointer,
 
 
157
            # or E_FAIL meaning the function failed - regardless, just ignore it
 
 
162
def get_appdata_location():
 
 
163
    """Return Application Data location.
 
 
164
    Return None if we cannot obtain location.
 
 
166
    Windows defines two 'Application Data' folders per user - a 'roaming'
 
 
167
    one that moves with the user as they logon to different machines, and
 
 
168
    a 'local' one that stays local to the machine.  This returns the 'roaming'
 
 
169
    directory, and thus is suitable for storing user-preferences, etc.
 
 
171
    Returned value can be unicode or plain string.
 
 
172
    To convert plain string to unicode use
 
 
173
    s.decode(osutils.get_user_encoding())
 
 
174
    (XXX - but see bug 262874, which asserts the correct encoding is 'mbcs')
 
 
176
    appdata = _get_sh_special_folder_path(CSIDL_APPDATA)
 
 
180
    appdata = os.environ.get('APPDATA')
 
 
183
    # if we fall to this point we on win98
 
 
184
    # at least try C:/WINDOWS/Application Data
 
 
185
    windir = os.environ.get('windir')
 
 
187
        appdata = os.path.join(windir, 'Application Data')
 
 
188
        if os.path.isdir(appdata):
 
 
190
    # did not find anything
 
 
194
def get_local_appdata_location():
 
 
195
    """Return Local Application Data location.
 
 
196
    Return the same as get_appdata_location() if we cannot obtain location.
 
 
198
    Windows defines two 'Application Data' folders per user - a 'roaming'
 
 
199
    one that moves with the user as they logon to different machines, and
 
 
200
    a 'local' one that stays local to the machine.  This returns the 'local'
 
 
201
    directory, and thus is suitable for caches, temp files and other things
 
 
202
    which don't need to move with the user.
 
 
204
    Returned value can be unicode or plain string.
 
 
205
    To convert plain string to unicode use
 
 
206
    s.decode(bzrlib.user_encoding)
 
 
207
    (XXX - but see bug 262874, which asserts the correct encoding is 'mbcs')
 
 
209
    local = _get_sh_special_folder_path(CSIDL_LOCAL_APPDATA)
 
 
212
    # Vista supplies LOCALAPPDATA, but XP and earlier do not.
 
 
213
    local = os.environ.get('LOCALAPPDATA')
 
 
216
    return get_appdata_location()
 
 
219
def get_home_location():
 
 
220
    """Return user's home location.
 
 
221
    Assume on win32 it's the <My Documents> folder.
 
 
222
    If location cannot be obtained return system drive root,
 
 
225
    Returned value can be unicode or plain sring.
 
 
226
    To convert plain string to unicode use
 
 
227
    s.decode(osutils.get_user_encoding())
 
 
229
    home = _get_sh_special_folder_path(CSIDL_PERSONAL)
 
 
232
    # try for HOME env variable
 
 
233
    home = os.path.expanduser('~')
 
 
236
    # at least return windows root directory
 
 
237
    windir = os.environ.get('windir')
 
 
239
        return os.path.splitdrive(windir)[0] + '/'
 
 
240
    # otherwise C:\ is good enough for 98% users
 
 
245
    """Return user name as login name.
 
 
246
    If name cannot be obtained return None.
 
 
248
    Returned value can be unicode or plain sring.
 
 
249
    To convert plain string to unicode use
 
 
250
    s.decode(osutils.get_user_encoding())
 
 
254
            advapi32 = ctypes.windll.advapi32
 
 
255
            GetUserName = getattr(advapi32, 'GetUserName'+suffix)
 
 
256
        except AttributeError:
 
 
259
            buf = create_buffer(UNLEN+1)
 
 
260
            n = ctypes.c_int(UNLEN+1)
 
 
261
            if GetUserName(buf, ctypes.byref(n)):
 
 
263
    # otherwise try env variables
 
 
264
    return os.environ.get('USERNAME', None)
 
 
267
# 1 == ComputerNameDnsHostname, which returns "The DNS host name of the local
 
 
268
# computer or the cluster associated with the local computer."
 
 
269
_WIN32_ComputerNameDnsHostname = 1
 
 
272
    """Return host machine name.
 
 
273
    If name cannot be obtained return None.
 
 
275
    :return: A unicode string representing the host name. On win98, this may be
 
 
276
        a plain string as win32 api doesn't support unicode.
 
 
280
            return win32api.GetComputerNameEx(_WIN32_ComputerNameDnsHostname)
 
 
281
        except (NotImplementedError, win32api.error):
 
 
282
            # NotImplemented will happen on win9x...
 
 
286
            kernel32 = ctypes.windll.kernel32
 
 
287
        except AttributeError:
 
 
288
            pass # Missing the module we need
 
 
290
            buf = create_buffer(MAX_COMPUTERNAME_LENGTH+1)
 
 
291
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
 
 
293
            # Try GetComputerNameEx which gives a proper Unicode hostname
 
 
294
            GetComputerNameEx = getattr(kernel32, 'GetComputerNameEx'+suffix,
 
 
296
            if (GetComputerNameEx is not None
 
 
297
                and GetComputerNameEx(_WIN32_ComputerNameDnsHostname,
 
 
298
                                      buf, ctypes.byref(n))):
 
 
301
            # Try GetComputerName in case GetComputerNameEx wasn't found
 
 
302
            # It returns the NETBIOS name, which isn't as good, but still ok.
 
 
303
            # The first GetComputerNameEx might have changed 'n', so reset it
 
 
304
            n = ctypes.c_int(MAX_COMPUTERNAME_LENGTH+1)
 
 
305
            GetComputerName = getattr(kernel32, 'GetComputerName'+suffix,
 
 
307
            if (GetComputerName is not None
 
 
308
                and GetComputerName(buf, ctypes.byref(n))):
 
 
310
    # otherwise try env variables, which will be 'mbcs' encoded
 
 
311
    # on Windows (Python doesn't expose the native win32 unicode environment)
 
 
313
    # http://msdn.microsoft.com/en-us/library/aa246807.aspx
 
 
314
    # environment variables should always be encoded in 'mbcs'.
 
 
316
        return os.environ['COMPUTERNAME'].decode("mbcs")
 
 
321
def _ensure_unicode(s):
 
 
322
    from bzrlib import osutils
 
 
323
    if s and type(s) != unicode:
 
 
324
        from bzrlib import osutils
 
 
325
        s = s.decode(osutils.get_user_encoding())
 
 
329
def get_appdata_location_unicode():
 
 
330
    return _ensure_unicode(get_appdata_location())
 
 
332
def get_home_location_unicode():
 
 
333
    return _ensure_unicode(get_home_location())
 
 
335
def get_user_name_unicode():
 
 
336
    return _ensure_unicode(get_user_name())
 
 
338
def get_host_name_unicode():
 
 
339
    return _ensure_unicode(get_host_name())
 
 
342
def _ensure_with_dir(path):
 
 
343
    if not os.path.split(path)[0] or path.startswith(u'*') or path.startswith(u'?'):
 
 
344
        return u'./' + path, True
 
 
348
def _undo_ensure_with_dir(path, corrected):
 
 
356
def glob_expand(file_list):
 
 
357
    """Replacement for glob expansion by the shell.
 
 
359
    Win32's cmd.exe does not do glob expansion (eg ``*.py``), so we do our own
 
 
362
    :param file_list: A list of filenames which may include shell globs.
 
 
363
    :return: An expanded list of filenames.
 
 
365
    Introduced in bzrlib 0.18.
 
 
370
    expanded_file_list = []
 
 
371
    for possible_glob in file_list:
 
 
373
        # work around bugs in glob.glob()
 
 
374
        # - Python bug #1001604 ("glob doesn't return unicode with ...")
 
 
375
        # - failing expansion for */* with non-iso-8859-* chars
 
 
376
        possible_glob, corrected = _ensure_with_dir(possible_glob)
 
 
377
        glob_files = glob.glob(possible_glob)
 
 
380
            # special case to let the normal code path handle
 
 
381
            # files that do not exists
 
 
382
            expanded_file_list.append(
 
 
383
                _undo_ensure_with_dir(possible_glob, corrected))
 
 
385
            glob_files = [_undo_ensure_with_dir(elem, corrected) for elem in glob_files]
 
 
386
            expanded_file_list += glob_files
 
 
388
    return [elem.replace(u'\\', u'/') for elem in expanded_file_list] 
 
 
391
def get_app_path(appname):
 
 
392
    """Look up in Windows registry for full path to application executable.
 
 
393
    Typicaly, applications create subkey with their basename
 
 
394
    in HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\
 
 
396
    :param  appname:    name of application (if no filename extension
 
 
397
                        is specified, .exe used)
 
 
398
    :return:    full path to aplication executable from registry,
 
 
399
                or appname itself if nothing found.
 
 
403
        hkey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
 
 
404
                               r'SOFTWARE\Microsoft\Windows'
 
 
405
                               r'\CurrentVersion\App Paths')
 
 
406
    except EnvironmentError:
 
 
410
    if not os.path.splitext(basename)[1]:
 
 
411
        basename = appname + '.exe'
 
 
414
            fullpath = _winreg.QueryValue(hkey, basename)
 
 
418
        _winreg.CloseKey(hkey)
 
 
423
def set_file_attr_hidden(path):
 
 
424
    """Set file attributes to hidden if possible"""
 
 
426
        win32file.SetFileAttributes(path, win32file.FILE_ATTRIBUTE_HIDDEN)