前言
??最近在開發(fā)工作中感混,接到一個需求旦装,要在Windows資源管理器中顯示自定義文件格式的縮略圖和預(yù)覽圖吠谢,由于沒有接觸過 Shell 加上網(wǎng)上資源較少,經(jīng)過一番努力終于實現(xiàn)了需要的功能唯袄,遂記錄一下弯屈。
介紹
??根據(jù)官方文檔介紹,不同的操作系統(tǒng)提供了不同的解決方案恋拷。Windows XP 操作系統(tǒng) 通過繼承 IExtractImage 或 IExtractImage2 接口實現(xiàn)縮略圖功能资厉。Windows Vista 及以上操作系統(tǒng) ,提供更簡單易用的 IThumbnailProivder 接口來代替之前的 IExtractImage 和 IExtractImage2 接口蔬顾,但后兩個接口現(xiàn)在仍然可以使用宴偿。
??PS:IExtractImage2 繼承至 IExtractImage,比后者多了一個 GetDateStamp 方法诀豁,通過重寫這個方法窄刘,允許 Shell 確定緩存的圖像是否已經(jīng)過期。
實現(xiàn)過程
Windows XP
??要實現(xiàn)縮略圖功能舷胜,必須繼承IExtractImage 或 IExtractImage2 接口和下列三個接口中的一個并重寫GetLocation娩践、Extract 和 Load 接口:
例子
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接口和下列三個接口中的一個并重寫 Initialize 和 GetThumbnail 接口:
- 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.
};
說明:主要是重寫 Initialize 和 GetThumbnail 兩個函數(shù)姐浮,在 Initialize 完成初始化,在 GetThumbnail 完成自定義文件的解析化戳,base64 的解碼单料,HBITMAP 的構(gòu)建。