设置文件缩略图

作者: admin 分类: 文件格式 发布时间: 2019-09-20 16:27

在公司培训有一段时间了,终于接到了部门任务,需求很简单,就是先Windows Explorer中以缩略图的形式显示自定义的图片,部门提供图片解码代码,我和我的同事实现需求,在实现的过程中走了很多的弯路,犯了不少错误,不过最 终我们还是完成了,通过这次实践我相信的能力又提高了很多,本文不阐述开发过程中遇到了什么问题,只是说明如何实现这个需求的步骤。

   开发语言是C++,工具VC6,需要有COM的知识以及ATL的使用,当然COM和ATL我基本不会,都是在这次任务中学习的,如果跟我一样没有学过COM的,建议先看看《COM本质论》至少把什么是COM以及简单实现原理搞清楚,否则就算实现了,意义也不大。

  其实实现非常简单,只需要3个接口就可以搞定,分别是IPersistFile, IExtractImage和IExtractImage2,每个接口的作用可以查MSDN得到,描述的很清楚,这里就不说了,简单说下原理,假设你的图 片后缀名为.tp,那么当你在资源浏览器中点到.tp的文件,或者使用缩略图显示图片的时候当前文件夹中有.tp的文件,那么系统会请求 IPersistFile接口的Load方法,这里可以得到选择或浏览到的文件名(包括路径),然后请求IExtractImage接口的 GetLocation方法,最后请求IExtractImage的Extract方法,你只需要返回你图片的HBITMAP就可以了,大小都不需要你自 己改变,系统会帮你缩小在显示在资源浏览器上。

  下边是详细的实现步骤:

  1。打开VC6建立一个ATL工程,名字为TPThumbnail,OK and Finish。

  2。选择Insert->New ATL Object。然后选择Simple Object,Next,ShortName写TPeExtract其他的也就会自动填写上了,点OK。

  这里牢骚2句,IExtractImage和IExtractImage2接口在MSDN中说只在VISTA中提供,所以你要想实现这2个接口,必须从VISTA的SDK中把这2个接口的声明弄出来,这点不明白MS怎么想的,为什么不在XP中直接提供。

  3。此时TPeExtract.h是实现IDispatchImpl接口的,这个对我们没什么用处,所以我们把他去掉(CComObjectRootEx 和CComCoClass接口不要删除), COM_INTERFACE_ENTRY(IDispatch)和COM_INTERFACE_ENTRY (ITPeExtract)这个也要删除,然后#include “IExtractImage.h” 头文件,并把这2个实现加上去,然后实现这3个接口的方法就可以了。最后的TPeExtract.h如下:

// TPeExtract.h : Declaration of the CTPeExtract

#ifndef __TPEEXTRACT_H_

#define __TPEEXTRACT_H_

#include “resource.h”       // main symbols

#include “IExtractImage.h”

/////////////////////////////////////////////////////////////////////////////

// CTPeExtract

class ATL_NO_VTABLE CTPeExtract : 

    public CComObjectRootEx<CComSingleThreadModel>,

    public CComCoClass<CTPeExtract, &CLSID_TPeExtract>,

    public IPersistFile,

    public IExtractImage2

{

public:

    CTPeExtract();

DECLARE_REGISTRY_RESOURCEID(IDR_TPEEXTRACT)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CTPeExtract)

    COM_INTERFACE_ENTRY(IPersistFile)

    COM_INTERFACE_ENTRY(IExtractImage)

    COM_INTERFACE_ENTRY(IExtractImage2)

END_COM_MAP()

// IExtractImage

public:

    STDMETHOD(GetLocation)(LPWSTR pszPathBuffer,

        DWORD cchMax,

        DWORD *pdwPriority,

        const SIZE *prgSize,

        DWORD dwRecClrDepth,

        DWORD *pdwFlags);

    STDMETHOD(Extract)(HBITMAP*);

    // IExtractImage2

    STDMETHOD(GetDateStamp)(FILETIME *pDateStamp);

    // IPersistFile

    STDMETHOD(Load)(LPCOLESTR wszFile, DWORD dwMode);

    STDMETHOD(GetClassID)(LPCLSID clsid)

    { return E_NOTIMPL;    }

    STDMETHOD(IsDirty)(VOID)

    { return E_NOTIMPL; }

    STDMETHOD(Save)(LPCOLESTR, BOOL)

    { return E_NOTIMPL; }

    STDMETHOD(SaveCompleted)(LPCOLESTR)

    { return E_NOTIMPL; }

    STDMETHOD(GetCurFile)(LPOLESTR FAR*)

    { return E_NOTIMPL; }

};

#endif //__TPEEXTRACT_H_

  当然现在是编译不通过的,因为有些方法没实现,其中象Save, GetCurFile等等在本例中没有用到。所以直接返回E_NOTIMPL就可以了。

  4。然后增加1个成员变量TCHAR m_szFileName[MAX_PATH]; 这个是保存文件名用的。

  5。在构造函数中初始化szFileName。

CTPeExtract::CTPeExtract()

{

    ZeroMemory(m_szFileName, MAX_PATH);

}

  6。现在实现Load方法,这个方法只保存传入的文件名,代码如下:

HRESULT CTPeExtract::Load(LPCOLESTR wszFile, DWORD dwMode)

{

    USES_CONVERSION;

    _tcscpy(m_szFileName, OLE2T((WCHAR*)wszFile)); 

    return S_OK;    

};

   7。实现GetLocation。

HRESULT CTPeExtract::GetLocation(LPWSTR pszPathBuffer,

                                        DWORD cchMax, DWORD *pdwPriority,

                                        const SIZE *prgSize, DWORD dwRecClrDepth,

                                        DWORD *pdwFlags)

{

    if (*pdwFlags & IEIFLAG_ASYNC)    

        return E_PENDING; 

    return NOERROR;

}

  有人会问为什么return E_PENDING;这个你看MSDN就知道,让是XP or later的系统,就必须这样做。

  8。重要的函数Extract的实现,这个函数是系统问你要你这个图片的HBITMAP,给他就是了,这里就写你图片的解码,由于是例子所以我直接加载个资源就可以了。

HRESULT CTPeExtract::Extract(HBITMAP* phBmpThumbnail)


    HBITMAP hBM = 0;

    hBM = ::LoadBitmap(_Module.GetModuleInstance(),MAKEINTRESOURCE(IDB_BITMAP1)); 

    if (0 != hBM)

        *phBmpThumbnail = hBM;

    return NOERROR; 

}

  9。最后是GetDateStamp函数的实现,这个是IExtractImage2的函数,具体作用看MSND。

HRESULT CTPeExtract::GetDateStamp(FILETIME *pDateStamp)

{

    FILETIME ftCreationTime,ftLastAccessTime,ftLastWriteTime;

    HANDLE hFile = CreateFile(m_szFileName,GENERIC_READ,FILE_SHARE_READ,NULL,

        OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

    if(!hFile)

        return E_FAIL;

    GetFileTime(hFile,&ftCreationTime,&ftLastAccessTime,&ftLastWriteTime);

    CloseHandle(hFile);

    *pDateStamp = ftLastWriteTime;

    return NOERROR; 

}

这个是编译就可以通过了。  TPThumbnail.dll – 0 error(s), 0 warning(s)

但是,光这样还不够,还需要最重要的一步,就是这个组件注册到合适的地方。这样修改Resource Files中的TPeExtract.rgs文件。修改后如下:

HKCR

{

    TPThumbnail.TPeExtract.1 = s ‘TPeExtract Class’

    {

        CLSID = s ‘{E45A903D-53AE-4A14-906E-0260D13FF77F}’

    }

    TPThumbnail.TPeExtract = s ‘TPeExtract Class’

    {

        CLSID = s ‘{E45A903D-53AE-4A14-906E-0260D13FF77F}’

        CurVer = s ‘TPThumbnail.TPeExtract.1’

    }

    NoRemove CLSID

    {

        ForceRemove {E45A903D-53AE-4A14-906E-0260D13FF77F} = s ‘TPeExtract Class’

        {

            ProgID = s ‘TPThumbnail.TPeExtract.1’

            VersionIndependentProgID = s ‘TPThumbnail.TPeExtract’

            ForceRemove ‘Programmable’

            InprocServer32 = s ‘%MODULE%’

            {

                val ThreadingModel = s ‘Apartment’

            }

            ‘TypeLib’ = s ‘{F473925C-FDEE-42AD-9FC2-C843DB7BA2B2}’

        }

    }

    NoRemove .tp

    {

        shellex

        {

            {BB2E617C-0920-11d1-9A0B-00C04FC2D6C1} = s ‘{E45A903D-53AE-4A14-906E-0260D13FF77F}’

        }

    }

    NoRemove .1

    {

        shellex

        {

            {BB2E617C-0920-11d1-9A0B-00C04FC2D6C1} = s ‘{E45A903D-53AE-4A14-906E-0260D13FF77F}’

        }

    }

}

  这里的 .tp 就是关联的文件后缀名,BB2E617C-0920-11d1-9A0B-00C04FC2D6C1是IUnknow接口的CLSID,E45A903D -53AE-4A14-906E-0260D13FF77F是本例的CLSID,如果你在开发中需要修改这个CLSID就可以了。

  编译, PASS~~~

  现在需要注册这个COM组件,方法很简单。开始-运行-cmd。

  注册: regsvr32 编译出来的DLL

  反注册,就是移除: regsvr32 /u 编译出来的DLL

  注册和反注册都会有提示的,这个时候你在任意文件夹下建一个后缀名为.tp的文件就可以看到效果了,再切换到缩略图看看。

最后再次感谢帮助我的人,还有和我一起开发的同事,他在整个开发中给我提出了很多中肯的意见以及帮助。

  (本代码只是例子,并没有做详尽的测试)

本例子代码下载地址: http://download.csdn.net/source/392403

  • iUserNameYLMcsdn: 很好,就是只能在XP下实现,在Win7下要使用新的接口IThumbnailProvider(4年前#4楼)
  • 茜斯托洛夫斯基: IUnknow接口的CLSID需要改吗?是固定的吗?怎么查到呢…… 我现在遇到的问题是只有在我的测试程序中,当我点击打开文件时,能显示缩略图,可是在windows下随便打开一个文件夹,都无法显示缩略图……为什么会这样?……是不是我注册的不对?……求大神帮助……(4年前#3楼)
  • 茜斯托洛夫斯基: BB2E617C-0920-11d1-9A0B-00C04FC2D6C1是IUnknow接口的CLSID 楼主,上面这句话怎么理解?从那儿能查到IUnknow接口的CLSID? 非常感谢~(4年前#2楼)

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!

发表评论

标签云