我编写了这个最小的可重现示例来“艰难地”计算 Windows 上的桌面文件夹(使用
SHGetKnownFolderPath
),但我似乎最终得到了成功错误代码,而输出缓冲区在取消引用时仅产生 b'C'
通过 .result
的
c_char_p
属性。我做错了什么?
我的代码是这样做的:
_GUID
结构格式result_ptr = c_char_p()
,它最初是一个 NULL 指针,但将被指向结果的指针覆盖SHGetKnownFolderPath
,通过引用传递我们的 result_ptr
,以便可以覆盖其值SHGetKnownFolderPath
指示成功,则使用 result_ptr
取消引用
.value
我得到的结果只有一个字符长,但我认为
c_char_p
应该是指向空终止字符串开头的指针。
Windows 是否在我的指针中写入了虚假字符串,我是否错误地读取了它的值,或者我在构建函数时犯了其他错误?
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}'
根据[MS.Learn]:SHGetKnownFolderPath函数(shlobj_core.h)(强调是我的):
[out] ppszPath
类型:PWSTR*
此方法返回时,包含指向以 null 结尾的 Unicode 字符串的 指针的地址
函数以WIDE(016bit)字符串形式返回路径,即wchar_t*,或[Python.Docs]:类ctypes.c_wchar_p。
检查[SO]:将 utf-16 字符串传递给 Windows 函数(@CristiFati 的答案)了解更多详细信息。
因此,您需要更改的(在get_known_folder中)是:
result_ptr = ctypes.c_wchar_p() # Note the 'w'