Windows資源管理器中自定義文件格式顯示縮略圖和預(yù)覽圖功能(C++)

前言

??最近在開發(fā)工作中,接到一個需求,要在Windows資源管理器中顯示自定義文件格式縮略圖預(yù)覽圖,由于沒有接觸過 Shell 加上網(wǎng)上資源較少,經(jīng)過一番努力終于實現(xiàn)了需要的功能,遂記錄一下。

介紹

??根據(jù)官方文檔介紹,不同的操作系統(tǒng)提供了不同的解決方案。Windows XP 操作系統(tǒng) 通過繼承 IExtractImageIExtractImage2 接口實現(xiàn)縮略圖功能。Windows Vista 及以上操作系統(tǒng) ,提供更簡單易用的 IThumbnailProivder 接口來代替之前的 IExtractImageIExtractImage2 接口,但后兩個接口現(xiàn)在仍然可以使用。
??PSIExtractImage2 繼承至 IExtractImage,比后者多了一個 GetDateStamp 方法,通過重寫這個方法,允許 Shell 確定緩存的圖像是否已經(jīng)過期。

實現(xiàn)過程

Windows XP

??要實現(xiàn)縮略圖功能,必須繼承IExtractImageIExtractImage2 接口和下列三個接口中的一個并重寫GetLocationExtractLoad 接口:

例子

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

    return S_OK;
};

說明:重寫Load函數(shù),在函數(shù)中保存自定義文件的路徑。

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函數(shù)。

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

    /// 字符串轉(zhuǎn)換成字節(jié)流
    int iLength = strdcode.length();
    BYTE* pBuffer = new BYTE[iLength + 1];
    for (int i = 0; i < iLength; ++i)
    {
        pBuffer[i] = strdcode[i];
    }

    /// 字節(jié)流轉(zhuǎn)換成HBITMAP
    *phBmpThumbnail = ConvertDibToHBitmap(pBuffer);

    delete[] pBuffer;

    return NOERROR;
}

說明:重寫Extract函數(shù),在函數(shù)中實現(xiàn)自定義文件的解析(主要是解析出自定義文件中縮略圖的base64編碼),根據(jù)縮略圖的base64解碼構(gòu)建字節(jié)流,然后將字節(jié)流轉(zhuǎn)換成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函數(shù)。

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

說明:位圖字節(jié)流轉(zhuǎn)HBITMAP函數(shù)。

Windows Vista 及以上版本

??要實現(xiàn)縮略圖功能,必須繼承IThumbnailProivder接口和下列三個接口中的一個并重寫 InitializeGetThumbnail 接口:

  • IInitializeWithStream官方推薦繼承這個接口,增強安全性和穩(wěn)定性
  • IInitializeWithItem
  • IInitializeWithFile
    注意:如果不是繼承自 IInitializeWithStream 接口,則必須設(shè)置下列的注冊表值:
    HKEY_CLASSES_ROOT
        CLSID
            {The CLSID of your thumbnail handler}
                DisableProcessIsolation = 1
    

例子(詳細(xì)例子見參考中的官方例子)

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 兩個函數(shù),在 Initialize 完成初始化,在 GetThumbnail 完成自定義文件的解析,base64 的解碼,HBITMAP 的構(gòu)建。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,791評論 6 545
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,795評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,943評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,057評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,773評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,106評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,082評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,282評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,793評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,507評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,741評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,220評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,929評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,325評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,661評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,482評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,702評論 2 380