// TRACE log starts when user selects Quick View on guitar.dib
// Windows launches QUIKVIEW.EXE, which looks DIB up in registry to find
// the GUID and location of DLL for my viewer. QuickView calls
// CoCreateInstance or CoGetClassObject to create an instance of my
// viewer. My DllGetClassObject returns a class factory, and Quick View
// calls IClassFactory::CreateInstance to create a CFileViewer object,
// returning its IUnknown pointer with ref count = 1.
//
DllGetClassObject({828c5d60-1b7e-11cf-82ed-444553540000}, IClassFactory)
CFileViewer::CFileViewer
// QuickView requests IPersistFile interface
CFileViewer::IFileViewer::QueryInterface(IPersistFile)
[ref count=2]
// QuickView calls my Load function to load "guitar.dib"
CFileViewer::IPersistFile::Load(C:\APD\MSJ\SAMPLES\guitar.dib,00000010)
CFileViewerApp::OnLoad(C:\APD\MSJ\SAMPLES\guitar.dib)
// QuickView requests IFileViewer interface
CFileViewer::IFileViewer::QueryInterface(IFileViewer)
[ref count=3]
// QuickView calls my ShowInitialize function
CFileViewer::IFileViewer::ShowInitialize
CFileViewerApp::OnShowInitialize
[creating main window]
CFVFrameWnd::CFVFrameWnd
// QuickView calls my Show function. I will enter message loop and not
// return until user quits.
CFileViewer::IFileViewer::Show
hwndOwner = 0x00000000
iShow = SW_SHOWNORMAL
dwFlags =
rect = (0,0,0,0)
punkrel = 00000000
strNewFile =
CFileViewerApp::OnShow
[Initializing frame]
CDIBView::OnInitialUpdate
[Entering message loop]
CFileViewerApp::Run
// Now I enter a message loop. Windows calls me a few times to see if
// it's OK to unload, and I return FALSE because objects are still
// outstanding (AfxOleLockApp count is > 0).
DllCanUnloadNow? No
DllCanUnloadNow? No
DllCanUnloadNow? No
// Now I click the window's close button
CFVFrameWnd::OnClose
CFVFrameWnd::OnDestroy
CFVFrameWnd::~CFVFrameWnd
[Exited message loop]
// Finally, I return from IFileViewer::Show
returning from OnShow with:
hwndOwner = 0x00000000
iShow = SW_SHOWNORMAL
dwFlags =
rect = (0,0,0,0)
punkrel = 00000000
strNewFile =
// QuickView releases my IFileViewer interface
CFileViewer::IFileViewer::Release
[ref count=2]
// QuickView releases my IPersistFile interface
CFileViewer::IPersistFile::Release
[ref count=1]
// QuickView is done, and could Release me, but it has a feature where it
// waits about 30 seconds or so before releasing me. I presume this is for
// speed: if the user browses another DIB file, my viewer is already loaded.
// Windows asks several times if it can unload my DLL: no.
DllCanUnloadNow? No
DllCanUnloadNow? No
DllCanUnloadNow? No
// Half a minute later, QuickView decides to Release my viewer (the original
// IUnknown it got from CoCreateInstance. CCmdTarget::OnFinalRelease calls
// "delete this", which is why you see CFileViewer::~CFileViewer called from
// inside CFileViewer::IFileViewer::Release.
CFileViewer::IFileViewer::Release
CFileViewer::OnFinalRelease
CFileViewerApp::OnFinalReleaseFV
CFileViewer::~CFileViewer
[ref count=0]
CFileViewerApp::ExitInstance
CFileViewerApp::~CFileViewerApp
Figure 8 File Viewer Framework Classes
|
CFileViewer |
CCmdTarget-derived class implements viewer COM object: IPersistFile and IFileViewer. Handles trivial stuff by itself; delegates real work to CFileViewerApp. |
|
CFileViewerApp |
CWinApp-derived application class for file viewers. You should derive your app from this instead of CWinApp. This is where all the meat is. It implements all the basic file viewer behavior, mapping file viewer operations like IPersistFile::Load to MFC operations like CWinApp::OpenDocumentFile. |
|
CFVFrameWnd |
CFrameWnd-derived class implements file viewer main frame window. You should derive your CMainFrame class from this instead of CFrameWnd. Implements drag/drop, close and destroy handlers, and manages toolbar Open button. |
Figure 9 Viewer Framework Functions
|
Interfaces and functions implemented by CFileViewer |
|
|
IUnknown |
Implemented by CCmdTarget. |
|
IPersistFile |
|
|
GetClassID |
Returns E_NOTIMPL. |
|
IsDirty |
Returns S_FALSE (Viewers can only view, never modify a file). |
|
Load |
Track state, pass to CFileViewerApp::OnLoad. |
|
Save |
Returns E_NOTIMPL. |
|
SaveCompleted |
Returns E_NOTIMPL. |
|
GetCurFile |
Calls CFileViewerApp::OnGetFileName to get the current filename as a CString, then converts it to OLESTR before returning it. |
|
IFileViewer |
|
|
ShowInitialize |
Track state and save pointer to IFileViewerSite before passing to CFileViewerApp::OnShowInitialize. |
|
Show |
Track state and save FVSHOWINFO before passing to CFileViewerApp::OnShow. Releases IFileViewerSite when done. |
|
PrintTo |
Pass to CFileViewerApp::OnPrintTo. |
|
Special entry points in FileView.cpp |
|
|
DllGetClassObject |
Calls AfxDllGetClassObject. |
|
DllCanUnloadNow |
Calls AfxDllCanUnloadNow. |
|
Functions implemented by CFileViewerApp |
|
|
New virtual functions implemented by CFileViewerApp |
|
|
OnLoad |
Handles IPersistFile::Load. Finds document template, calls CDocTemplate::CreateNewDocument to create a new doc object, then calls CDocument::OnOpenDocument to open it. |
|
OnShowInitialize |
Handles IFileViewer::ShowInitialize. Creates main window using normal MFC mechanism: that is, by getting doc template and calling CDocTemplate::CreateNewFrame. |
|
OnShow |
Handles IFileViewer::Show. Shows the main window and enters a message loop by calling CFileViewerApp::Run. Performs all the logic required to handle flags like FVSIF_RECT and FVSIF_NEWFAILED. Releases previous viewer in FVSHOWINFO::punkRel. Handles pinned state properly. |
|
OnPrintTo |
Handles IFileViewer::PrintTo. Returns FALSE with m_hr = E_NOTIMPL. |
|
OnFinalReleaseFV |
Called from CFileViewer::OnFinalRelease when the COM object is about to be destroyed (ref count=0), this function closes the document and destroys the main window. |
|
OnGetFileName |
Used throughout the code, and to handle IPersistFile::GetCurFile. Default implementation returns the name of the active CDocument as a CString. |
|
CanOpenDocument |
Helper function used to determine if the viewer knows how to open a document, by attempting to match the filename extension with a CDocTemplate in the app's list of supported doc types. Called from OpenDocumentFile and OnLoad. |
|
Overrides of CWinApp/CWinThread virtual functions |
|
|
OpenDocumentFile |
First calls CanOpenDocument to see if the document type is supported; if not, copies filename to strNewFile, sets FVSIF_NEWFILE, and posts a quit message to terminate message loop; otherwise calls CWinApp::OpenDocumentFile to open the doc the normal MFC way. |
|
InitInstance |
Registers file viewer class object and initializes doc manager. Required to work around MFC problems. |
|
ExitInstance |
Displays a TRACE message. |
|
OnFileOpen |
Handles File Open command: call ShellExecute to open the file for editing in native app. |
|
Run |
Special message loop required to work around problems in MFC. |
|
Command handlers |
|
|
OnPinWindow |
ON_COMMAND handler for pin command (View Replace Window). Use in your app's message map. |
|
OnUpdatePinWindow |
ON_UPDATE_COMMAND_UI handler for pin command, correctly sets the checked/unchecked state of the pin button. Use in your app's message map. |
|
Functions implemented by CFVFrameWnd |
|
|
New virtual functions |
|
|
OnSetOpenButtonIcon |
Sets the bitmap for the Open button in the toolbar to the file type's shell icon. |
|
Message handlers |
|
|
OnClose |
Special handling to work around MFC refusing to post a quit message if there are COM objects outstanding. |
|
OnDestroy |
Unpin window if pinned before normal destroy. |
|
OnDropFiles |
Ignore extra files if more than one dropped. |
Figure 10 DIBVIEW
DibView.h
////////////////////////////////////////////////////////////////
// 1997 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// See DibView.cpp
#include "Resource.h"
#include "FVApp.h"
//////////////////
// Application class: derive from CFileViewerApp
class CApp : public CFileViewerApp {
public:
CApp();
virtual ~CApp();
virtual BOOL InitInstance();
protected:
DECLARE_MESSAGE_MAP()
afx_msg void OnAppAbout();
};
DibView.cpp
// DIBVIEW is a Quick File Viewer for device-independent bitmaps.
// You can open .DIB or .BMP files and view them
#include "StdAfx.h"
BEGIN_MESSAGE_MAP(CApp, CFileViewerApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
// Below are handled by CFileViewerApp
ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
ON_COMMAND(ID_VIEW_REPLACEWINDOW, OnPinWindow)
ON_UPDATE_COMMAND_UI(ID_VIEW_REPLACEWINDOW, OnUpdatePinWindow)
END_MESSAGE_MAP()
CApp theApp;
// You must insert this line in your app somewhere.
// Change GUID and name to your own
// {828C5D60-1B7E-11cf-82ED-444553540000}
IMPLEMENT_OLECREATE(CFileViewer, "MSJ DIB Bitmap Viewer",
0x828c5d60, 0x1b7e, 0x11cf, 0x82, 0xed, 0x44, 0x45, 0x53,
0x54, 0x0, 0x0)
#ifdef _DEBUG
// Used for debugging--Interfaces I want to watch
static DBGINTERFACENAME InterfaceNames[] = {
{ &IID_IUnknown, "IUnknown" },
{ &IID_IClassFactory,"IClassFactory" },
{ &IID_IPersistFile, "IPersistFile" },
{ &IID_IFileViewer, "IFileViewer" },
{ &IID_IUnknown, NULL }
};
#endif
CApp::CApp()
{
#ifdef _DEBUG
_pDbgInterfaceNames = InterfaceNames;
#endif
}
CApp::~CApp()
{
}
BOOL CApp::InitInstance()
{
// Save settings in registry, not INI file
SetRegistryKey("MSJ");
if (!CFileViewerApp::InitInstance())
return FALSE;
// Create document template
TRACE("[creating doc template]\n");
AddDocTemplate(new CSingleDocTemplate(IDR_MAINFRAME,
RUNTIME_CLASS(CDIBDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CDIBView)));
return TRUE;
}
void CApp::OnAppAbout()
{
CDialog(IDD_ABOUTBOX).DoModal();
}
MainFrm.h
#include "PalHook.h"
#include "FVFrame.h"
////////////////
// Palette-handling main frame window
class CMainFrame : public CFVFrameWnd {
protected:
DECLARE_DYNCREATE(CMainFrame)
CPalMsgHandler m_palMsgHandler; // handles palette messages
CStatusBar m_wndStatusBar; // status bar
CToolBar m_wndToolBar; // tool (button) bar
DECLARE_MESSAGE_MAP()
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
public:
CMainFrame();
virtual ~CMainFrame();
};
MainFrm.cpp
#include "StdAfx.h"
#include "DibView.h"
#include "MainFrm.h"
IMPLEMENT_DYNCREATE(CMainFrame, CFVFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFVFrameWnd)
ON_WM_CREATE()
ON_WM_CLOSE()
END_MESSAGE_MAP()
static UINT indicators[] = {
ID_SEPARATOR // status line indicator
};
CMainFrame::CMainFrame()
{
}
CMainFrame::~CMainFrame()
{
}
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFVFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndToolBar.Create(this) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME)) {
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if (!m_wndStatusBar.Create(this) ||
!m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT))) {
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
// Install palette hook
m_palMsgHandler.Install(this, NULL);
// Viewers must support drag/drop files to do pinned windows.
DragAcceptFiles();
return 0;
}
Figure 12 DibView.reg
REGEDIT4
[HKEY_CLASSES_ROOT\QuickView\{828C5D60-1B7E-11cf-82ED-444553540000}]
@="MSJ Device Independent Bitmap Viewer"
[HKEY_CLASSES_ROOT\QuickView\.DIB]
@="Windows Device Independent Bitmap File"
[HKEY_CLASSES_ROOT\QuickView\.DIB\{828C5D60-1B7E-11cf-82ED-444553540000}]
@="MSJ Bitmap Viewer"
[HKEY_CLASSES_ROOT\CLSID\{828C5D60-1B7E-11cf-82ED-444553540000}]
@="MSJ Bitmap Viewer"
[HKEY_CLASSES_ROOT\CLSID\{828C5D60-1B7E-11cf-82ED-444553540000}\InprocServer32]
@="C:\\windows\\system\\viewers\\dibview.dll"
"ThreadingModel"="Apartment"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved]
"{828C5D60-1B7E-11cf-82ED-444553540000}"="MSJ Device Independent Bitmap Viewer"