Windows Special and Known Folders from python (Start Menu, Downloads, ...) [duplicate]
Asked Answered
I

1

4

What is the best method to determine the path of Windows and 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

  • ?
Irmine answered 27/4, 2015 at 5:47 Comment(3)
Wikipedia has a list of them at en.wikipedia.org/wiki/Special_folder#List_of_special_foldersHogback
Thanks @Marichyasana, but I need to determine what they are dynamically at run time. They don't always have the same spellings (different languages) and can be in different locations according to Group Policy and/or other customizations. That wikipedia list is also very incomplete.Irmine
The question that was previously found includes some answers about known folders, and that sort of information goes there anyway - since "known folders" is really just a new API for the same feature.Caren
P
4

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
Pyrophotometer answered 27/4, 2015 at 6:34 Comment(4)
Your intuition is correct, win32com.shell doesn't have the SHGetKnownFolderPath function, at least not on my Win7 py27 machine. Instantiating IKnownFolderManager via COM is beyond my current understanding. I eventually tracked your clue to shell.IID_IKnownFolderManagement (thanks, I was using an older pywin32 without it) but GetFolderByName attribute doesn't exist. A 'net search hasn't returned examples, just the announcement of it being available. I found items like shell.FOLDERID_Downloads that return an IID code but I haven't been able to figure out how to use that to get to a path.Irmine
I confess to bewilderment. Windows KnownFolders have been out for a long time and I expected to be pointed to a well trodden path. I guess I've been spoiled, in that most of my python quests to date have been of the batteries-included variety. :) ...I'll explore ctypes example tomorrow. Maybe that will bear more fruit for me. (Almost forgot: thank you very much for giving me something to pore over and chew on.)Irmine
Remembered I'd stumbled across this ctypes example yesterday, gist.github.com/mkropat/7550097. It's similar to yours, though I've yet to study it closely to see if resemblance is only superficial. In my quick initial testing it always returns values for default instead of current user.Irmine
@mattwilkie: That last problem may just be a stupid mistake on my part. Check the docs to make sure 0 (NULL) actual means "current user" instead of "default". There are a few cases where the APIs use 1-7 cast to void* for other special values, and this could be one...Pyrophotometer

© 2022 - 2024 — McMap. All rights reserved.