Quantcast
Channel: Recent Questions - Stack Overflow
Viewing all articles
Browse latest Browse all 12111

Calling SHGetKnownFolderPath from Python?

$
0
0

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 contextlibimport ctypesimport ctypes.wintypesimport functoolsimport osimport pathlibimport typesimport uuidtry:    wintypes_GUID = ctypes.wintypes.GUIDexcept 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.contextmanagerdef _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}'

Viewing all articles
Browse latest Browse all 12111

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>