基于IPreviewHandler实现文件预览窗格图片预览的问题求助
基于IPreviewHandler的图片预览DLL优化方案
背景
我正尝试使用IPreviewHandler创建一个能在文件预览窗格中预览图片的DLL,开发基于微软官方的RecipePreviewHandler示例。
当前方案的问题
- 调整HBITMAP大小必须重新加载,效率极低;
- 无法根据点击的文件动态切换位图路径,期望提取文件名并匹配同名字文件夹中的位图。
理想效果
类似VLC预览窗格,同时显示视频预览图与软件标识。
现有实现代码
loadPreview() 加载位图方法
boolean CRecipePreviewHandler::loadPreview(void) //*Description // change the bitmap control part of this to show the preview // from the file associated with m_bitmapFile { boolean r = TRUE; // Find dimension of bitmap std::ifstream file; file.open(m_bitmapFile, std::ios::binary); if (file.fail()) { // File does not exist file.close(); return r; } BITMAPINFOHEADER bmiHeader; file.seekg(sizeof(BITMAPFILEHEADER)); file.read((char*)&bmiHeader, sizeof(BITMAPINFOHEADER)); file.close(); UINT cx = bmiHeader.biWidth; UINT cy = bmiHeader.biHeight; if (cx == 0 || cy == 0) { return r; } // transfer picture to dialog control m_b = (HBITMAP)LoadImage(NULL, m_bitmapFile, IMAGE_BITMAP, cx, cy, LR_LOADFROMFILE); r = FALSE; return r; }
其中m_b为HBITMAP类型,m_bitmapFile为存储位图路径的LPCWSTR类型。
DoPreview() 预览处理方法
HRESULT CRecipePreviewHandler::DoPreview() { HRESULT hr = E_FAIL; if (_hwndPreview == NULL && _pStream) // cannot call more than once (Unload should be called before another DoPreview) { int border = 0; // Create the preview window _hwndPreview = CreateWindowExW(0, L"STATIC", NULL, SS_BITMAP | SS_CENTERIMAGE | SS_REALSIZECONTROL | WS_CHILDWINDOW, // Always create read-only _rcParent.left+border, _rcParent.top+border, RECTWIDTH(_rcParent)+border, RECTHEIGHT(_rcParent)+border, _hwndParent, NULL, NULL, NULL); if (_hwndPreview == NULL) { hr = HRESULT_FROM_WIN32(GetLastError()); } else { if (loadPreview()) { hr = HRESULT_FROM_WIN32(GetLastError()); } else { SendMessage(_hwndPreview, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)m_b); ShowWindow(_hwndPreview, SW_SHOW); } } } return hr; }
其余代码包含初始化、反初始化及矩形大小设置器,目前因性能问题未调整位图大小。
优化方案
1. 动态缩放位图,避免重复加载
放弃依赖LoadImage的大小参数,改用GDI+加载原始位图并在内存中动态缩放,窗口尺寸变化时仅需重新渲染,无需重读文件:
- 在预览Handler初始化时启动GDI+,将原始位图加载为
Image对象并保存为成员变量; - 窗口大小变化时(通过
SetRect触发),根据预览窗口尺寸计算缩放比例,绘制缩放后的图像到窗口DC。
示例代码片段:
// 初始化GDI+(Handler初始化时调用) Gdiplus::GdiplusStartupInput gdiplusInput; ULONG_PTR gdiplusToken; Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusInput, NULL); // 加载原始位图到Image对象 Gdiplus::Image* m_pOriginalImage = Gdiplus::Image::FromFile(m_bitmapFile); // 缩放绘制到窗口 void CRecipePreviewHandler::DrawScaledPreview(HWND hWnd) { if (!m_pOriginalImage) return; HDC hdc = GetDC(hWnd); RECT rc; GetClientRect(hWnd, &rc); Gdiplus::Graphics graphics(hdc); // 保持比例缩放 float scaleX = (float)rc.right / m_pOriginalImage->GetWidth(); float scaleY = (float)rc.bottom / m_pOriginalImage->GetHeight(); float scale = min(scaleX, scaleY); int drawW = (int)(m_pOriginalImage->GetWidth() * scale); int drawH = (int)(m_pOriginalImage->GetHeight() * scale); int x = (rc.right - drawW) / 2; int y = (rc.bottom - drawH) / 2; graphics.DrawImage(m_pOriginalImage, x, y, drawW, drawH); ReleaseDC(hWnd, hdc); }
之后在SetRect方法中调用DrawScaledPreview,并将静态控件替换为自绘窗口(处理WM_PAINT消息)。
2. 动态获取当前文件路径,匹配对应位图
通过IPreviewHandler的SetStream方法提取当前文件路径,生成对应位图路径:
- 调用
IStream::Stat获取文件的STATSTG结构体,从中提取文件名; - 截取文件名(不含扩展名),拼接同名字文件夹的位图路径,替换硬编码的
m_bitmapFile。
示例代码片段:
HRESULT CRecipePreviewHandler::SetStream(IStream* pStream, REFIID riid) { HRESULT hr = IPreviewHandler::SetStream(pStream, riid); if (SUCCEEDED(hr)) { STATSTG stat; hr = pStream->Stat(&stat, STATFLAG_NONAME); if (SUCCEEDED(hr)) { CStringW filePath(stat.pwcsName); CoTaskMemFree(stat.pwcsName); // 提取无扩展名的文件名 CStringW fileName = filePath.Mid(filePath.ReverseFind(L'\\') + 1); fileName = fileName.Left(fileName.ReverseFind(L'.')); // 拼接同名字文件夹的位图路径 CStringW bitmapPath = filePath.Left(filePath.ReverseFind(L'\\') + 1); bitmapPath += fileName; bitmapPath += L"\\preview.bmp"; // 自定义位图文件名 // 更新成员变量 if (m_bitmapFile) CoTaskMemFree(m_bitmapFile); m_bitmapFile = bitmapPath.AllocSysString(); } } return hr; }
注意在Unload方法中释放m_bitmapFile的内存,避免泄漏。
3. 实现多元素布局(预览图+软件标识)
放弃静态控件,使用自定义窗口类处理WM_PAINT消息,依次绘制预览图和软件标识:
- 加载软件标识为GDI+ Image对象;
- 在WM_PAINT中先绘制缩放后的预览图,再在指定位置(如右下角)绘制标识,调整标识的大小和位置。
内容的提问来源于stack exchange,提问作者NSC
相关产品推荐
相关产品推荐

