Windows 7下Shell API IFileOperation复制无盘符文件报接口不支持错误
问题:Windows 7下使用IFileOperation复制手机无盘符文件报错解决
我正在使用Windows Shell API的IFileOperation接口,将USB连接的手机中的文件复制到电脑。由于手机存储没有盘符,常规的基于文件的复制函数(如os或shutil)无法实现该操作,因此采用以下代码:
import pythoncom from win32comext.shell import shell, shellcon fo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation, None, pythoncom.CLSCTX_ALL, shell.IID_IFileOperation) src = shell.SHCreateShellItem(...) # 详见下方补充说明 dst = shell.SHCreateItemFromParsingName(path, None, shell.IID_IShellItem) # 这些对象已成功创建 # 示例:<PyIShellFolder at 0x0000000002A6AB50 with obj at 0x0000000002ADA270> fo.CopyItem(src, dst) # 也测试过:fo.CopyItem(src, dst_folder, filename), fo.CopyItem(src, dst_folder, None, None) 等形式 fo.PerformOperations() # 错误发生在该行
测试情况
- 标准路径如
C:\ABC\DEF\test.txt:在Win7和Win10上均可正常运行 - 路径如
This PC\Phone\card\DCIM\test.jpg:在Win10上可正常运行 - 路径如
Computer\Phone\card\DCIM\test.jpg:在Win7上运行失败,并出现以下错误(注意Win7中“This PC”名称为“Computer”)。
错误信息
pywintypes.com_error: (-2147467262, 'No such interface supported', None, None)
需求
如何让这段代码在Windows 7上正常运行?(仍需支持该平台)
补充说明
- 与问题《Shell API to copy all files in a folder?》相关(该问题建议使用IFileOperation和CopyFile),但并非重复问题。
- 同时涉及IFileOperation::PerformOperations返回E_NOINTERFACE的相关讨论。
- 根据文档,IFileOperation::CopyItem自Windows Vista起便已支持。
- 使用
SHCreateShellItem创建src是因为需要递归遍历目录树:
for f in folder.EnumObjects(0, shellcon.SHCONTF_NONFOLDERS): srcfolder = shell.SHGetIDListFromObject(folder) src = shell.SHCreateShellItem(srcfolder, None, f) ...
另外,未使用shell.SHParseDisplayName或shell.SHCreateItemFromParsingName,因为它们在Win7上无法处理类似Computer\MyPhone\Card\myfiles\test.txt的路径。
解决办法
方法1:逐级枚举获取ShellItem(推荐)
Win7无法直接解析Computer\Phone\card\DCIM\test.jpg这类非文件系统路径,因此需要从Shell根节点(桌面)开始,逐级枚举子项获取目标文件的ShellItem,而非直接通过路径构造。示例代码如下:
import pythoncom from win32comext.shell import shell, shellcon def get_shell_item_by_hierarchy(root_item, path_parts): """从根ShellItem开始,逐级查找子项""" current_item = root_item for part in path_parts: found = False # 枚举当前目录的子项(包含文件夹和文件) for item in current_item.EnumObjects(0, shellcon.SHCONTF_FOLDERS | shellcon.SHCONTF_NONFOLDERS): item_name = item.GetDisplayName(shellcon.SHGDN_NORMAL) if item_name == part: current_item = item found = True break if not found: raise FileNotFoundError(f"找不到子项:{part}") return current_item # 初始化COM pythoncom.CoInitialize() # 获取桌面ShellItem(Shell命名空间根节点) desktop = shell.SHCreateItemFromParsingName("::{00021400-0000-0000-C000-000000000046}", None, shell.IID_IShellItem) # 获取Win7的"Computer"(Win10对应"This PC") computer = desktop.BindToHandler(None, shell.BHID_SFObject, shell.IID_IShellItem) # 替换为你的手机存储路径层级,比如["Phone", "card", "DCIM", "test.jpg"] src_path_parts = ["Phone", "card", "DCIM", "test.jpg"] src = get_shell_item_by_hierarchy(computer, src_path_parts) # 目标本地文件夹路径 dst_path = "C:\\TargetFolder" dst = shell.SHCreateItemFromParsingName(dst_path, None, shell.IID_IShellItem) # 执行复制操作 fo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation, None, pythoncom.CLSCTX_ALL, shell.IID_IFileOperation) # 设置操作选项(按需调整) fo.SetOperationFlags(shellcon.FOF_NOCONFIRMATION | shellcon.FOF_SILENT | shellcon.FOF_NOERRORUI) fo.CopyItem(src, dst) fo.PerformOperations() # 释放COM资源 pythoncom.CoUninitialize()
这种方式通过Shell层级枚举获取的ShellItem,在Win7上能被IFileOperation正确识别,避免路径解析的兼容性问题。
方法2:回退到旧API SHFileOperation
如果IFileOperation仍无法兼容,可使用旧的SHFileOperation API,它在Win7上对MTP设备的支持更稳定:
import pythoncom from win32comext.shell import shell, shellcon import win32api # 初始化COM pythoncom.CoInitialize() # 假设已通过逐级枚举获取到src和dst的IShellItem对象 src_pidl = shell.SHGetIDListFromObject(src) dst_pidl = shell.SHGetIDListFromObject(dst) # 构造SHFILEOPSTRUCT结构体 fos = shell.SHFILEOPSTRUCT() fos.hwnd = None fos.wFunc = shellcon.FO_COPY # pFrom和pTo需要是双空终止的字符串 fos.pFrom = shell.SHILCreateFromIDList(src_pidl, shell.IID_IShellItem).GetDisplayName(shellcon.SHGDN_FORPARSING) + '\x00\x00' fos.pTo = shell.SHILCreateFromIDList(dst_pidl, shell.IID_IShellItem).GetDisplayName(shellcon.SHGDN_FORPARSING) + '\x00\x00' # 设置操作选项 fos.fFlags = shellcon.FOF_NOCONFIRMATION | shellcon.FOF_SILENT | shellcon.FOF_NOERRORUI | shellcon.FOF_NOCONFIRMMKDIR # 执行复制 result = shell.SHFileOperation(fos) if result != 0: raise win32api.error(result, "SHFileOperation执行失败") # 释放COM资源 pythoncom.CoUninitialize()
方法3:尝试转换为IShellItem2接口
部分Win7的Shell扩展可能需要IShellItem2接口支持,可尝试将src对象转换为该接口:
# 在创建src后添加以下代码 try: src_item2 = src.QueryInterface(shell.IID_IShellItem2) # 使用转换后的对象执行复制 fo.CopyItem(src_item2, dst) except pythoncom.com_error: # 转换失败,改用其他方法 pass
内容的提问来源于stack exchange,提问作者Basj
相关产品推荐
相关产品推荐

