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 接口和下列三個接口中的一個并重寫GetLocation娩践、ExtractLoad 接口:

例子

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
    

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

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)系作者
  • 序言:七十年代末点楼,一起剝皮案震驚了整個濱河市扫尖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掠廓,老刑警劉巖换怖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蟀瞧,居然都是意外死亡沉颂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門悦污,熙熙樓的掌柜王于貴愁眉苦臉地迎上來铸屉,“玉大人,你說我怎么就攤上這事切端〕固常” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵踏枣,是天一觀的道長昌屉。 經(jīng)常有香客問我,道長茵瀑,這世上最難降的妖魔是什么间驮? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮马昨,結(jié)果婚禮上竞帽,老公的妹妹穿的比我還像新娘。我一直安慰自己鸿捧,他們只是感情好抢呆,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著笛谦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪昌阿。 梳的紋絲不亂的頭發(fā)上饥脑,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天恳邀,我揣著相機與錄音,去河邊找鬼灶轰。 笑死谣沸,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的笋颤。 我是一名探鬼主播乳附,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伴澄!你這毒婦竟也來了赋除?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤非凌,失蹤者是張志新(化名)和其女友劉穎举农,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敞嗡,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡颁糟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了喉悴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棱貌。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖箕肃,靈堂內(nèi)的尸體忽然破棺而出婚脱,到底是詐尸還是另有隱情,我是刑警寧澤突雪,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布起惕,位于F島的核電站,受9級特大地震影響咏删,放射性物質(zhì)發(fā)生泄漏惹想。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一督函、第九天 我趴在偏房一處隱蔽的房頂上張望嘀粱。 院中可真熱鬧,春花似錦辰狡、人聲如沸锋叨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娃磺。三九已至,卻和暖如春叫倍,著一層夾襖步出監(jiān)牢的瞬間偷卧,已是汗流浹背豺瘤。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留听诸,地道東北人坐求。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像晌梨,于是被迫代替她去往敵國和親桥嗤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354