What is the best method to determine the path of Windows special-folders and known-folders in python?
I've uncovered a couple of popular methods for SpecialFolders, a.k.a CSIDL, but nothing straightforward yet for KNOWNFOLDERID. Backwards compatibility is maintained, so the CSIDL methods still work, but any special/known folders introduced since Windows XP are not enumerated there. Examples of the "new" unavailable ones include Downloads, Playlists, Program Files x64.
Special Folders
Find system folder locations in Python - simplest CSIDL method, relies on win32com, good for AllUsersDesktop, AllUsersStartMenu, AllUsersPrograms, AllUsersStartup, Desktop, Favorites, Fonts, MyDocuments, NetHood, PrintHood, Recent, SendTo, StartMenu, Startup & Templates (list probably incomplete).
How to get path of Start Menu's Programs directory? - has same approach as above, as well as method using ctypes directly, so win32com not needed (not as clear or straightforward)
Known Folders
Both in one
You solve this problem the same way as that one: First you find the documentation and ideally some nice sample code in C++, C#, or VB, then you figure out how to use PyWin32 to make the same shell API or IKnownFolder
COM calls from Python.
As the MSDN overview documentation on Known Folders says, you can use the new shell wrapper function SHGetKnownFolderPath
instead of the old SHFolderPath
or SHGetFolderPath
, or you can use the complete IKnownFolderManager
interface via COM.
Unfortunately, I don't have a Windows machine in front of me, and MSDN's sample downloads are not responding, so I'm going to have to do a bit of guessing. But it may be something like this:
from win32com.shell import shell, shellcon
path = shell.SHGetKnownFolderPath(shellcon.FOLDERID_AccountPictures,
0, # see KNOWN_FOLDER_FLAG
0) # current user
If shellcon
doesn't have the FOLDERID
values, you'll have to look them up on the KNOWNFOLDERID
and define the constants you need yourself.
If shell
doesn't have the SHGetKnownFolderPath
function, you'll have to instantiate an IKnownFolderManager
and call GetFolderByName
.
If shell
doesn't even have IKnownFolderManager
… but a quick Google shows it was added in build 218, so that's not going to be an issue.
If you'd rather do it via ctypes
than win32com
, it would look something like this (again, untested because I don't have a Windows box and MSDN's server is broken):
from ctypes import windll, wintypes
from ctypes import *
from uuid import UUID
# ctypes GUID copied from MSDN sample code
class GUID(Structure):
_fields_ = [
("Data1", wintypes.DWORD),
("Data2", wintypes.WORD),
("Data3", wintypes.WORD),
("Data4", wintypes.BYTE * 8)
]
def __init__(self, uuidstr):
uuid = UUID(uuidstr)
Structure.__init__(self)
self.Data1, self.Data2, self.Data3, self.Data4[0], self.Data4[1], rest = uuid.fields
for i in range(2, 8):
self.Data4[i] = rest>>(8-i-1)*8 & 0xff
FOLDERID_AccountPictures = '{008ca0b1-55b4-4c56-b8a8-4de4b299d3be}'
SHGetKnownFolderPath = windll.shell32.SHGetKnownFolderPath
SHGetKnownFolderPath.argtypes = [
POINTER(GUID), wintypes.DWORD, wintypes.HANDLE, POINTER(c_char_p)]
def get_known_folder_path(uuidstr):
pathptr = c_wchar_p()
guid = GUID(uuidstr)
if SHGetKnownFolderPath(byref(guid), 0, 0, byref(pathptr)):
raise Exception('Whatever you want here...')
return pathptr.value