Figure 5   TRACE Output for DIB Viewer

// 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"