目标是在尽可能短的时间内从 Windows 上选定的文件夹中获取文件缩略图。
Imagemagick 和其他工具每次都会打开文件,因此它们可能不是最高效的。 Windows 中的内置机制可以将缩略图缓存在缓存中,理论上这会带来相当大的速度提升。
从如何使用Windows API获取文件的缩略图?(answer),方法变得清晰,但有很多问题:
在 win32com.shell.shell 中找不到 IID,因此它们是手动提供的。
IThumbnailProvider,IShellItem::BindToHandler 方法:已修复,但现在错误如下:
# str(shell.IID_IShellItem) == '{43826D1E-E718-42EE-BC55-A1E261C37BFE}'
# str(shell.BHID_ThumbnailHandler) == '{7B2E650A-8E20-4F4A-B09E-6597AFC72FB0}'
item = shell.SHCreateItemFromParsingName(r'd:\some.png', None, str(shell.IID_IShellItem)) # str() is works
IID_IThumbnailProvider = '{e357fccd-a995-4576-b01f-234630154e96}'
provider = item.BindToHandler(None, shell.BHID_ThumbnailHandler, IID_IThumbnailProvider) # error
> TypeError: 'There is no interface object registered that supports this IID'
IShellItemImageFactory:怀疑 IID 的使用错误,但从下面的answer来看它应该可以工作:
IID_IShellItemImageFactory = '{bcc18b79-ba16-442f-80c4-8a59c30c463b}'
item = shell.SHCreateItemFromParsingName(r'd:\some.png', None, IID_IShellItemImageFactory) # error
> TypeError: 'There is no interface object registered that supports this IID'
如果能够在不学习整个 winapi 的情况下了解如何在这一切中游泳,那就太好了..
提前致谢。
Windows 10 (20H2)、Python 3.9.5、PyWin32 301
更新:找到有关 PyWin32 的答案(pythoncom.CoCreateInstance 无法创建 IZoneIdentifier;尽管 MSDN 中有记录,但注册表中也缺少该接口)。简而言之,这些接口都没有实现。检查者:
> import pythoncom
> pythoncom.InterfaceNames
所以..还剩下2个问题。是否有任何选项可以在不使用 PyWin32 的情况下获取缩略图?例如,通过 ctypes。又如何?
这里有一些基于 ctypes 和 comtypes 的可用 Python 3 代码:
from ctypes import POINTER, byref, cast, windll, c_void_p, c_wchar_p
from ctypes.wintypes import SIZE, UINT, HANDLE, HBITMAP
from comtypes import GUID, IUnknown, COMMETHOD, HRESULT
shell32 = windll.shell32
shell32.SHCreateItemFromParsingName.argtypes = [c_wchar_p, c_void_p, POINTER(GUID), POINTER(HANDLE)]
shell32.SHCreateItemFromParsingName.restype = HRESULT
SIIGBF_RESIZETOFIT = 0
class IShellItemImageFactory(IUnknown):
_case_insensitive_ = True
_iid_ = GUID('{bcc18b79-ba16-442f-80c4-8a59c30c463b}')
_idlflags_ = []
IShellItemImageFactory._methods_ = [
COMMETHOD([], HRESULT, 'GetImage',
( ['in'], SIZE, 'size' ),
( ['in'], UINT, 'flags' ),
( ['out'], POINTER(HBITMAP), 'phbm' )),
]
LP_IShellItemImageFactory = POINTER(IShellItemImageFactory)
def get_thumbnail(filename, icon_size):
''' Returns thumbnail image as HBITMAP'''
h_siif = HANDLE()
hr = shell32.SHCreateItemFromParsingName(filename, 0,
byref(IShellItemImageFactory._iid_), byref(h_siif))
if hr < 0:
raise Exception(f'SHCreateItemFromParsingName failed: {hr}')
h_siif = cast(h_siif, LP_IShellItemImageFactory)
# Raises exception on failure.
return h_siif.GetImage(SIZE(icon_size, icon_size), SIIGBF_RESIZETOFIT)
if __name__ == "__main__":
h_bitmap = get_thumbnail(r'C:\test\test.jpg', 64)
print(h_bitmap)
我使用user15566053的解决方案来获取h_bitmap句柄,但无法按照他的建议检索实际图像(使用CreateCompatibleDC,SelectObject...)。
但是,以下方法确实有效:
from PIL import Image
import win32ui
...
h_bitmap = get_thumbnail(r'C:\test\test.jpg', 128)
pyCBitmap = win32ui.CreateBitmapFromHandle(h_bitmap) # Returns a PyCBitmap
info = pyCBitmap.GetInfo() # Get a dictionary with info about the image (including width and height)
data = pyCBitmap.GetBitmapBits(True)
pil_image = Image.frombuffer('RGB', (info['bmWidth'], info['bmHeight']), data, 'raw', 'BGRX', 0, 1)
pil_image.show()
警告:shell32.SHCreateItemFromParsingName() 无法处理带有“/”的文件路径。确保将它们转换为“\”。