第 ⑧ 章 · 属性表处理器(IShellPropSheetExt)
往文件「属性」对话框里加一个自定义标签页——IShellExtInit 拿文件、AddPages 用对话框模板造页、PSPCB 回调管生命周期。
作者 David更新于
右键文件 →「属性」弹出的那个多标签对话框,就是属性表(property sheet)。本章往里加一个自己的标签页。配套代码:08-propsheet。
一个初始化 + 一个页
IShellExtInit::Initialize—— 老规矩,从IDataObject取出被查看属性的文件(见第 ① 章)。IShellPropSheetExt::AddPages—— Shell 在组装属性对话框时调它,你用CreatePropertySheetPage造一个页、交给回调lpfnAddPage。
页本身是一个子对话框:模板放在 .rc 资源里,行为写在 DlgProc 里。
IFACEMETHODIMP PropSheetHandler::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) {
PROPSHEETPAGE psp = {};
psp.dwSize = sizeof(psp);
psp.dwFlags = PSP_USETITLE | PSP_USECALLBACK;
psp.hInstance = g_hInst;
psp.pszTemplate= MAKEINTRESOURCEW(IDD_PROPPAGE);
psp.pszTitle = L"Shell 示例"; // 标签页标题
psp.pfnDlgProc = PropPageDlgProc;
psp.lParam = (LPARAM)_wcsdup(m_szFile); // 传给页的数据(路径副本)
psp.pfnCallback= PropPageCallback;
HPROPSHEETPAGE hPage = CreatePropertySheetPageW(&psp);
if (!hPage) { free((void*)psp.lParam); return E_OUTOFMEMORY; }
if (!lpfnAddPage(hPage, lParam)) { DestroyPropertySheetPage(hPage); return E_FAIL; }
return S_OK;
}DlgProc 在 WM_INITDIALOG 里从 PROPSHEETPAGE.lParam 取到路径,算出大小,SetDlgItemText 填进静态控件。
生命周期:PSPCB 回调
属性对话框是异步的——AddPages 返回后对话框才真正显示、用户看完才关闭。所以要用 PSP_USECALLBACK + 回调管好生命周期:
static UINT CALLBACK PropPageCallback(HWND, UINT uMsg, LPPROPSHEETPAGE ppsp) {
if (uMsg == PSPCB_ADDREF) DllAddRef(); // 页在,DLL 别被卸载
else if (uMsg == PSPCB_RELEASE) { free((void*)ppsp->lParam); DllRelease(); }
return 1;
}两个易漏点:① DLL 保活——页还开着时 DLL 不能被卸载,靠 PSPCB_ADDREF/RELEASE 里 DllAddRef/DllRelease。② 释放传给页的数据——lParam 里 _wcsdup 的路径副本要在 PSPCB_RELEASE 里 free,否则内存泄漏。
.rc 资源里只放 ASCII,避免 rc.exe 在中文 locale 下的编码坑;标题、正文等中文在运行时用 pszTitle / SetDlgItemText 填。这和第 ① 章 /utf-8 是同一类问题的两种应对。
注册
属性表处理器可挂多个,注册在 ProgID 的 shellex\PropertySheetHandlers\<任取的名字> 下:
RegWriteString(HKEY_CLASSES_ROOT,
L"txtfile\\shellex\\PropertySheetHandlers\\DemoPropSheet", nullptr, clsidStr);