如何获取MFC MDI应用中活动CDocument的文件图标?
获取MFC MDI活动文档的文件图标ID
首先得说,你的观察完全正确——CMultiDocTemplate构造时传入的第一个资源ID确实存在CDocTemplate的受保护成员m_nIDResource里,但MFC确实没给公开的访问接口。下面给你几个可行的方案:
派生包装类(最合规的做法)
这其实就是你想到的思路,实现起来非常简单:class CMyMultiDocTemplate : public CMultiDocTemplate { public: // 复制基类的构造函数签名 CMyMultiDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass) : CMultiDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass) {} // 公开获取资源ID的方法 UINT GetResourceID() const { return m_nIDResource; } };之后创建文档模板的时候,用
CMyMultiDocTemplate代替原来的CMultiDocTemplate,这样你就能通过活动文档的模板对象调用GetResourceID()拿到图标对应的资源ID了:// 获取活动文档 CDocument* pActiveDoc = AfxGetApp()->GetMainWnd()->GetActiveDocument(); if (pActiveDoc != nullptr) { CDocTemplate* pTemplate = pActiveDoc->GetDocTemplate(); // 转换为我们的派生类 CMyMultiDocTemplate* pMyTemplate = dynamic_cast<CMyMultiDocTemplate*>(pTemplate); if (pMyTemplate != nullptr) { UINT nIconID = pMyTemplate->GetResourceID(); // 这里就可以用nIconID获取图标了,比如LoadIcon(nIconID) } }这个方法完全符合MFC的设计规范,没有任何hack,非常稳定。
直接获取图标句柄(如果不需要资源ID的话)
如果你最终的目的是拿到图标而不是资源ID,其实可以跳过获取ID这一步,直接通过文档的路径调用SHGetFileInfo获取系统关联的文件图标,或者从模板的资源里加载:CDocument* pActiveDoc = AfxGetApp()->GetMainWnd()->GetActiveDocument(); if (pActiveDoc != nullptr) { CString strPath = pActiveDoc->GetPathName(); SHFILEINFO sfi = {0}; SHGetFileInfo(strPath, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_SMALLICON); HICON hIcon = sfi.hIcon; // 使用图标... }不过这个方法拿到的是系统关联图标,不是你在文档模板里设置的自定义图标,如果你需要的是自定义图标,还是第一种方法更靠谱。
不推荐的hack方式
虽然可以通过内存偏移或者强制类型转换去访问受保护成员,但这种方法非常脆弱,一旦MFC版本更新,内存布局变化就会崩溃,绝对不建议在生产代码里用。
总结一下,派生包装类是最稳妥的方案,实现成本极低,完全符合MFC的设计思路,比任何hack方法都可靠。
内容的提问来源于stack exchange,提问作者Paul Harrison
相关产品推荐
相关产品推荐

