Windows资源管理器中自定义文件格式显示缩略图和预览图功能(C++)

前言

  最近在开发工作中,接到一个需求,要在Windows资源管理器中显示自定义文件格式缩略图预览图,由于没有接触过 Shell 加上网上资源较少,经过一番努力终于实现了需要的功能,遂记录一下。

介绍

  根据官方文档介绍,不同的操作系统提供了不同的解决方案。Windows XP 操作系统 通过继承 IExtractImageIExtractImage2 接口实现缩略图功能。Windows Vista 及以上操作系统 ,提供更简单易用的 IThumbnailProivder 接口来代替之前的 IExtractImageIExtractImage2 接口,但后两个接口现在仍然可以使用。
  PSIExtractImage2 继承至 IExtractImage,比后者多了一个 GetDateStamp 方法,通过重写这个方法,允许 Shell 确定缓存的图像是否已经过期。

实现过程

Windows XP

  要实现缩略图功能,必须继承IExtractImageIExtractImage2 接口和下列三个接口中的一个并重写GetLocationExtractLoad 接口:

例子

HRESULT CTPeExtract::Load(LPCOLESTR pszFileName, DWORD dwMode)
{   
    USES_CONVERSION;
    _tcscpy_s(m_szFileName, OLE2T((WCHAR*)pszFileName));

    return S_OK;
};

说明:重写Load函数,在函数中保存自定义文件的路径。

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;
}

说明:重写GetLocation函数。

HRESULT CTPeExtract::Extract(HBITMAP* phBmpThumbnail)
{
    /// 解析自定义文件
    tinyxml2::XMLDocument doc;
    doc.LoadFile(m_szFileName);
    XMLElement *pRoot = doc.RootElement();
    const char* cSource = pRoot->FirstChildElement("Attachments")
        ->FirstChildElement("Picture")->Attribute("source");

    /// base64解码
    int datalen(0);
    DWORD dwritelen(0);
    std::string strdcode = CBase64::Base64Decode(cSource, strlen(cSource), datalen);

    /// 字符串转换成字节流
    int iLength = strdcode.length();
    BYTE* pBuffer = new BYTE[iLength + 1];
    for (int i = 0; i < iLength; ++i)
    {
        pBuffer[i] = strdcode[i];
    }

    /// 字节流转换成HBITMAP
    *phBmpThumbnail = ConvertDibToHBitmap(pBuffer);

    delete[] pBuffer;

    return NOERROR;
}

说明:重写Extract函数,在函数中实现自定义文件的解析(主要是解析出自定义文件中缩略图的base64编码),根据缩略图的base64解码构建字节流,然后将字节流转换成HBITMAP

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;
}

说明:重写GetDateStamp函数。

HBITMAP CTPeExtract::ConvertDibToHBitmap(void* bmpData)
{
    HBITMAP hBitmap = NULL;
    BOOL success = FALSE;

    LPBITMAPFILEHEADER bfh = (LPBITMAPFILEHEADER)bmpData;
    LPBITMAPINFOHEADER bih = (LPBITMAPINFOHEADER)(bfh + 1);
    void* pixels = (char*)(bih + 1); // NOTE: Assumes no color table (i.e., bpp >= 24) 

    HDC hdc = GetDC(NULL);
    if (NULL == hdc)
    {
        return NULL;
    }

    hBitmap = CreateCompatibleBitmap(hdc, bih->biWidth, bih->biHeight);
    if (NULL == hBitmap)
    {
        return NULL;
    }

    HDC hdcMem = CreateCompatibleDC(hdc);
    if (NULL == hdcMem)
    {
        return NULL;
    }

    HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, hBitmap);
    if (StretchDIBits(hdcMem, 0, 0, bih->biWidth, bih->biHeight,
        0, 0, bih->biWidth, bih->biHeight, pixels,
        (LPBITMAPINFO)bih, DIB_RGB_COLORS, SRCCOPY) > 0)
    {
        success = TRUE;
    }

    SelectObject(hdcMem, hOldBitmap);
    DeleteDC(hdcMem);

    ReleaseDC(NULL, hdc);
    
    if (!success && hBitmap != NULL)
    {
        DeleteObject(hBitmap);
        hBitmap = NULL;
    }

    return hBitmap;
}

说明:位图字节流转HBITMAP函数。

Windows Vista 及以上版本

  要实现缩略图功能,必须继承IThumbnailProivder接口和下列三个接口中的一个并重写 InitializeGetThumbnail 接口:

  • IInitializeWithStream官方推荐继承这个接口,增强安全性和稳定性
  • IInitializeWithItem
  • IInitializeWithFile
    注意:如果不是继承自 IInitializeWithStream 接口,则必须设置下列的注册表值:
    HKEY_CLASSES_ROOT
        CLSID
            {The CLSID of your thumbnail handler}
                DisableProcessIsolation = 1
    

例子(详细例子见参考中的官方例子)

class CTestThumbnail : public IInitializeWithStream,
    public IThumbnailProvider
{
public:
    CTestThumbnail() : _cRef(1), _pStream(NULL)
    {
    }

    virtual ~CTestThumbnail()
    {
        if (_pStream)
        {
            _pStream->Release();
        }
    }

    // IUnknown
    IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
    {
        static const QITAB qit[] =
        {
            QITABENT(CTestThumbnail, IInitializeWithStream),
            QITABENT(CTestThumbnail, IThumbnailProvider),
            { 0 },
        };
        return QISearch(this, qit, riid, ppv);
    }

    IFACEMETHODIMP_(ULONG) AddRef()
    {
        return InterlockedIncrement(&_cRef);
    }

    IFACEMETHODIMP_(ULONG) Release()
    {
        ULONG cRef = InterlockedDecrement(&_cRef);
        if (!cRef)
        {
            delete this;
        }
        return cRef;
    }

    // IInitializeWithStream
    IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode);

    // IThumbnailProvider
    IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha);

private:
    HRESULT _LoadXMLDocument(IXMLDOMDocument **ppXMLDoc);
    HRESULT _GetBase64EncodedImageString(UINT cx, PWSTR *ppszResult);
    HRESULT _GetStreamFromString(PCWSTR pszImageName, IStream **ppStream);

    long _cRef;
    IStream *_pStream;     // provided during initialization.
};

说明:主要是重写 InitializeGetThumbnail 两个函数,在 Initialize 完成初始化,在 GetThumbnail 完成自定义文件的解析,base64 的解码,HBITMAP 的构建。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。