第 ⑨ 章 · 预览处理器(IPreviewHandler)
在资源管理器预览窗格里渲染文件——它跑在独立宿主 prevhost.exe 里(靠 AppID 隔离);讲 SetWindow/DoPreview/Unload、IObjectWithSite 与三处注册。
作者 David更新于
预览处理器负责资源管理器右侧「预览窗格」(Alt+P)里的内容。它是本指南里最“重”的一种:跑在独立宿主进程 prevhost.exe 里,要自己管一个窗口。配套代码:09-preview。
三个接口协作
IInitializeWithStream—— 拿文件流。IPreviewHandler—— 核心:SetWindow告诉你“画在哪个父窗口、哪块矩形”,DoPreview里你在那建子窗口渲染,SetRect跟随窗格大小变化,Unload收尾。IObjectWithSite—— 宿主设置的站点(预览处理器约定要实现)。
IFACEMETHODIMP PreviewHandler::DoPreview() {
m_hwndPreview = CreateWindowExW(0, L"EDIT", nullptr,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL,
m_rc.left, m_rc.top, m_rc.right - m_rc.left, m_rc.bottom - m_rc.top,
m_hwndParent, nullptr, g_hInst, nullptr);
// 从 m_pStream 读最多 64KB,MultiByteToWideChar(CP_UTF8,…) 后 SetWindowText
return S_OK;
}
IFACEMETHODIMP PreviewHandler::Unload() {
if (m_hwndPreview) { DestroyWindow(m_hwndPreview); m_hwndPreview = nullptr; }
if (m_pStream) { m_pStream->Release(); m_pStream = nullptr; }
return S_OK;
}几个生命周期铁律:SetWindow 可能在 DoPreview 前后多次调用(窗格移动/缩放),要能随时把子窗口 MoveWindow 到新矩形;Unload 必须销毁窗口、释放流,因为同一个预览宿主会被复用来预览下一个文件。
隔离宿主:AppID
预览处理器不直接跑在资源管理器里,而是被托管进 prevhost.exe——这样即使你的渲染代码崩了,倒的也只是预览宿主,资源管理器安然无恙。实现方式是给 CLSID 挂一个 AppID,指向系统预置的预览宿主:
// 64 位预览宿主 prevhost.exe 的 AppID(DllSurrogate 系统已注册好)
RegWriteString(HKEY_CLASSES_ROOT, L"CLSID\\{我们的CLSID}", L"AppID",
L"{534A1E02-D58F-44f0-B58B-36CBED287C7C}");三处注册
CLSID\{CLSID}\ AppID = {534A1E02-...} ← 用哪个隔离宿主
HKLM\...\PreviewHandlers\ (值名={CLSID}) = 显示名 ← 登记进系统清单
.myp\shellex\{8895b1c6-b41f-4c1c-a562-0d564250836f}\ (默认)={CLSID} ← 绑定文件类型Windows 内置了大量文档/图片的预览处理器。写自己的通常是为了私有格式。调试时可以直接给 prevhost.exe 挂调试器(它是独立进程),比调资源管理器省心得多。