You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python调用SHGetKnownFolderPath仅返回b'C',求排查错误原因

解决SHGetKnownFolderPath仅返回单个字符的问题

你的问题核心是字符串类型不匹配:Windows API的SHGetKnownFolderPath返回的是宽字符串指针(wchar_t*),但你用了单字节字符指针类型ctypes.c_char_p()来接收,导致读取错误。

宽字符串中每个字符占2字节,比如桌面路径C:\Users\XXX\Desktop的宽字符存储形式是C\x00:\x00U\x00s\x00e\x00r\x00s\x00\...,当用c_char_p读取时,会把第一个\x00(宽字符的第二个字节)当成字符串结束符,所以只得到了第一个字符b'C'。

修正后的代码

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

try:
    wintypes_GUID = ctypes.wintypes.GUID
except AttributeError:
    class wintypes_GUID(ctypes.Structure):
        _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):
    # 改为用c_wchar_p接收宽字符串指针
    result_ptr = ctypes.c_wchar_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:
            # c_wchar_p.value直接返回Python字符串,无需fsdecode
            result = result_ptr.value
            return pathlib.Path(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}; expected: {pathlib.Path("~/Desktop").expanduser()!r}'

关键修改点

  1. 替换指针类型:把result_ptr = ctypes.c_char_p()改成ctypes.c_wchar_p(),对应Windows API的宽字符串返回类型。
  2. 简化字符串转换:c_wchar_p.value直接返回Python原生字符串,不需要再用os.fsdecode()处理。
  3. 释放逻辑不变:不管是单字节还是宽字符串指针,SHGetKnownFolderPath都是用CoTaskMemAlloc分配内存,所以CoTaskMemFree可以正常释放。

内容的提问来源于stack exchange,提问作者JamesTheAwesomeDude

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 15:07:05