[Python] Calling SHGetKnownFolderPath from Python?

Discussão em 'Python' iniciado por Stack, Maio 2, 2025.

  1. Stack

    Stack Membro Participativo

    I've written this minimal reproducible example to calculate the Desktop folder on Windows "the hard way" (using SHGetKnownFolderPath), but I seem to end up with a Success error code while the output buffer only yields b'C' when dereferenced via the .result property of c_char_p. What am I doing wrong?

    My code does this:

    1. Converts the desired GUID into the cursed _GUID struct format according to Microsoft's specification
    2. Allocates result_ptr = c_char_p() which is initially a NULL pointer but will be overwritten with the pointer to the result
    3. Calls SHGetKnownFolderPath with the desired GUID struct, no flags, on the current user, passing our result_ptr by reference so its value can be overwritten
    4. If SHGetKnownFolderPath indicated success, dereferences result_ptr using .value

    I'm getting a result which is only a single char long, but I thought that c_char_p is supposed to be the pointer to the start of a null-terminated string.

    Is Windows writing a bogus string into my pointer, am I reading its value out wrongly, or have I made some other error in building my function?

    import contextlib
    import ctypes
    import ctypes.wintypes
    import functools
    import os
    import pathlib
    import types
    import uuid

    try:
    wintypes_GUID = ctypes.wintypes.GUID
    except AttributeError:
    class wintypes_GUID(ctypes.Structure):
    # https://learn.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid
    # https://github.com/enthought/comtypes/blob/1.3.1/comtypes/GUID.py
    _fields_ = [
    ('Data1', ctypes.c_ulong),
    ('Data2', ctypes.c_ushort),
    ('Data3', ctypes.c_ushort),
    ('Data4', ctypes.c_ubyte * 8)
    ]

    @classmethod
    def _from_uuid(cls, u):
    u = uuid.UUID(u)
    u_str = f'{{{u!s}}}'
    result = wintypes_GUID()
    errno = ctypes.oledll.ole32.CLSIDFromString(u_str, ctypes.byref(result))
    if errno == 0:
    return result
    else:
    raise RuntimeError(f'CLSIDFromString returned error code {errno}')

    DESKTOP_UUID = 'B4BFCC3A-DB2C-424C-B029-7FE99A87C641'


    def get_known_folder(uuid):
    # FIXME this doesn't work, seemingly returning just b'C' no matter what
    result_ptr = ctypes.c_char_p()
    with _freeing(ctypes.oledll.ole32.CoTaskMemFree, result_ptr):
    errno = ctypes.windll.shell32.SHGetKnownFolderPath(
    ctypes.pointer(wintypes_GUID._from_uuid(uuid)),
    0,
    None,
    ctypes.byref(result_ptr)
    )
    if errno == 0:
    result = result_ptr.value
    if len(result) < 2:
    import warnings
    warnings.warn(f'result_ptr.value == {result!r}')
    return pathlib.Path(os.fsdecode(result))
    else:
    raise RuntimeError(f'Shell32.SHGetKnownFolderPath returned error code {errno}')


    @contextlib.contextmanager
    def _freeing(freefunc, obj):
    try:
    yield obj
    finally:
    freefunc(obj)


    assert get_known_folder(DESKTOP_UUID) ==\
    pathlib.Path('~/Desktop').expanduser(),\
    f'Result: {get_known_folder(DESKTOP_UUID)!r}; expcected: {pathlib.Path("~/Desktop").expanduser()!r}'

    Continue reading...

Compartilhe esta Página