引言
前面的一篇博文中總結了開發Windows Thumbnail Handler的一些經驗。在公司實際項目中,需要同時針對圖片和視頻實現縮略圖。同時還要在圖片和視頻文件的頂部加上LOGO。像如下這樣的:
於是考慮了一下實現方案:
(1)LOGO資源采用Base64編碼編譯到DLL中去
(2)公司自有的圖片和視頻文件進行全景拼接時依賴一串參數,而這串參數需要從文件中提取。因此采用
RecipeThumbnailProvider實現
IInitializeWithFile接口比較合適,這樣能得到文件路徑,具備更靈活的可操作性。
(3)LOGO資源使用Windows自帶的解碼庫來進行解碼,也就是Windows Image Component(
WIC).
這么一思考,還是挺靠譜的。於是就動手開始編碼,編寫了一個解碼LOGO資源的函數:
/** * Decode the Base64-encoded string to get logo resources. */ HRESULT RecipeThumbnailProvider::GetLogoFromString(LPCWSTR encodedString, UINT* width, UINT* height, PBYTE* rawPixels) { IStream* pImageStream = NULL; HRESULT hr = E_FAIL; DWORD dwDecodedImageSize = 0; DWORD dwSkipChars = 0; DWORD dwActualFormat = 0; if (CryptStringToBinary(encodedString, NULL, CRYPT_STRING_BASE64, NULL, &dwDecodedImageSize, &dwSkipChars, &dwActualFormat)) { BYTE* pbDecodedImage = static_cast<BYTE*>(LocalAlloc(LPTR, dwDecodedImageSize)); if (pbDecodedImage) { if (CryptStringToBinary(encodedString, lstrlen(encodedString), CRYPT_STRING_BASE64, pbDecodedImage, &dwDecodedImageSize, &dwSkipChars, &dwActualFormat)) { pImageStream = SHCreateMemStream(pbDecodedImage, dwDecodedImageSize); if (pImageStream != NULL) { IWICImagingFactory* pImageFactory; hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pImageFactory)); LOGINFO(L"CoCreateIntance() returns 0x%x", hr); if (SUCCEEDED(hr)) { IWICBitmapDecoder* pDecoder; hr = pImageFactory->CreateDecoderFromStream(pImageStream, &GUID_VendorMicrosoft, WICDecodeMetadataCacheOnDemand, &pDecoder); if (SUCCEEDED(hr)) { IWICBitmapFrameDecode* pBitmapFrameDecode; hr = pDecoder->GetFrame(0, &pBitmapFrameDecode); if (SUCCEEDED(hr)) { IWICBitmapSource* pBitmapSourceConverted = NULL; WICPixelFormatGUID guidPixelFormatSource; hr = pBitmapFrameDecode->GetPixelFormat(&guidPixelFormatSource); if (SUCCEEDED(hr) && (guidPixelFormatSource != GUID_WICPixelFormat24bppBGR)) { IWICFormatConverter* pFormatConverter; hr = pImageFactory->CreateFormatConverter(&pFormatConverter); if (SUCCEEDED(hr)) { hr = pFormatConverter->Initialize(pBitmapFrameDecode, GUID_WICPixelFormat24bppBGR, WICBitmapDitherTypeNone, NULL, 0, WICBitmapPaletteTypeCustom); if (SUCCEEDED(hr)) { hr = pFormatConverter->QueryInterface(&pBitmapSourceConverted); } pFormatConverter->Release(); } } else { hr = pBitmapFrameDecode->QueryInterface(&pBitmapSourceConverted); } if (SUCCEEDED(hr)) { hr = pBitmapSourceConverted->GetSize(width, height); if (SUCCEEDED(hr)) { WICRect rect = { 0, 0, *width, *height }; *rawPixels = static_cast<BYTE*>(LocalAlloc(LPTR, (*width)*(*height)*3)); hr = pBitmapSourceConverted->CopyPixels(&rect, (*width) * 3, (*width)*(*height) * 3, *rawPixels); } else { *width = 0; *height = 0; *rawPixels = NULL; } pBitmapSourceConverted->Release(); } pBitmapFrameDecode->Release(); } pDecoder->Release(); } pImageFactory->Release(); } pImageStream->Release(); } } } LocalFree(pbDecodedImage); } return hr; }
當我注冊好COM組件開始使用時,在本機上測試簡直完美。滿以為就這么搞定了,然而並么有。在另外一台Win7機器上測試時,縮略圖中並沒有出現想象中的LOGO。一看日志文件,發現一直在報:CoCreateInstance()調用返回0x80040154。於是下面的代碼都沒執行,LOGO資源自然沒有加載成功了。那么CoCreateInstance()為啥會返回0x80040154呢?這個代碼又意味着什么嗯?從網上的搜索結果來看,0x80040154是表示"Class Not Registered"。也就是說COM類並沒有注冊,在注冊表\HKEY_CLASSES_ROOT\CLSID\下面也就沒有類ID了。我們程序中使用了WIC組件來解碼圖片,那么難道是WIC組件類沒有注冊嗎?
再一想,開發時采用的一直是Windows10,可以正常運行。到了Windows7上為啥就不行了呢?難道是WIC在Windows7上不支持?這個懷疑顯然是不成立的,從MSDN上來看從XP SP2就開始支持了啊:

那么難道是參數給的不對?以CLSID_WICImagingFactory為關鍵字一搜索果然搜到了
一篇帖子:
CLSID_WICImagingFactory在Windows10上被解析為了
CLSID_WICImagingFactory2:

而這個GUID在Windows7上是不存在的(搜索注冊表即可看到結果):


自然CoCreateInstance()調用就會返回0x80040154了。解決方案就是傳遞
CLSID_WICImagingFactory1給CoCreateInstance()。這樣就能同時兼容Windows10和Windows7了。
參考鏈接