如何通过python从windows缓存中获取文件缩略图?

问题描述 投票:0回答:2

目标是在尽可能短的时间内从 Windows 上选定的文件夹中获取文件缩略图。

Imagemagick 和其他工具每次都会打开文件,因此它们可能不是最高效的。 Windows 中的内置机制可以将缩略图缓存在缓存中,理论上这会带来相当大的速度提升。

如何使用Windows API获取文件的缩略图?answer),方法变得清晰,但有很多问题:

  • IThumbnailProviderIThumbnailCache 未在以下位置找到:
    1. ctypes.windll.shell32(只有函数,似乎没有简单的方法获取接口)
    2. win32com.shell.shell(PyWin32)
  • IID_IThumbnailProviderIShellItemImageFactory 通过 SHCreateItemFromParsingName 和 IID:
    1. 在 win32com.shell.shell 中找不到 IID,因此它们是手动提供的。

    2. IThumbnailProviderIShellItem::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'
      
    3. 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。又如何?

python winapi ctypes thumbnails pywin32
2个回答
1
投票

这里有一些基于 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)

0
投票

我使用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() 无法处理带有“/”的文件路径。确保将它们转换为“\”。

© www.soinside.com 2019 - 2024. All rights reserved.