Skip to content

Commit 32c72f2

Browse files
InfernioUtumno
authored andcommitted
env: Use KNOWNFOLDERID instead of CSIDL
As Microsoft's info page on these constants (https://docs.microsoft.com/en-us/windows/win32/shell/csidl) mentions: As of Windows Vista, these values have been replaced by KNOWNFOLDERID values. [...] The CSIDL system is supported under Windows Vista for compatibility reasons. However, new development should use KNOWNFOLDERID values rather than CSIDL values. I suspect this is the cause of the IOError: [Errno 22] Invalid argument that some users are getting - for one user, the CSIDL values resolved to C:\Users\{USER NAME HERE}\OneDrive\My Documents\, which is obviously wrong. In either case, we should be using the new ones here. For some reason, pywin32 doesn't provide wrappers for these. So this is based on code by Michael Kropat, see the code in env.py for full credits and a link to the original. I don't like that this introduces some ctypes-based code to env.py, I'd rather have that in windows.py, but a circular dependency would arise if I tried that...
1 parent 7cd5dcc commit 32c72f2

1 file changed

Lines changed: 162 additions & 10 deletions

File tree

Mopy/bash/env.py

Lines changed: 162 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
import re as _re
3030
import shutil as _shutil
3131
import stat
32+
from ctypes import byref, c_wchar_p, c_void_p, POINTER, Structure, windll, \
33+
wintypes
34+
from uuid import UUID
3235

3336
from .bolt import GPath, deprint, Path, decode, struct_unpack
3437
from .exception import BoltError, CancelError, SkipError, AccessDeniedError, \
@@ -124,21 +127,21 @@ def clear_read_only(filepath): # copied from bolt
124127

125128
def get_personal_path():
126129
if shell and shellcon:
127-
path = _getShellPath(shellcon.CSIDL_PERSONAL)
128-
sErrorInfo = _(u"Folder path extracted from win32com.shell.")
130+
personal_path = get_known_path(FOLDERID.Documents)
131+
error_info = _(u'Folder path retrieved via SHGetKnownFolderPath')
129132
else:
130-
path = _getShellPath('Personal')
131-
sErrorInfo = __get_error_info()
132-
return GPath(path), sErrorInfo
133+
personal_path = _getShellPath('Personal')
134+
error_info = __get_error_info()
135+
return GPath(personal_path), error_info
133136

134137
def get_local_app_data_path():
135138
if shell and shellcon:
136-
path = _getShellPath(shellcon.CSIDL_LOCAL_APPDATA)
137-
sErrorInfo = _(u"Folder path extracted from win32com.shell.")
139+
local_path = get_known_path(FOLDERID.LocalAppData)
140+
error_info = _(u'Folder path retrieved via SHGetKnownFolderPath')
138141
else:
139-
path = _getShellPath('Local AppData')
140-
sErrorInfo = __get_error_info()
141-
return GPath(path), sErrorInfo
142+
local_path = _getShellPath('Local AppData')
143+
error_info = __get_error_info()
144+
return GPath(local_path), error_info
142145

143146
def __get_error_info():
144147
try:
@@ -768,3 +771,152 @@ def getJava():
768771
# So javaw.exe would actually be in Windows\SysWOW64
769772
java = win.join(u'syswow64', u'javaw.exe')
770773
return java
774+
775+
# TODO(inf) Maybe move to windows.py? Circular dependency though...
776+
# All code starting from the 'BEGIN MIT-LICENSED PART' comment and until the
777+
# 'END MIT-LICENSED PART' comment is based on
778+
# https://gist.github.com/mkropat/7550097 by Michael Kropat
779+
# Modifications made for py3 compatibility and to conform to our code style
780+
# BEGIN MIT-LICENSED PART =====================================================
781+
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa373931.aspx
782+
class GUID(Structure):
783+
_fields_ = [
784+
("Data1", wintypes.DWORD),
785+
("Data2", wintypes.WORD),
786+
("Data3", wintypes.WORD),
787+
("Data4", wintypes.BYTE * 8)
788+
]
789+
790+
def __init__(self, uuid_):
791+
super(GUID, self).__init__()
792+
self.Data1, self.Data2, self.Data3, self.Data4[0], self.Data4[1], \
793+
rest = uuid_.fields
794+
for i in range(2, 8):
795+
self.Data4[i] = rest>>(8 - i - 1)*8 & 0xff
796+
797+
# http://msdn.microsoft.com/en-us/library/windows/desktop/dd378457.aspx
798+
class FOLDERID(object):
799+
AccountPictures = UUID('{008ca0b1-55b4-4c56-b8a8-4de4b299d3be}')
800+
AdminTools = UUID('{724EF170-A42D-4FEF-9F26-B60E846FBA4F}')
801+
ApplicationShortcuts = UUID('{A3918781-E5F2-4890-B3D9-A7E54332328C}')
802+
CameraRoll = UUID('{AB5FB87B-7CE2-4F83-915D-550846C9537B}')
803+
CDBurning = UUID('{9E52AB10-F80D-49DF-ACB8-4330F5687855}')
804+
CommonAdminTools = UUID('{D0384E7D-BAC3-4797-8F14-CBA229B392B5}')
805+
CommonOEMLinks = UUID('{C1BAE2D0-10DF-4334-BEDD-7AA20B227A9D}')
806+
CommonPrograms = UUID('{0139D44E-6AFE-49F2-8690-3DAFCAE6FFB8}')
807+
CommonStartMenu = UUID('{A4115719-D62E-491D-AA7C-E74B8BE3B067}')
808+
CommonStartup = UUID('{82A5EA35-D9CD-47C5-9629-E15D2F714E6E}')
809+
CommonTemplates = UUID('{B94237E7-57AC-4347-9151-B08C6C32D1F7}')
810+
Contacts = UUID('{56784854-C6CB-462b-8169-88E350ACB882}')
811+
Cookies = UUID('{2B0F765D-C0E9-4171-908E-08A611B84FF6}')
812+
Desktop = UUID('{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}')
813+
DeviceMetadataStore = UUID('{5CE4A5E9-E4EB-479D-B89F-130C02886155}')
814+
Documents = UUID('{FDD39AD0-238F-46AF-ADB4-6C85480369C7}')
815+
DocumentsLibrary = UUID('{7B0DB17D-9CD2-4A93-9733-46CC89022E7C}')
816+
Downloads = UUID('{374DE290-123F-4565-9164-39C4925E467B}')
817+
Favorites = UUID('{1777F761-68AD-4D8A-87BD-30B759FA33DD}')
818+
Fonts = UUID('{FD228CB7-AE11-4AE3-864C-16F3910AB8FE}')
819+
GameTasks = UUID('{054FAE61-4DD8-4787-80B6-090220C4B700}')
820+
History = UUID('{D9DC8A3B-B784-432E-A781-5A1130A75963}')
821+
ImplicitAppShortcuts = UUID('{BCB5256F-79F6-4CEE-B725-DC34E402FD46}')
822+
InternetCache = UUID('{352481E8-33BE-4251-BA85-6007CAEDCF9D}')
823+
Libraries = UUID('{1B3EA5DC-B587-4786-B4EF-BD1DC332AEAE}')
824+
Links = UUID('{bfb9d5e0-c6a9-404c-b2b2-ae6db6af4968}')
825+
LocalAppData = UUID('{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}')
826+
LocalAppDataLow = UUID('{A520A1A4-1780-4FF6-BD18-167343C5AF16}')
827+
LocalizedResourcesDir = UUID('{2A00375E-224C-49DE-B8D1-440DF7EF3DDC}')
828+
Music = UUID('{4BD8D571-6D19-48D3-BE97-422220080E43}')
829+
MusicLibrary = UUID('{2112AB0A-C86A-4FFE-A368-0DE96E47012E}')
830+
NetHood = UUID('{C5ABBF53-E17F-4121-8900-86626FC2C973}')
831+
OriginalImages = UUID('{2C36C0AA-5812-4b87-BFD0-4CD0DFB19B39}')
832+
PhotoAlbums = UUID('{69D2CF90-FC33-4FB7-9A0C-EBB0F0FCB43C}')
833+
PicturesLibrary = UUID('{A990AE9F-A03B-4E80-94BC-9912D7504104}')
834+
Pictures = UUID('{33E28130-4E1E-4676-835A-98395C3BC3BB}')
835+
Playlists = UUID('{DE92C1C7-837F-4F69-A3BB-86E631204A23}')
836+
PrintHood = UUID('{9274BD8D-CFD1-41C3-B35E-B13F55A758F4}')
837+
Profile = UUID('{5E6C858F-0E22-4760-9AFE-EA3317B67173}')
838+
ProgramData = UUID('{62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}')
839+
ProgramFiles = UUID('{905e63b6-c1bf-494e-b29c-65b732d3d21a}')
840+
ProgramFilesX64 = UUID('{6D809377-6AF0-444b-8957-A3773F02200E}')
841+
ProgramFilesX86 = UUID('{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}')
842+
ProgramFilesCommon = UUID('{F7F1ED05-9F6D-47A2-AAAE-29D317C6F066}')
843+
ProgramFilesCommonX64 = UUID('{6365D5A7-0F0D-45E5-87F6-0DA56B6A4F7D}')
844+
ProgramFilesCommonX86 = UUID('{DE974D24-D9C6-4D3E-BF91-F4455120B917}')
845+
Programs = UUID('{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}')
846+
Public = UUID('{DFDF76A2-C82A-4D63-906A-5644AC457385}')
847+
PublicDesktop = UUID('{C4AA340D-F20F-4863-AFEF-F87EF2E6BA25}')
848+
PublicDocuments = UUID('{ED4824AF-DCE4-45A8-81E2-FC7965083634}')
849+
PublicDownloads = UUID('{3D644C9B-1FB8-4f30-9B45-F670235F79C0}')
850+
PublicGameTasks = UUID('{DEBF2536-E1A8-4c59-B6A2-414586476AEA}')
851+
PublicLibraries = UUID('{48DAF80B-E6CF-4F4E-B800-0E69D84EE384}')
852+
PublicMusic = UUID('{3214FAB5-9757-4298-BB61-92A9DEAA44FF}')
853+
PublicPictures = UUID('{B6EBFB86-6907-413C-9AF7-4FC2ABF07CC5}')
854+
PublicRingtones = UUID('{E555AB60-153B-4D17-9F04-A5FE99FC15EC}')
855+
PublicUserTiles = UUID('{0482af6c-08f1-4c34-8c90-e17ec98b1e17}')
856+
PublicVideos = UUID('{2400183A-6185-49FB-A2D8-4A392A602BA3}')
857+
QuickLaunch = UUID('{52a4f021-7b75-48a9-9f6b-4b87a210bc8f}')
858+
Recent = UUID('{AE50C081-EBD2-438A-8655-8A092E34987A}')
859+
RecordedTVLibrary = UUID('{1A6FDBA2-F42D-4358-A798-B74D745926C5}')
860+
ResourceDir = UUID('{8AD10C31-2ADB-4296-A8F7-E4701232C972}')
861+
Ringtones = UUID('{C870044B-F49E-4126-A9C3-B52A1FF411E8}')
862+
RoamingAppData = UUID('{3EB685DB-65F9-4CF6-A03A-E3EF65729F3D}')
863+
RoamedTileImages = UUID('{AAA8D5A5-F1D6-4259-BAA8-78E7EF60835E}')
864+
RoamingTiles = UUID('{00BCFC5A-ED94-4e48-96A1-3F6217F21990}')
865+
SampleMusic = UUID('{B250C668-F57D-4EE1-A63C-290EE7D1AA1F}')
866+
SamplePictures = UUID('{C4900540-2379-4C75-844B-64E6FAF8716B}')
867+
SamplePlaylists = UUID('{15CA69B3-30EE-49C1-ACE1-6B5EC372AFB5}')
868+
SampleVideos = UUID('{859EAD94-2E85-48AD-A71A-0969CB56A6CD}')
869+
SavedGames = UUID('{4C5C32FF-BB9D-43b0-B5B4-2D72E54EAAA4}')
870+
SavedSearches = UUID('{7d1d3a04-debb-4115-95cf-2f29da2920da}')
871+
Screenshots = UUID('{b7bede81-df94-4682-a7d8-57a52620b86f}')
872+
SearchHistory = UUID('{0D4C3DB6-03A3-462F-A0E6-08924C41B5D4}')
873+
SearchTemplates = UUID('{7E636BFE-DFA9-4D5E-B456-D7B39851D8A9}')
874+
SendTo = UUID('{8983036C-27C0-404B-8F08-102D10DCFD74}')
875+
SidebarDefaultParts = UUID('{7B396E54-9EC5-4300-BE0A-2482EBAE1A26}')
876+
SidebarParts = UUID('{A75D362E-50FC-4fb7-AC2C-A8BEAA314493}')
877+
SkyDrive = UUID('{A52BBA46-E9E1-435f-B3D9-28DAA648C0F6}')
878+
SkyDriveCameraRoll = UUID('{767E6811-49CB-4273-87C2-20F355E1085B}')
879+
SkyDriveDocuments = UUID('{24D89E24-2F19-4534-9DDE-6A6671FBB8FE}')
880+
SkyDrivePictures = UUID('{339719B5-8C47-4894-94C2-D8F77ADD44A6}')
881+
StartMenu = UUID('{625B53C3-AB48-4EC1-BA1F-A1EF4146FC19}')
882+
Startup = UUID('{B97D20BB-F46A-4C97-BA10-5E3608430854}')
883+
System = UUID('{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}')
884+
SystemX86 = UUID('{D65231B0-B2F1-4857-A4CE-A8E7C6EA7D27}')
885+
Templates = UUID('{A63293E8-664E-48DB-A079-DF759E0509F7}')
886+
UserPinned = UUID('{9E3995AB-1F9C-4F13-B827-48B24B6C7174}')
887+
UserProfiles = UUID('{0762D272-C50A-4BB0-A382-697DCD729B80}')
888+
UserProgramFiles = UUID('{5CD7AEE2-2219-4A67-B85D-6C9CE15660CB}')
889+
UserProgramFilesCommon = UUID('{BCBD3057-CA5C-4622-B42D-BC56DB0AE516}')
890+
Videos = UUID('{18989B1D-99B5-455B-841C-AB7C74E4DDFC}')
891+
VideosLibrary = UUID('{491E922F-5643-4AF4-A7EB-4E7A138D8174}')
892+
Windows = UUID('{F38BF404-1D43-42F2-9305-67DE0B28FC23}')
893+
894+
# http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188.aspx
895+
class UserHandle(object):
896+
current = wintypes.HANDLE(0)
897+
common = wintypes.HANDLE(-1)
898+
899+
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms680722.aspx
900+
_CoTaskMemFree = windll.ole32.CoTaskMemFree
901+
_CoTaskMemFree.restype= None
902+
_CoTaskMemFree.argtypes = [c_void_p]
903+
904+
# http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188.aspx
905+
# http://web.archive.org/web/20111025090317/http://www.themacaque.com/?p=954
906+
_SHGetKnownFolderPath = windll.shell32.SHGetKnownFolderPath
907+
_SHGetKnownFolderPath.argtypes = [
908+
POINTER(GUID), wintypes.DWORD, wintypes.HANDLE, POINTER(c_wchar_p)
909+
]
910+
911+
def get_known_path(known_folder_id, user_handle=UserHandle.current):
912+
kf_id = GUID(known_folder_id)
913+
pPath = c_wchar_p()
914+
S_OK = 0
915+
if _SHGetKnownFolderPath(byref(kf_id), 0, user_handle,
916+
byref(pPath)) != S_OK:
917+
raise RuntimeError(u"Failed to retrieve known folder path '%r'" %
918+
known_folder_id)
919+
path = pPath.value
920+
_CoTaskMemFree(pPath)
921+
return path
922+
# END MIT-LICENSED PART =======================================================

0 commit comments

Comments
 (0)