为您的 MFC 文档类型创建缩略图提取器对象






4.94/5 (21投票s)
2002年9月13日
5分钟阅读

371894

4340
关于为 MFC 文档类型编写缩略图 Shell 扩展的文章。
引言
缩略图视图是 Windows Explorer 的一个很棒的功能。然而,关于如何为自定义文档创建 Shell 缩略图扩展的知识却很少。我当时正在开发一款医学图像可视化软件,并希望为该软件能够加载的 DICOM(医学)图像添加此功能。在搜索了网络之后,我终于在 MSDN 杂志上找到了一篇相关的文章:更多 Windows 2000 UI 改进:通过自定义超文本模板文件扩展 Explorer 视图。这篇文章涵盖了这一主题,并包含了一个简单的图像提取器,用于处理图标文件。在我创建了自己的 DICOM 图像提取器(可根据要求提供)之后,我还为 Scribble(MFC 教程)文档,特别是 Scribble Step 5 创建了一个图像提取器 Shell 扩展。我试图以面向对象的方式编写代码,以促进重用(我是面向对象“大师” Paul Dilascia 的“粉丝”,他是 MSDN 杂志的作者)。最后,我将 scribble 图像提取器项目转换为一个自定义 AppWizard,这样您就可以轻松地为您的 MFC 文档生成图像提取器的骨架代码。上图显示了一个包含医学文件和我的宝贵草图的文件夹的缩略图视图。:)
CScribbleExtractor COM 对象
基于 MFC 的 Scribble 缩略图扩展(ThumbScb
项目)已创建为 MFC 普通 DLL。在 AppWizard 之后,我向项目中添加了一个 ATL 对象。该对象被编码为实现所需的两个接口:IPersistFile
用于了解 Shell 中当前选定的文件,以及 IExtractImage2
(派生自 IExtractImage
)用于访问文档并返回渲染其内容的位图。
// ScribbleExtractor.h
class ATL_NO_VTABLE CScribbleExtractor :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CScribbleExtractor, &CLSID_ScribbleExtractor>,
public IPersistFile,
public IExtractImage2
{
public: ...
// ScribbleExtractor.cpp
// IExtractImage::Extract
HRESULT CScribbleExtractor::Extract(HBITMAP* phBmpThumbnail)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
theApp.LoadDoc(m_szFile);
m_hPreview = theApp.CreateThumbnail(m_bmSize);
*phBmpThumbnail = m_hPreview;
return NOERROR;
}
...
// Code for other interface functions is omitted since it is boiterblate.
CExtractImageApp 通用应用程序类
正如您所见,COM 对象将文档加载和缩略图生成委托给主应用程序对象。这很方便,因为 CWinApp
派生类可以通过文档管理器机制确定支持的文档类型并创建相应的文档对象。为了尽可能多地重用代码,我在一个名为 CExtractImageApp
的通用 CWinApp
派生类中实现了 LoadDoc
和 CreateThumbnail
函数。前者使用通用的 MFC 代码实现。辅助函数 CanOpenDocument
返回一个合适的文档模板,该模板可以创建文档对象来服务文件。文档对象是动态创建的,然后从磁盘加载文件内容。加载代码主要从 MFC 的 OnOpenDocument
函数复制粘贴而来。
// Load document. This function is responsible for opening the document
// and setting m_pOpenDoc to the document loaded.
BOOL CExtractImageApp::LoadDoc(LPCTSTR lpFileName)
{
ASSERT(lpFileName!=NULL);
CString sFileName = lpFileName;
CDocTemplate* pDocTemplate = CanOpenDocument(sFileName); // helper function
// defined above
if (pDocTemplate) {
if(!m_pDoc) {
m_pDoc = pDocTemplate->CreateNewDocument();
m_pDoc->m_bAutoDelete = TRUE;
}
if (m_pDoc)
{
// load content of file, code taken from MFC OnOpenDocument and modified
CFileException fe;
CFile* pFile = m_pDoc->GetFile(sFileName, CFile::modeRead, &fe);
if (pFile == NULL)
return FALSE;
m_pDoc->DeleteContents();
CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete);
loadArchive.m_pDocument = m_pDoc;
loadArchive.m_bForceFlat = FALSE;
try
{
if (pFile->GetLength() != 0)
m_pDoc->Serialize(loadArchive); // load me
loadArchive.Close();
m_pDoc->ReleaseFile(pFile, FALSE);
}
catch(CException *e)
{
//e->ReportError();
m_pDoc->ReleaseFile(pFile, TRUE);
m_pDoc->DeleteContents(); // remove failed contents
e->Delete();
return FALSE;
}
return TRUE;
//delete pDoc;
}
}
return FALSE;
}
CreateThumbnail
函数创建缩略图位图并在其上绘制文档内容。它本质上是一个模板函数,因为它调用了 GetDocSize
和 OnDraw
函数,这些函数是纯 virtual
函数(必须由派生类实现)。绘图以等度映射进行,以使文档尺寸与缩略图尺寸匹配。SetViewportExt
中的负号用于定义笛卡尔坐标系(y 轴值从下到上增加)。
HBITMAP CExtractImageApp::CreateThumbnail(const SIZE bmSize)
{
HBITMAP hThumb; CBitmap bmpThumb;
if(!m_pDoc) return NULL;
CSize bmDocSize = GetDocSize(); // derived class knows it
// Create memory DC, create a color bitmap, and draw on it
CDC memdc;
memdc.CreateCompatibleDC(NULL);
bmpThumb.CreateBitmap(bmSize.cx,bmSize.cy,3,8,NULL);
CBitmap* pOldBm = memdc.SelectObject(&bmpThumb);
memdc.PatBlt(0,0,bmSize.cx,bmSize.cy,WHITENESS);
memdc.SetMapMode(MM_ISOTROPIC);
memdc.SetViewportExt(bmSize.cx,-bmSize.cy);
memdc.SetWindowExt(bmDocSize.cx,bmDocSize.cy);
OnDraw(&memdc); //let the derived class to handle it
memdc.SelectObject(pOldBm);
hThumb = (HBITMAP)bmpThumb.Detach();
return hThumb;
}
派生应用程序类
在派生自 CExtractImageApp
的主应用程序类中,上述纯 virtual
函数通过调用已加载文档的相应函数来实现。在此之前,我必须将 CDocument
的 m_pDoc
指针向下转换为 CScribbleDoc
指针。如果您有多个文档类,可以使用 CObject::IsKindOf
函数来查找 m_pDoc
指向的文档类的类型。在 InitInstance
函数中,我为 scribble 文档类型添加了一个模板。CMDIChildWnd
和 CView
是 MFC 类,而 CScribbleDoc
是原始 scribble 文档类的简化版本。CScribbleDoc
为此项目唯一需要实现的函数是文档加载期间调用的 Serialize
函数以及缩略图创建期间调用的 GetDocSize
和 OnDraw
函数。通常,OnDraw
函数属于 CScribbleView
类,但在此处,不需要文档/视图体系结构。为了简化实现,我从 CScribbleView
复制了 OnDraw
。在第一行,我将 GetDocument()
替换为 this
。
//thumbscb.cpp
BOOL CThumbScbApp::InitInstance()
{
if (!InitATL())
return FALSE;
// Create document template
AddDocTemplate(new CMultiDocTemplate(IDR_SCRIBBTYPE,
RUNTIME_CLASS(CScribbleDoc),RUNTIME_CLASS(CMDIChildWnd), RUNTIME_CLASS(CView)));
return CWinApp::InitInstance();
}
void CThumbScbApp::OnDraw(CDC *pDC)
{
CScribbleDoc *mydoc = (CScribbleDoc *)m_pDoc;
mydoc->OnDraw(pDC);
}
CSize CThumbScbApp::GetDocSize()
{
CScribbleDoc *mydoc = (CScribbleDoc *)m_pDoc;
return mydoc->GetDocSize();
}
// scribdoc.cpp
void CScribbleDoc::OnDraw(CDC* pDC)
{
CScribbleDoc* pDoc = this; //GetDocument();
ASSERT_VALID(pDoc);
...
如何调试
调试 Shell 扩展非常困难。我创建了一个 COM 对象(ThumbExtract
项目),该对象使用 Shell 接口来获取文件的 IExtractImage
并然后创建一个位图。在同一个文件夹中,您会找到一个名为 TestThumbExtract
的小型 VB6 EXE 项目,它引用了 ThumbExtract
对象。下面显示了这个测试应用程序的一个实例。单击按钮选择一个文件,该文件的较大缩略图将出现在图片框中。您可以通过将 TestThumbExtract.exe 设置为项目/设置/调试中的“可执行文件用于调试会话”来调试您的 Shell 扩展。
缩略图项目向导
由于新缩略图 Shell 扩展项目的代码更改很少,我创建了一个基于 ThumbScb
项目的自定义 AppWizard(ThumbWiz
项目)。编译后的向导 DLL 名为 ThumbWiz.awx,您必须将其复制到“C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Template”文件夹。然后在新建项目对话框中会出现一个名为“**缩略图项目向导**”的条目。该向导有一个自定义步骤,会询问您的对象的名称(例如,Scribble)以及支持的文件扩展名(如果您想支持多个扩展名,请用逗号分隔)。创建的应用程序类、文档类和 COM 类的名称基于您提供的对象名称。此外,对于每个文件扩展名,都会在 InitInstance
中创建一个新的文档模板。会自动生成新的 GUID。骨架代码包含 TODO 注释,指示在哪里添加所需的详细信息。在 CThumbWizAppWiz::CustomizeProject
中,我自定义了项目设置(特别是 MIDL 设置),并创建了一个后置构建步骤来注册该对象。会在文件扩展名键下创建注册表项,以便系统可以确定为每个文件扩展名加载哪个对象。请小心不要替换已知类型(如 .jpg)的条目,因为不会备份注册表条目。
结论
现在,为您的 MFC 文档类型创建缩略图 Shell 扩展应该会非常容易(我希望如此!)。随着本文的结束,我的缩略图博士论文也告一段落了!访问我的主页,获取其他免费工具和程序。
历史
- 2002 年 10 月 5 日 - 更新源代码
- 2002 年 11 月 22 日 - 更新源代码
许可证
本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。