Figure 1   Printing Return Codes

Return Code

Description

SP_ERROR

The print job was aborted for an unspecified reason

SP_APPABORT

The print job was aborted because the user clicked the Cancel button in the dialog box that displays the status of the print job

SP_USERABORT

The print job was aborted because the user canceled it through the operating system shell

SP_OUTOFDISK

The system is out of disk space, so no further printer data can be spooled

SP_OUTOFMEMORY

The system is out of memory, so no further printer data can be spooled

Figure 2   CView Print Overridables

Function

Description

CView::OnPreparePrinting

Called at the very onset of a print job. Override to call CView::DoPreparePrinting and provide the framework with the page count (if known) and other information about the print job.

CView::OnBeginPrinting

Called just before printing begins. Override to allocate fonts and other resources required for printing.

CView::OnPrepareDC

Called before each page is printed. Override to set the viewport origin or clipping region to print the current page if OnDraw will be used to produce the printed output.

CView::OnPrint

Called to print one page of the document. A page number and printer DC are supplied to you by the framework. Override to print headers, footers, and other page elements that aren't drawn by the view's OnDraw function or to print each page without relying on OnDraw.

CView::OnEndPrinting

Called when printing is finished. Override to deallocate resources allocated in OnBeginPrinting.

Figure 4   EZPrint

Resource.h

 //***********************************************************************
//
//  Resource.h (EZPrint)
//
//***********************************************************************

#define IDR_MAINFRAME           100

EZPrint.rc

 //***********************************************************************
//
//  EZPrint.rc
//
//***********************************************************************

#include <afxres.h>
#include "Resource.h"

IDR_MAINFRAME MENU
BEGIN
    POPUP "&File" {
        MENUITEM "&Print...\tCtrl+P",       ID_FILE_PRINT
        MENUITEM "Print Pre&view",          ID_FILE_PRINT_PREVIEW
        MENUITEM "P&rint Setup...",         ID_FILE_PRINT_SETUP
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                   ID_APP_EXIT
    }
END

IDR_MAINFRAME ACCELERATORS
BEGIN
    "P", ID_FILE_PRINT, VIRTKEY, CONTROL
END

STRINGTABLE
BEGIN
    IDR_MAINFRAME "EZPrint"
END

EZPrint.h

 //***********************************************************************
//
//  EZPrint.h
//
//***********************************************************************

class CPrintApp : public CWinApp
{
public:
    virtual BOOL InitInstance ();

protected:
    DECLARE_MESSAGE_MAP ()
};

EZPrint.cpp

 //***********************************************************************
//
//  EZPrint.cpp
//
//***********************************************************************

#include <afxwin.h>
#include "Resource.h"
#include "EZPrint.h"
#include "EZPrintDoc.h"
#include "EZPrintView.h"

CPrintApp myApp;

BEGIN_MESSAGE_MAP (CPrintApp, CWinApp)
    ON_COMMAND (ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP ()

BOOL CPrintApp::InitInstance ()
{
    CSingleDocTemplate* pDocTemplate;
    pDocTemplate = new CSingleDocTemplate (
        IDR_MAINFRAME,
        RUNTIME_CLASS (CPrintDoc),
        RUNTIME_CLASS (CFrameWnd),
        RUNTIME_CLASS (CPrintView)
    );

    AddDocTemplate (pDocTemplate);

    CCommandLineInfo cmdInfo;
    ParseCommandLine (cmdInfo);

    if (!ProcessShellCommand (cmdInfo))
        return FALSE;

    return TRUE;
}

EZPrintDoc.h

 //***********************************************************************
//
//  EZPrintDoc.h
//
//***********************************************************************

class CPrintDoc : public CDocument
{
    DECLARE_DYNCREATE (CPrintDoc)
};

EZPrintDoc.cpp

 //***********************************************************************
//
//  EZPrintDoc.cpp
//
//***********************************************************************

#include <afxwin.h>
#include "EZPrintDoc.h"

IMPLEMENT_DYNCREATE (CPrintDoc, CDocument)

EZPrintView.h

 //***********************************************************************
//
//  EZPrintView.h
//
//***********************************************************************

class CPrintView : public CView
{
    DECLARE_DYNCREATE (CPrintView)

protected:
    virtual void OnDraw (CDC*);
    virtual BOOL OnPreparePrinting (CPrintInfo*);

    DECLARE_MESSAGE_MAP ()
};

EZPrintView.cpp

 //***********************************************************************
//
//  EZPrintView.cpp
//
//***********************************************************************

#include <afxwin.h>
#include <afxext.h>
#include "EZPrintView.h"

IMPLEMENT_DYNCREATE (CPrintView, CView)

BEGIN_MESSAGE_MAP (CPrintView, CView)
    ON_COMMAND (ID_FILE_PRINT, CView::OnFilePrint)
    ON_COMMAND (ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP ()

void CPrintView::OnDraw (CDC* pDC)
{
    CPen pen (PS_SOLID, 50, RGB (0, 0, 255));
    CBrush brush (RGB (255, 255, 0));

    pDC->SetMapMode (MM_LOMETRIC);
    CPen* pOldPen = pDC->SelectObject (&pen);
    CBrush* pOldBrush = pDC->SelectObject (&brush);

    pDC->Ellipse (100, -100, 1100, -1100);

    pDC->SelectObject (pOldBrush);
    pDC->SelectObject (pOldPen);
}

BOOL CPrintView::OnPreparePrinting (CPrintInfo* pInfo)
{
    pInfo->SetMaxPage (1);
    return DoPreparePrinting (pInfo);
}

Figure 8   HexDump

Resource.h

 //***********************************************************************
//
//  Resource.h (HexDump)
//
//***********************************************************************

#define IDR_MAINFRAME           100

HexDump.rc

 //***********************************************************************
//
//  HexDump.rc
//
//***********************************************************************

#include <afxres.h>
#include "Resource.h"

IDR_MAINFRAME MENU
BEGIN
    POPUP "&File" {
        MENUITEM "&Open...\tCtrl+O",        ID_FILE_OPEN
        MENUITEM SEPARATOR
        MENUITEM "&Print...\tCtrl+P",       ID_FILE_PRINT
        MENUITEM "Print Pre&view",          ID_FILE_PRINT_PREVIEW
        MENUITEM "P&rint Setup...",         ID_FILE_PRINT_SETUP
        MENUITEM SEPARATOR
        MENUITEM "Recent File",             ID_FILE_MRU_FILE1
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                   ID_APP_EXIT
    }
END

IDR_MAINFRAME ACCELERATORS
BEGIN
    "O", ID_FILE_OPEN,  VIRTKEY, CONTROL
    "P", ID_FILE_PRINT, VIRTKEY, CONTROL
END

STRINGTABLE
BEGIN
    IDR_MAINFRAME "HexDump"
END

HexDump.h

 //***********************************************************************
//
//  HexDump.h
//
//***********************************************************************

class CHexDumpApp : public CWinApp
{
public:
    virtual BOOL InitInstance ();

protected:
    DECLARE_MESSAGE_MAP ()
};

HexDump.cpp

 //***********************************************************************
//
//  HexDump.cpp
//
//***********************************************************************

#include <afxwin.h>
#include "Resource.h"
#include "HexDump.h"
#include "HexDoc.h"
#include "HexView.h"

CHexDumpApp myApp;

BEGIN_MESSAGE_MAP (CHexDumpApp, CWinApp)
    ON_COMMAND (ID_FILE_OPEN, CWinApp::OnFileOpen)
    ON_COMMAND (ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP ()

BOOL CHexDumpApp::InitInstance ()
{
    SetRegistryKey ("Programming Windows 95 with MFC");
    LoadStdProfileSettings ();

    CSingleDocTemplate* pDocTemplate;
    pDocTemplate = new CSingleDocTemplate (
        IDR_MAINFRAME,
        RUNTIME_CLASS (CHexDoc),
        RUNTIME_CLASS (CFrameWnd),
        RUNTIME_CLASS (CHexView)
    );

    AddDocTemplate (pDocTemplate);

    CCommandLineInfo cmdInfo;
    ParseCommandLine (cmdInfo);

    if (!ProcessShellCommand (cmdInfo))
        return FALSE;

    m_pMainWnd->DragAcceptFiles ();
    return TRUE;
}

HexDoc.h

 //***********************************************************************
//
//  HexDoc.h
//
//***********************************************************************

class CHexDoc : public CDocument
{
    DECLARE_DYNCREATE (CHexDoc)

private:
    UINT m_nDocLength;
    BYTE* m_pFileData;

public:
    CHexDoc ();
    virtual void DeleteContents ();
    virtual void Serialize (CArchive&);
    UINT GetBytes (UINT, UINT, PVOID);
    UINT GetDocumentLength ();
};

HexDoc.cpp

 //***********************************************************************
//
//  HexDoc.cpp
//
//***********************************************************************

#include <afxwin.h>
#include "HexDoc.h"

IMPLEMENT_DYNCREATE (CHexDoc, CDocument)

CHexDoc::CHexDoc ()
{
    m_nDocLength = 0;
    m_pFileData = NULL;
}

void CHexDoc::DeleteContents ()
{
    if (m_pFileData != NULL) {
        delete[] m_pFileData;
        m_pFileData = NULL;
        m_nDocLength = 0;
    }
}

void CHexDoc::Serialize (CArchive& ar)
{
    if (ar.IsLoading ()) {
        CFile* pFile = ar.GetFile ();
        m_nDocLength = (UINT) pFile->GetLength ();

        // Allocate a buffer for the file data
        try {
            m_pFileData = new BYTE[m_nDocLength];
        }
        catch (CMemoryException* e) {
            m_nDocLength = 0;
            throw e;
        }

        // Read the file data into the buffer
        try {
            pFile->Read (m_pFileData, m_nDocLength);
        }
        catch (CFileException* e) {
            delete[] m_pFileData;
            m_pFileData = NULL;
            m_nDocLength = 0;
            throw e;
        }
    }
}

UINT CHexDoc::GetBytes (UINT nIndex, UINT nCount, PVOID pBuffer)
{
    if (nIndex >= m_nDocLength)
        return 0;

    UINT nLength = nCount;
    if ((nIndex + nCount) > m_nDocLength)
        nLength = m_nDocLength - nIndex;

    ::CopyMemory (pBuffer, m_pFileData + nIndex, nLength);
    return nLength;
}

UINT CHexDoc::GetDocumentLength ()
{
    return m_nDocLength;
}

HexView.h

 //***********************************************************************
//
//  HexView.h
//
//***********************************************************************

class CHexView : public CScrollView
{
    DECLARE_DYNCREATE (CHexView)

private:
    CFont m_screenFont;
    CFont m_printerFont;

    UINT m_cyScreen;
    UINT m_cyPrinter;
    UINT m_cxOffset;
    UINT m_cxWidth;

    UINT m_nLinesTotal;
    UINT m_nLinesPerPage;

    CHexDoc* GetDocument () { return (CHexDoc*) m_pDocument; }
    void FormatLine (UINT, CString&);
    void PrintPageHeader (CDC*, UINT);
    void PrintPage (CDC*, UINT);

public:
    virtual void OnInitialUpdate ();

protected:
    virtual void OnDraw (CDC*);
    virtual BOOL OnPreparePrinting (CPrintInfo*);
    virtual void OnBeginPrinting (CDC*, CPrintInfo*);
    virtual void OnPrint (CDC*, CPrintInfo*);
    virtual void OnEndPrinting (CDC*, CPrintInfo*);

    afx_msg int OnCreate (LPCREATESTRUCT);
    DECLARE_MESSAGE_MAP ()
};

HexView.cpp

 //***********************************************************************
//
//  HexView.cpp
//
//***********************************************************************

#include <afxwin.h>
#include <afxext.h>
#include "HexDoc.h"
#include "HexView.h"

#define PRINTMARGIN 2

IMPLEMENT_DYNCREATE (CHexView, CScrollView)

BEGIN_MESSAGE_MAP (CHexView, CScrollView)
    ON_WM_CREATE ()
    ON_COMMAND (ID_FILE_PRINT, CScrollView::OnFilePrint)
    ON_COMMAND (ID_FILE_PRINT_DIRECT, CScrollView::OnFilePrint)
    ON_COMMAND (ID_FILE_PRINT_PREVIEW, CScrollView::OnFilePrintPreview)
END_MESSAGE_MAP ()

int CHexView::OnCreate (LPCREATESTRUCT lpcs)
{
    if (CScrollView::OnCreate (lpcs) == -1)
        return -1;

    CClientDC dc (this);
    int nHeight = -((dc.GetDeviceCaps (LOGPIXELSY) * 10) / 72);

    m_screenFont.CreateFont (nHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0,
        DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS,
        DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Courier New");

    TEXTMETRIC tm;
    CFont* pOldFont = dc.SelectObject (&m_screenFont);
    dc.GetTextMetrics (&tm);
    m_cyScreen = tm.tmHeight + tm.tmExternalLeading;
    dc.SelectObject (pOldFont);
    return 0;
}

void CHexView::OnInitialUpdate ()
{
    UINT nDocLength = GetDocument ()->GetDocumentLength ();
    m_nLinesTotal = (nDocLength + 15) / 16;

    SetScrollSizes (MM_TEXT, CSize (0, m_nLinesTotal * m_cyScreen),
        CSize (0, m_cyScreen * 10), CSize (0, m_cyScreen));
    ScrollToPosition (CPoint (0, 0));

    CScrollView::OnInitialUpdate ();
}

void CHexView::OnDraw (CDC* pDC)
{
    if (m_nLinesTotal != 0) {
        CRect rect;
        pDC->GetClipBox (&rect);

        UINT nStart = rect.top / m_cyScreen;
        UINT nEnd = min (m_nLinesTotal - 1,
            (rect.bottom + m_cyScreen - 1) / m_cyScreen);

        CString string;
        CFont* pOldFont = pDC->SelectObject (&m_screenFont);

        for (UINT i=nStart; i<=nEnd; i++) {
            FormatLine (i, string);
            pDC->TextOut (2, (i * m_cyScreen) + 2, string);
        }
        pDC->SelectObject (pOldFont);
    }
}

BOOL CHexView::OnPreparePrinting (CPrintInfo* pInfo)
{
    return DoPreparePrinting (pInfo);
}

void CHexView::OnBeginPrinting (CDC* pDC, CPrintInfo* pInfo)
{
    int nHeight = -((pDC->GetDeviceCaps (LOGPIXELSY) * 10) / 72);

    m_printerFont.CreateFont (nHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0,
        DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS,
        DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Courier New");

    TEXTMETRIC tm;
    CFont* pOldFont = pDC->SelectObject (&m_printerFont);
    pDC->GetTextMetrics (&tm);
    m_cyPrinter = tm.tmHeight + tm.tmExternalLeading;
    CSize size = pDC->GetTextExtent ("---------1---------2---------" \
        "3---------4---------5---------6---------7---------8-", 81);
    pDC->SelectObject (pOldFont);

    m_nLinesPerPage = (pDC->GetDeviceCaps (VERTRES) -
        (m_cyPrinter * (3 + (2 * PRINTMARGIN)))) / m_cyPrinter;
    UINT nMaxPage = max (1, (m_nLinesTotal + (m_nLinesPerPage - 1)) /
        m_nLinesPerPage);
    pInfo->SetMaxPage (nMaxPage);

    m_cxOffset = (pDC->GetDeviceCaps (HORZRES) - size.cx) / 2;
    m_cxWidth = size.cx;
}

void CHexView::OnPrint (CDC* pDC, CPrintInfo* pInfo)
{
    PrintPageHeader (pDC, pInfo->m_nCurPage);
    PrintPage (pDC, pInfo->m_nCurPage);
}

void CHexView::OnEndPrinting (CDC* pDC, CPrintInfo* pInfo)
{
    m_printerFont.DeleteObject ();
}

void CHexView::FormatLine (UINT nLine, CString& string)
{
    BYTE b[17];
    ::FillMemory (b, 16, 32);
    UINT nCount = GetDocument ()->GetBytes (nLine * 16, 16, b);

    string.Format ("%0.8X    %0.2X %0.2X %0.2X %0.2X %0.2X %0.2X " \
        "%0.2X %0.2X - %0.2X %0.2X %0.2X %0.2X %0.2X %0.2X %0.2X " \
        "%0.2X    ", nLine * 16,
        b[0], b[1],  b[2],  b[3],  b[4],  b[5],  b[6],  b[7],
        b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);

    for (UINT i=0; i<nCount; i++) {
        if (!::IsCharAlphaNumeric (b[i]))
            b[i] = 0x2E;
    }

    b[nCount] = 0;
    string += b;

    if (nCount < 16) {
        UINT pos1 = 59;
        UINT pos2 = 60;
        UINT j = 16 - nCount;

        for (i=0; i<j; i++) {
            string.SetAt (pos1, ' ');
            string.SetAt (pos2, ' ');
            pos1 -= 3;
            pos2 -= 3;
            if (pos1 == 35) {
                string.SetAt (35, ' ');
                string.SetAt (36, ' ');
                pos1 = 33;
                pos2 = 34;
            }
        }
    }
}

void CHexView::PrintPageHeader (CDC* pDC, UINT nPageNumber)
{
    CString strHeader = GetDocument ()->GetPathName ();
    if (strHeader.GetLength () > 68)
        strHeader = GetDocument ()->GetTitle ();

    CString strPageNumber;
    strPageNumber.Format ("Page %d", nPageNumber);

    UINT nSpaces = 81 - strPageNumber.GetLength () -
        strHeader.GetLength ();
    for (UINT i=0; i<nSpaces; i++)
        strHeader += ' ';
    strHeader += strPageNumber;

    UINT y = m_cyPrinter * PRINTMARGIN;
    CFont* pOldFont = pDC->SelectObject (&m_printerFont);
    pDC->TextOut (m_cxOffset, y, strHeader);

    y += (m_cyPrinter * 3) / 2;
    pDC->MoveTo (m_cxOffset, y);
    pDC->LineTo (m_cxOffset + m_cxWidth, y);

    pDC->SelectObject (pOldFont);
}

void CHexView::PrintPage (CDC* pDC, UINT nPageNumber)
{
    if (m_nLinesTotal != 0) {
        UINT nStart = (nPageNumber - 1) * m_nLinesPerPage;
        UINT nEnd = min (m_nLinesTotal - 1, nStart + m_nLinesPerPage - 1);

        CString string;
        CFont* pOldFont = pDC->SelectObject (&m_printerFont);

        UINT y;
        for (UINT i=nStart; i<=nEnd; i++) {
            FormatLine (i, string);
            y = ((i - nStart) + PRINTMARGIN + 3) * m_cyPrinter;
            pDC->TextOut (m_cxOffset, y, string);
        }
        pDC->SelectObject (pOldFont);
    }
}

Figure 9   GetDeviceCaps Values That Query Print Device Parameters

Value

Description

HORZRES

Returns the width of the printable page area in pixels

VERTRES

Returns the height of the printable page area in pixels

HORSIZE

Returns the width of the printable page area in millimeters

VERTSIZE

Returns the height of the printable page area in millimeters

LOGPIXELSX

Returns the number of pixels per inch in the horizontal direction (300 for a 300 dpi printer)

LOGPIXELSY

Returns the number of pixels per inch in the vertical direction (300 for a 300 dpi printer)

PHYSICALWIDTH

Returns the page width in pixels (2,550 for an 8.5 by 11-inch page on a 300 dpi printer)

PHYSICALHEIGHT

Returns the page height in pixels (3,350 for an 8.5 by 11-inch page on a 300 dpi printer)

PHYSICALOFFSETX

Returns the distance in pixels from the left of the page to the beginning of the page's printable area

PHYSICALOFFSETY

Returns the distance in pixels from the top of the page to the beginning of the page's printable area

TECHNOLOGY

Returns a value that identifies the type of output device that the DC pertains to (the most common return values are DT_RASDISPLAY for screens, DT_RASPRINTER for printers, and DT_PLOTTER for plotters)

RASTERCAPS

Returns a series of bit flags identifying the level of GDI support provided by the printer driver (for example, an RC_BITBLT flag indicates that the printer support BitBlts, and RC_STRETCHBLT indicates that the printer supports StretchBlts)

NUMCOLORS

Returns the number of colors that the printer supports (the return value is 2 for black-and-white printers)

Figure 10   Enumerating Printers

 #include <winspool.h>
·
·
·
DWORD dwSize, dwPrinters;
::EnumPrinters (PRINTER_ENUM_LOCAL, NULL, 5,
    NULL, 0, &dwSize, &dwPrinters); 

BYTE* pBuffer = new BYTE[dwSize];

::EnumPrinters (PRINTER_ENUM_LOCAL, NULL, 5,
    pBuffer, dwSize, &dwSize, &dwPrinters); 

if (dwPrinters != 0) {
    PRINTER_INFO_5* pPrnInfo = (PRINTER_INFO_5*) pBuffer;
    for (UINT i=0; i<dwPrinters; i++) {
        pComboBox->AddString (pPrnInfo->pPrinterName);
        pPrnInfo++;
    }
}

delete[] pBuffer;

If a printer is selected from the combo box and you want to create a device context for it, you can pass the device name copied from the PRINTER_INFO_5 structure to CDC::CreateDC as follows:

 CString strPrinterName;
int nIndex = pComboBox->GetCurSel ();
pComboBox->GetLBText (nIndex, strPrinterName);

CDC dc;
dc.CreateDC (NULL, strPrinterName, NULL, NULL);