|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThumbnail views is a nice feature of Windows Explorer. However, little is known about how to create shell thumbnail extensions for custom documents. I was developing a medical image visualisation software and wanted this feature for the DICOM (medical) images that the software can load. After searching the Web i finally found a relevant article in the MSDN magazine: More Windows 2000 UI Goodies: Extending Explorer Views by Customizing Hypertext Template Files. The articles covers this topic and includes a simple image extractor for icon files. After creating my DICOM image extractor (i can give it upon request) i created also an image extractor shell extension for Scribble (MFC tutorial) documents and specifically for Scribble Step 5. I tried to write the code in an objected oriented way which promotes reuse (i am a "fun" of the OO "guru" Paul Dilascia, author of the MSDN mag). Finally, i converted the scribble image extractor project to a Custom AppWizard so that you can easily generate the sceleton code of an image extractor for your MFC documents. The figure above shows the thumbnail view of a folder containing a medical file and also my precious scribbles :) CScribbleExtractor COM objectThe MFC-based thumbnail extension for Scribble (ThumbScb project) has been created as an MFC regular DLL. After the AppWizard i added an ATL object to the project. The object was coded to implement the two interfaces needed: IPersistFile to know about the file currently selected in the shell, and IExtractImage2 (derived from IExtractImage) to access the document and return a bitmap that renders its content. // 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 generic application classAs you can see, the COM object delegates document loading and thumbnail
generation to the main application object. This is convenient because the
CWinApp-derived class can determine the supported document types and create corresponding
document objects via the document manager mechanism. In order to
reuse as much code as possible i implemented the // 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; } The 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; } Derived application class
In the main application class derived from //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); ... How to debugDebugging shell extensions is difficult. I have created a COM object (ThumbExtract project) that uses shell interfaces to get an IExtractImage for a file and then creates a bitmap. In the same folder you 'll find small VB6 EXE project called TestThumbExtract that references the ThumbExtract object. An instance of this test application is shown below. Click the button to select a file and a large thumbnail for that file will appear in the picture box. You can debug your shell extension by setting the TestThumbExtract.exe as the 'Executable for debug session' in Project/Settings/Debug.
Thumbnail Project WizardSince code changes for a new thumbnail shell extension project are few, i
made a custom AppWizard (ThumbWiz project) based on the ThumbScb
project. The compiled wizard DLL is
named ThumbWiz.awx and you must copy it to "C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Template"
folder. An entry named "Thumbnail Project Wizard" appears
then in the New Project dialog. The wizard has a custom step which askes your
object's name (e.g. Scribble) and the file extensions supported (if you want to
support many extensions separate them with commas). The names of the application
class, the document class and the COM class created, are based on the object
name you give. Also, for each file extension a new document template is created
in ConclusionCreating thumbnail shell extensions for your MFC document types should be very easy now (i hope :). With this article, my thumbnail-PhD comes to an end !! Visit my homepage for other free tools and programs. History5 Oct 2002 - updated source code 22 Nov 2002 - updated source code
|
||||||||||||||||||||||