[Tutor] Windows "feature" I don't understand

eryksun eryksun at gmail.com
Mon Aug 3 11:26:09 CEST 2015


 On Sun, Aug 2, 2015 at 9:14 PM, Clayton Kirkwood <crk at godblessthe.us> wrote:
> get into the innards much. I ran into a problem in my program, which we have
> been discussing, which is windows-caused. I originally set my directory in
> my code to /users/Clayton/Pictures by memory that that was the name of the
> directory in windows. My code worked using the directory. I then noticed
> that my "real" directory as listed by windows explorer was
> /users/Clayton/My Pictures. I changed it in my code and nothing works.
> /users/Clayton/Pictures doesn't show up in explorer even tho I've set to
> display hidden files, system files, etc.

"My Pictures" is a localized name. The actual filesystem path in your
case is %USERPROFILE%\Pictures. There should be a hidden file named
desktop.ini in that directory. It defines the shell32 resource ID of
the localized name. If for some reason you need this display string,
you can retrieve it via PyWin32's win32api.LoadString, or via ctypes.
Here's an example of the latter.

rstring.py:

    import ctypes
    from ctypes import wintypes

    user32 = ctypes.WinDLL('user32', use_last_error=True)

    def errcheck_bool(result, func, args):
        if not result:
            raise ctypes.WinError(ctypes.get_last_error())
        return args

    user32.LoadStringW.errcheck = errcheck_bool
    user32.LoadStringW.argtypes = (wintypes.HINSTANCE,
                                   wintypes.UINT,
                                   wintypes.LPWSTR,
                                   ctypes.c_int)

    PWCHAR = ctypes.POINTER(wintypes.WCHAR)

    def load_string(hInstance, uID):
        resource = PWCHAR()
        lpBuffer = ctypes.cast(ctypes.byref(resource), wintypes.LPWSTR)
        nchar = user32.LoadStringW(hInstance, uID, lpBuffer, 0)
        return resource[:nchar]

    if __name__ == '__main__':
        import os
        import sys
        if len(sys.argv) != 3:
            sys.exit('usage:\n%s dllPath stringId' %
os.path.basename(sys.argv[0]))
        hInstance = ctypes.WinDLL(sys.argv[1])._handle
        uID = int(sys.argv[2])
        print(load_string(hInstance, uID))

For example, here's the contents of the desktop.ini file in Windows 7:

    C:\>type C:\Users\Administrator\Pictures\desktop.ini

    [.ShellClassInfo]
    LocalizedResourceName=@%SystemRoot%\system32\shell32.dll,-21779
    InfoTip=@%SystemRoot%\system32\shell32.dll,-12688
    IconResource=%SystemRoot%\system32\imageres.dll,-113
    IconFile=%SystemRoot%\system32\shell32.dll
    IconIndex=-236

And here's what I get for the LocalizedResourceName and InfoTip for a
system configured for U.S. English:

    C:\>rstring.py shell32 21779
    My Pictures

    C:\>rstring.py shell32 12688
    Contains digital photos, images, and graphic files.

(In practice, instead of looking for desktop.ini files, a program
should call SHGetLocalizedName to get the module path and resource
ID.)

It's unwise to just assume the location of a shell folder. The
underlying filesystem paths are customizable. Instead, use the API to
get the location of known folders by GUID. For Windows Vista and later
you can call SHGetKnownFolderPath. This function isn't available in
PyWin32, but it does wrap the old SHGetFolderPath function in
win32com.shell.shell. SHGetFolderPath uses the CSIDL_* constants,
which are defined in win32com.shell.shellcon.

Here's a ctypes example that calls SHGetKnownFolderPath to look up the
path for FOLDERID_Pictures. As a bonus I've also included the GUIDs
for several other folders.

    import ctypes
    from ctypes import wintypes

    ole32 = ctypes.OleDLL('ole32')
    shell32 = ctypes.OleDLL('shell32')

    class GUID(ctypes.Structure):
        _fields_ = (('Data1', ctypes.c_ulong),
                    ('Data2', ctypes.c_ushort),
                    ('Data3', ctypes.c_ushort),
                    ('Data4', ctypes.c_char * 8))
        def __init__(self, guid_string):
            ole32.IIDFromString(guid_string, ctypes.byref(self))

    IID = GUID
    LPIID = ctypes.POINTER(IID)
    LPCOLESTR = wintypes.LPCWSTR
    ole32.IIDFromString.argtypes = (LPCOLESTR,
                                    LPIID)

    ole32.CoTaskMemFree.restype = None
    ole32.CoTaskMemFree.argtypes = (wintypes.LPVOID,)

    REFKNOWNFOLDERID = LPIID
    shell32.SHGetKnownFolderPath.argtypes = (REFKNOWNFOLDERID,
                                             wintypes.DWORD,
                                             wintypes.HANDLE,
                                             ctypes.POINTER(wintypes.LPWSTR))

    def get_known_folder_path(folder_id):
        pszPath = wintypes.LPWSTR()
        shell32.SHGetKnownFolderPath(ctypes.byref(folder_id),
                                     0, None, ctypes.byref(pszPath))
        folder_path = pszPath.value
        ole32.CoTaskMemFree(pszPath)
        return folder_path

    # common
    FOLDERID_Public          = GUID('{DFDF76A2-C82A-4D63-906A-5644AC457385}')
    FOLDERID_PublicDesktop   = GUID('{C4AA340D-F20F-4863-AFEF-F87EF2E6BA25}')
    FOLDERID_PublicDocuments = GUID('{ED4824AF-DCE4-45A8-81E2-FC7965083634}')
    FOLDERID_PublicDownloads = GUID('{3D644C9B-1FB8-4f30-9B45-F670235F79C0}')
    FOLDERID_PublicMusic     = GUID('{3214FAB5-9757-4298-BB61-92A9DEAA44FF}')
    FOLDERID_PublicPictures  = GUID('{B6EBFB86-6907-413C-9AF7-4FC2ABF07CC5}')
    FOLDERID_PublicVideos    = GUID('{2400183A-6185-49FB-A2D8-4A392A602BA3}')
    FOLDERID_ProgramData     = GUID('{62AB5D82-FDC1-4DC3-A9DD-070D1D495D97}')
    FOLDERID_CommonStartMenu = GUID('{A4115719-D62E-491D-AA7C-E74B8BE3B067}')
    FOLDERID_CommonPrograms  = GUID('{0139D44E-6AFE-49F2-8690-3DAFCAE6FFB8}')
    FOLDERID_CommonStartup   = GUID('{82A5EA35-D9CD-47C5-9629-E15D2F714E6E}')
    FOLDERID_CommonTemplates = GUID('{B94237E7-57AC-4347-9151-B08C6C32D1F7}')
    # user
    FOLDERID_Profile         = GUID('{5E6C858F-0E22-4760-9AFE-EA3317B67173}')
    FOLDERID_Desktop         = GUID('{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}')
    FOLDERID_Documents       = GUID('{FDD39AD0-238F-46AF-ADB4-6C85480369C7}')
    FOLDERID_Downloads       = GUID('{374DE290-123F-4565-9164-39C4925E467B}')
    FOLDERID_Music           = GUID('{4BD8D571-6D19-48D3-BE97-422220080E43}')
    FOLDERID_Pictures        = GUID('{33E28130-4E1E-4676-835A-98395C3BC3BB}')
    FOLDERID_Videos          = GUID('{18989B1D-99B5-455B-841C-AB7C74E4DDFC}')
    FOLDERID_LocalAppData    = GUID('{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}')
    FOLDERID_LocalAppDataLow = GUID('{A520A1A4-1780-4FF6-BD18-167343C5AF16}')
    FOLDERID_RoamingAppData  = GUID('{3EB685DB-65F9-4CF6-A03A-E3EF65729F3D}')
    FOLDERID_StartMenu       = GUID('{625B53C3-AB48-4EC1-BA1F-A1EF4146FC19}')
    FOLDERID_Programs        = GUID('{A77F5D77-2E2B-44C3-A6A2-ABA601054A51}')
    FOLDERID_Startup         = GUID('{B97D20BB-F46A-4C97-BA10-5E3608430854}')
    FOLDERID_Templates       = GUID('{A63293E8-664E-48DB-A079-DF759E0509F7}')

    if __name__ == '__main__':
        print(get_known_folder_path(FOLDERID_Pictures))

> I dropped down into dos and lo and behold,

You probably ran cmd.exe, which is a Win32 console application, not
DOS. The cmd shell makes only limited use of shell32 functionality,
such as calling ShellExecuteEx to open/execute files. (FYI, the
attached console window is hosted by the GUI application conhost.exe.
If you run python.exe from Explorer it's similarly attached to an
instance of conhost.exe, without a running instance of cmd.exe. So
don't assume that console windows in general have anything to do with
cmd.exe.)


More information about the Tutor mailing list