|
1 | | -# -*- coding: UTF-8 -*- |
2 | | -#shlobj.py |
3 | | -#A part of NonVisual Desktop Access (NVDA) |
4 | | -#Copyright (C) 2006-2017 NV Access Limited, Babbage B.V. |
5 | | -#This file is covered by the GNU General Public License. |
6 | | -#See the file COPYING for more details. |
| 1 | +# A part of NonVisual Desktop Access (NVDA) |
| 2 | +# Copyright (C) 2009-2021 NV Access Limited, Babbage B.V., Łukasz Golonka |
| 3 | +# This file is covered by the GNU General Public License. |
| 4 | +# See the file COPYING for more details. |
7 | 5 |
|
8 | | -""" |
9 | | -This module wraps the SHGetFolderPath function in shell32.dll and defines the necessary contstants. |
10 | | -CSIDL (constant special item ID list) values provide a unique system-independent way to |
| 6 | +r""" |
| 7 | +This module wraps the `SHGetKnownFolderPath` function in shell32.dll and defines the necessary contstants. |
| 8 | +Known folder ids provide a unique system-independent way to |
11 | 9 | identify special folders used frequently by applications, but which may not have the same name |
12 | 10 | or location on any given system. For example, the system folder may be "C:\Windows" on one system |
13 | | -and "C:\Winnt" on another. The CSIDL system is used to be compatible with Windows XP. |
| 11 | +and "C:\Winnt" on another. |
14 | 12 | """ |
15 | 13 |
|
16 | | -from ctypes import * |
17 | | -from ctypes.wintypes import * |
| 14 | +import comtypes |
| 15 | +import ctypes |
| 16 | +import enum |
| 17 | +import functools |
| 18 | +import typing |
18 | 19 |
|
19 | | -shell32 = windll.shell32 |
20 | 20 |
|
21 | | -MAX_PATH = 260 |
| 21 | +class FOLDERID(str, enum.Enum): |
| 22 | + """Contains guids of known folders from Knownfolders.h. Full list is availabe at: |
| 23 | + https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid""" |
| 24 | + #: The file system directory that serves as a common repository for application-specific data. |
| 25 | + #: A typical path is C:\Documents and Settings\username\Application Data. |
| 26 | + RoamingAppData = "{3EB685DB-65F9-4CF6-A03A-E3EF65729F3D}" |
| 27 | + #: The file system directory that serves as a data repository for local (nonroaming) applications. |
| 28 | + #: A typical path is C:\Documents and Settings\username\Local Settings\Application Data. |
| 29 | + LocalAppData = "{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}" |
| 30 | + #: The file system directory that contains application data for all users. |
| 31 | + #: A typical path is C:\Documents and Settings\All Users\Application Data. |
| 32 | + #: This folder is used for application data that is not user specific. |
| 33 | + ProgramData = "{62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}" |
| 34 | + # The Windows System folder. |
| 35 | + # A typical path is C:\Windows\System32. |
| 36 | + System = "{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}" |
| 37 | + SystemX86 = "{D65231B0-B2F1-4857-A4CE-A8E7C6EA7D27}" |
22 | 38 |
|
23 | | -#: The file system directory that serves as a common repository for application-specific data. |
24 | | -#: A typical path is C:\Documents and Settings\username\Application Data. |
25 | | -CSIDL_APPDATA = 0x001a |
26 | | -#: The file system directory that serves as a data repository for local (nonroaming) applications. |
27 | | -#: A typical path is C:\Documents and Settings\username\Local Settings\Application Data. |
28 | | -CSIDL_LOCAL_APPDATA = 0x001c |
29 | | -#: The file system directory that contains application data for all users. |
30 | | -#: A typical path is C:\Documents and Settings\All Users\Application Data. |
31 | | -#: This folder is used for application data that is not user specific. |
32 | | -CSIDL_COMMON_APPDATA = 0x0023 |
33 | 39 |
|
34 | | -def SHGetFolderPath(owner, folder, token=0, flags=0): |
35 | | - path = create_unicode_buffer(MAX_PATH) |
36 | | - # Note As of Windows Vista, this function is merely a wrapper for SHGetKnownFolderPath |
37 | | - if shell32.SHGetFolderPathW(owner, folder, token, flags, byref(path)) != 0: |
38 | | - raise WinError() |
39 | | - return path.value |
| 40 | +@functools.lru_cache(maxsize=128) |
| 41 | +def SHGetKnownFolderPath(folderGuid: str, dwFlags: int = 0, hToken: typing.Optional[int] = None) -> str: |
| 42 | + """Wrapper for `SHGetKnownFolderPath` which caches the results |
| 43 | + to avoid calling the win32 function unnecessarily.""" |
| 44 | + guid = comtypes.GUID(folderGuid) |
| 45 | + pathPointer = ctypes.c_wchar_p() |
| 46 | + res = ctypes.windll.shell32.SHGetKnownFolderPath( |
| 47 | + comtypes.byref(guid), |
| 48 | + dwFlags, |
| 49 | + hToken, |
| 50 | + ctypes.byref(pathPointer) |
| 51 | + ) |
| 52 | + if res != 0: |
| 53 | + raise RuntimeError(f"SHGetKnownFolderPath failed with erro code {res}") |
| 54 | + path = pathPointer.value |
| 55 | + ctypes.windll.ole32.CoTaskMemFree(pathPointer) |
| 56 | + return path |
0 commit comments