系列专题:【附源码】Windows Shell接口之VB实现(三):IThumbnailProvider接口 (缩略图),该如何解决

系列专题:【附源码】Windows Shell接口之VB实现(三):IThumbnailProvider接口 (缩略图)
【源码下载地址】:http://econet.zjgsu.edu.cn/cy_filesxxx/vbsrc/ThumbnailProvider.rar

我们知道,在Windows XP中,系统会在每个需要产生缩略图的文件夹中创建一个名为thumb.db的缩略图数据库文件。但是,这种以文件夹为单位维护手段无法为我们提供预览或跨文件夹的查询的功能。针对这些缺点,MS在Windows Vista中对缩略图功能做了比较大的调整,将权利从地方收回中央,建立了一个全局的缩略图缓存。打开 "%SystemDrive%\Users\%UserName%\AppData\Local\Microsoft\Windows\Explorer" 文件夹,你会发现若干个名为thumbcache_*.*的文件。这些文件名后面标着数字32、96、256、1024的db文件就是保存了系统中相应尺寸缩略图(分别对应资源管理器“视图”中的“小图标”、“中等图标”、“大图标”和“特大图标”)的缓存数据库。根据新的设计,我们在Vista中可以不受限制的为每一个单独的文件提供任意大小、任意内容的缩略图,完全摆脱了以往那种只能提供几个特定尺寸的死板约束。
Windows Vista用户界面中常用的是32位256x256的缩略图。根据微软的文档的建议,当你使用自己自定义格式的文件时,最好能同时为其提供上述规格的动态缩略图,以随时反映出特定文件的内容。有鉴于此,Vista中引入了全新的接口IThumbnailProvider,以一种更加简洁方便的方式替代以前的IExtractImage帮助我们完成提供缩略图的工作。当然,为了保持兼容,IExtractImage在Vista中依旧可用,只是资源管理器中的“详细信息面板”不支持该接口。
缩略图提供器(ThumbnailProvider)主要由两个接口组成,除了实现IThumbnailProvider之外,你还必须要实现IInitializeWithStream或IInitializeWithItem接口,以获得目标文件的信息。系统在调用你的IThumbnailProvider之前会依照顺序先后尝试调用这两个接口。如果你已实现了IInitializeWithStream或已同时实现了两个接口,系统将会调用IInitializeWithStream.Initialize方法将代表目标文件内容的流对象通过第一个参数(pstream)传到你的提供器程序中。IInitializeWithItem当然也是可以使用的,通过IInitializeWithItem.Initialize传入的是代表目标文件的IShellItem对象。但是,MS强烈建议我们不要使用这个接口,如果你实在要使用它的话,请通过在HKEY_CLASSES_ROOT\CLSID\{缩略图提供器类的CLSID}下新建一个名为DisableProcessIsolation、值为1、类型为REG_DWORD的键,取消你提供器组件的进程隔离特性(process isolation feature)。另外,MS的文档中还提到了另一个接口IinitializeWithFile,但是不知道什么原因,似乎系统从来不会访问它。
介绍了有关缩略图以及缩略图提供器(ThumbnailProvider)的前世今生后,下面就让我们动手,使用Visual Basic 6创建一个我们自己的缩略图提供器(ThumbnailProvider)吧。
假设你某天早晨刷牙时,突然想到为自己的新软件设计一种文本格式的自定义文件,并给它取了一个很响亮的扩展名.myext。而现在你想为它写一个能够用于显示特定文件内容的缩略图提供器。
就像所有其他的接口编程一样,在开始之前我们得有一个类型库,一个包含了IThumbnailProvider、IInitializeWithStream以及IStream声明的类型库。这一步我已经帮你做好,示例文件中的ThumbnailProvider.tlb应该可以满足你的要求。
接着打开你的VB6 IDE,新建一个ActiveX DLL工程(比如叫MyThumbnailProvider),并添加一个"Instancing=5 - MultiUse"的公共类(比如叫CMyThumbnailProvider)。引用前面提到的类型库ThumbnailProvider.tlb。
OK,现在万事俱备,你可以开始写你的缩略图提供器了。
首先,实现两个接口:
VB code
Implements IInitializeWithStream
Implements IThumbnailProvider


根据MS文档的建议,我们选择实现最可靠的IInitializeWithStream接口,并在其Initialize方法中将传入的IStream对象保存起来:
VB code
Private Sub IInitializeWithStream_Initialize(ByVal pstream As ThumbnailProvider.IStream, ByVal grfMode As Long)
    If pstream Is Nothing Then
        Err.Raise E_NOTIMPL
    Else
        Set m_pStream = pstream
    End If
End Sub

接下来是IThumbnailProvider的GetThumbnail方法,系统会调用此方法以获得相应的缩略图。
Private Sub IThumbnailProvider_GetThumbnail(ByVal cx As Long, phbmp As Long, pdwAlpha As WTS_ALPHATYPE)
    Dim sFileName As String
    Dim st As STATSTG
    
    '取得IStream对象的信息
    m_pStream.Stat st
    
    '获取IStream对象的名称信息
    If st.pwcsName <> 0 Then
        sFileName = StrFromPtr(st.pwcsName, True)
        CoTaskMemFree st.pwcsName
    End If
    
    '获得流对象中的字节数据大小
    Dim lFileSize As Long
    lFileSize = IntegerFromCurrency(st.cbSize)
    
    If lFileSize <= 0 Then Exit Sub
    
    Dim sBuffer As String
    Dim abBuffer() As Byte
    ReDim abBuffer(lFileSize - 1)
    
    '读取所有数据
    If m_pStream.Read(abBuffer(0), lFileSize) <= 0 Then Exit Sub
    
    sBuffer = StrConv(abBuffer, vbUnicode)
    
    '创建缩略图
    phbmp = CreateThumbnail(sBuffer)
End Sub


从上面的例程可以看到,提供缩略图的这个过程其实并不复杂,而我这里仅仅是很简单的取出文件内容,并使用API DrawText将其写到新建的Bitmap对象中。我相信,能在刷牙时灵光乍现、聪明绝顶的你,肯定可以想出N种惊天地泣鬼神的方法来绘制各种神奇的缩略图。
以上的例程编译后就是一个个头虽小,但却五脏俱全的缩略图提供器了,要让它真正的运作起来,我们还必须将其与你响亮的扩展名.myext联系起来。具体的方法就是建立如下的注册表项:

HKEY_CLASSES_ROOT\.myext
HKEY_CLASSES_ROOT\.myext\ShellEx
HKEY_CLASSES_ROOT\.myext\ShellEx\ {E357FCCD-A995-4576-B01F-234630154E96}

其中,“{E357FCCD-A995-4576-B01F-234630154E96}”就是IThumbnailProvider 的接口ID——IID_IThumbnailProvider。建立好上面的注册表项之后,还要将HKEY_CLASSES_ROOT\.myext\ShellEx\ {E357FCCD-A995-4576-B01F-234630154E96}的默认值设为你所编写的缩略图提供器的类ID。