Page 1 of 1

Win32 API Printer Tutorial using GDI functions How to connect to a printer in c++ and print out graph and an invoice Rate Topic: **--- 1 Votes

#1 snoopy11  Icon User is offline

  • Engineering ● Software
  • member icon

Reputation: 710
  • View blog
  • Posts: 2,033
  • Joined: 20-March 10

Posted 10 October 2010 - 06:13 AM

Win32 API Printer Tutorial c++ using GDI Functions.


In this Tutorial we will cover how to connect to a Printer
in c++. Using the Win32 API, use GDI functions to draw a
graph and an Invoice. Then use a Menu to Print these structures
out. This is a Win32 application so build a Win32 application from
your IDE/Compilers main screen. I will be using Code::Blocks for this
application but I have tested it under MSVC as well.

1. Include files

Our main.cpp

#include <Windows.h>
#include <Winspool.h>
#include <CommDlg.h>
#include <math.h>
#include "resource.h"



As you can see we need CommDlg.h for the Printer Dialog
and Winspool.h for the printer functions.
We will need to link to kernel32, user32, comdlg32, gdi32 and winspool
libraries.
For Code::Blocks this is done through Project -> Build Options ->
Linker Settings and click the add button.
For MSVC this is done through Project -> Properties-> Configuration
Properties ->Linker -> Input.

As this is not a unicode build if you are using MSVC
go to Project -> Properties-> Configuration
Properties ->General -> Character Set and change it to use Multi Byte Character Set.

If you are using the express Edition you will have to download the
windows sdk. Found here at ->

http://www.microsoft...&displaylang=en

It requires Windows XP SP3 and above.

Click the link to download it.
And open up windows explorer and go to ->
Program Files\Microsoft SDKs\Windows\v7.0A

there you should see a bin folder, a lib folder and an include folder.
Open the include folder and select Edit -> Select All and then Edit -> Copy.

Then go to your compilers include folder typically Program Files ->
Microsoft Visual Studio 10.0-> VC
there you should see another include folder open it and paste what you have copied into it.
This will copy the Headers required for this program and all other headers
which you might like to have into it.

Go back to Program Files\Microsoft SDKs\Windows\v7.0A
and open the lib folder, select Edit -> Select All and then Edit -> Copy.

Go to Program Files\ Microsoft Visual Studio 10.0\ VC
and open the lib folder and paste what you have copied into it.

2. Setting up some Defines

#define NUM 1000
#define TWOPI (2 * 3.14159)



We need the above defines for calculations in our Sine Graph.

3. Procedures and variables.

HDC hdc;//Handle to device context for Printer
HDC GetPrinterDC (HWND Hwnd);// declaration of procedure

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "Printer Application";



In this section we set up some procedures and variables
the first is hdc, this is a global for a handle to the device context for the Printer.
Second is a declaration of a procedure which will return our device context from a
Print Dialog.
Third is our Main Window Procedure which is entitled WindowProcedure.
Last of all is our class name for the application entitled szClassName.

4. Main Windows Entry Point WinMain.

int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = "APP_MENU";                 /* menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
               0,                   /* Extended possibilites for variation */
               szClassName,         /* Classname */
               szClassName,       /* Title Text */
               WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX, /* default window */
               CW_USEDEFAULT,       /* Windows decides the position */
               CW_USEDEFAULT,       /* where the window ends up on the screen */
               300,                 /* The programs width */
               300,                 /* and height in pixels */
               HWND_DESKTOP,        /* The window is a child-window to desktop */
               NULL,                /* use class menu */
               hThisInstance,       /* Program Instance handler */
               NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */




Our HWND is called hwnd and our menu is set to "APP_MENU" everything else
is just basic windows overhead. We will create "APP_MENU" later on in a resource
header and resource file.

5. Windows callback procedure WindowProcedure.

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    POINT pt [NUM];
    int cxpage, cypage,i;
    HDC prn ;
    RECT rect;
    HFONT DisplayFont;
	static DOCINFO di = { sizeof (DOCINFO), TEXT ("INVOICE TABLE : Printing...")};
	static DOCINFO di2 = { sizeof (DOCINFO), TEXT ("SINE GRAPH : Printing...")};
/* prepare the doc info */    
switch (message) 
    {



POINT is a structure that represents an ordered pair of integer x- and y-coordinates that defines a point in a two-dimensional plane.
cxpage and cypage represent the dimensions of our printer page that we will get later on. Our Font for printing is called DisplayFont

We have two DOCINFO structures one for each thing we will print in this case
a Graph and an Invoice. This Structure contains the input and output file names and other information used by the StartDoc function.

 case WM_COMMAND:

        switch (wParam)
        {
        case APP_TABLE:


           {

            prn = GetPrinterDC(hwnd); // Get Printer Device Context

            cxpage = GetDeviceCaps (prn, HORZRES);
            cypage = GetDeviceCaps (prn, VERTRES);

            StartDoc (prn, &di);

            StartPage (prn) ;

            SetMapMode (prn, MM_ISOTROPIC);
            SetWindowExtEx(prn, 1500,1500, NULL);
            SetViewportExtEx(prn, cxpage/2, cypage/2, NULL);
            SetViewportOrgEx(prn, 0, 0, NULL);


//Start Drawing Table

            Rectangle (prn,15,15,cxpage,cypage);
            Rectangle(prn,50,50,cxpage-50,cypage-50);
            MoveToEx (prn,50,500,NULL);
            LineTo(prn, cxpage-50,500);
            DisplayFont = CreateFont (166, 0, 0, 0, FW_DONTCARE, false, false, false, DEFAULT_CHARSET,
                                      OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
                                      DEFAULT_PITCH | FF_DONTCARE, "Arial Rounded MT Bold");
            SelectObject (prn, DisplayFont);
            TextOut (prn, 300,200,"INVOICE ",7);
            TextOut (prn, 300,600,"Lenny's Car Spares",18);
            Rectangle (prn,cxpage-1000,600,cxpage-100,1100);
            DisplayFont = CreateFont (66, 0, 0, 0, FW_DONTCARE, false, false, false, DEFAULT_CHARSET,
                                      OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
                                      DEFAULT_PITCH | FF_DONTCARE, "Arial Rounded MT Bold");
            SelectObject (prn, DisplayFont);
            TextOut (prn, cxpage-950,650,"Mr Stephen Pollock.",18);
            TextOut (prn, cxpage-950,725,"47 Salmander Place.",19);
            TextOut (prn, cxpage-950,800,"SandyHills.",11);
            TextOut (prn, cxpage-950,875,"London.",7);
            TextOut (prn, cxpage- 950,950,"LO11 7PN.",9);
            Rectangle (prn,300,1800,cxpage-300,cypage-800);
            MoveToEx (prn,300,2100,NULL);
            LineTo(prn, cxpage-300,2100);
            DisplayFont = CreateFont (66, 0, 0, 0, FW_DONTCARE, false, false, false, DEFAULT_CHARSET,
                                      OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
                                      DEFAULT_PITCH | FF_DONTCARE, "Arial Rounded MT Bold");
            SelectObject (prn, DisplayFont);
            TextOut (prn, 390,1950,"Qty",3);
            TextOut (prn, 750,1950,"Description",11);
            TextOut (prn, 1300,1950,"Price.",5);
            TextOut (prn, 1700,1950,"Tax",3);
            TextOut (prn, 2100,1950,"Total",5);
            MoveToEx (prn,640,1800,NULL);
            LineTo(prn, 640,cypage-800);
            MoveToEx (prn,1240,1800,NULL);
            LineTo(prn, 1240,cypage-800);
            MoveToEx (prn,1540,1800,NULL);
            LineTo(prn, 1540,cypage-800);
            Rectangle (prn,1540,cypage-800,cxpage-300,cypage-400);
            MoveToEx (prn,1940,1800,NULL);
            LineTo(prn, 1940,cypage-400);
            DisplayFont = CreateFont (66, 0, 0, 0, FW_DONTCARE, false, false, false, DEFAULT_CHARSET,
                                      OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
                                      DEFAULT_PITCH | FF_DONTCARE, "Arial");
            SelectObject (prn, DisplayFont);
            TextOut (prn, 420,2250,"1",1);
            TextOut (prn, 700,2250,"Alon Car Battery",16);
            TextOut (prn, 1320,2250,"£22.50",6);
            TextOut (prn, 1620,2250,"£4.50",5);
            TextOut (prn, 2020,2250,"£27.00",6);
            TextOut (prn, 420,2550,"2",1);
            TextOut (prn, 700,2550,"Porrelli A64 Tyres",18);
            TextOut (prn, 1320,2550,"£46.50",6);
            TextOut (prn, 1620,2550,"£9.30",5);
            TextOut (prn, 2020,2550,"£111.60",7);
            TextOut (prn, 2020,cypage-600,"£138.60",7);
            TextOut (prn, 1620,cypage-600,"Amount.",7);

            EndPage (prn);

            EndDoc(prn);


            DeleteDC(prn);
           }

            break;





The above is the code for printing our invoice it references the WM_COMMAND
APP_TABLE which we will set up later on in our resource file and header.
The first thing it does is get the Printer device context. Then sets up cxpage and cypage by calling GetDeviceCaps. The StartDoc function starts a print job.
The StartPage function prepares the printer driver to accept data.
The rest of the code are GDI functions which draw out an Invoice table for
‘Lenny’s Car Spares’ a fictional car repair company. EndPage function notifies the device that the application has finished writing to a page. EndDoc ends the printer
job.

case APP_GRAPH:

          {
            GetClientRect(hwnd, &rect);
            prn = GetPrinterDC(hwnd); // Get Printer Device Context

            cxpage = GetDeviceCaps (prn, HORZRES);
            cypage = GetDeviceCaps (prn, VERTRES);


            StartDoc (prn, &di2);

            StartPage (prn) ;
            SetMapMode (prn, MM_ISOTROPIC);
            SetWindowExtEx(prn, 1500,1500, NULL);
            SetViewportExtEx(prn, cxpage/2, cypage/2, NULL);
            SetViewportOrgEx(prn, 0, 0, NULL);
            MoveToEx (prn,0,cypage/2,NULL);

            LineTo(prn,cxpage, (cypage/2));

            for (i=0; i<NUM; i++)
            {
                pt[i].x = i*cxpage/NUM;
                pt[i].y = (int) (cypage/2*(1 - sin(TWOPI*i/NUM)));
            }
            Polyline (prn,pt,NUM);
            EndPage (prn);
            EndDoc(prn);


            DeleteDC(prn);
          }

            break;


        case APP_EXIT:
            SendMessage(hwnd,WM_DESTROY,0,0);

            break;

        }
        return 0;





    case WM_DESTROY:
        PostQuitMessage (0);       /* send a WM_QUIT to the message queue */


        return 0;
    }

    return DefWindowProc (hwnd, message, wParam, lParam);
}




The above is the code for printing our Sine Wave Graph it references the WM_COMMAND ->
APP_GRAPH which we will set up later on in our resource file and header.
The first thing it does is get the Printer device context. Then sets up cxpage and cypage by calling GetDeviceCaps. The StartDoc function starts a print job.
The StartPage function prepares the printer driver to accept data.
The rest of the code are GDI functions which draw out a Sine Graph. EndPage function notifies the device that the application has finished writing to a page. EndDoc ends the printer job. APP_EXIT sends a message to our window to exit the
program cleanly.

HDC GetPrinterDC (HWND Hwnd)
{

// Initialize a PRINTDLG structure's size and set the PD_RETURNDC flag set the Owner flag to hwnd.
// The PD_RETURNDC flag tells the dialog to return a printer device context.
    PRINTDLG pd = {0};
    pd.lStructSize = sizeof( pd );
    pd.hwndOwner = Hwnd;
    pd.Flags = PD_RETURNDC;

// Retrieves the printer DC
    PrintDlg(&pd);
    hdc =pd.hDC;
    return hdc ;

}



The last piece of code in our main.cpp is GetPrinterDC, this gets the printer Device Context. First of all it initialises a PRINTDLG structure’s size and sets the pd Flags
item to PD_RETURNDC this means that our printer dialog is instructed to return a device context from a list selected by the user. For a list of other flag options see MSDN. The hwndOwner item is set to our window, then we bring up the standard
windows common dialog for the printer by calling PrintDlg. Finally we return our
printer device context in the form of hdc.

7.Resource header file.


Our resource.h file

#ifndef RESOURCE_H_INCLUDED
#define RESOURCE_H_INCLUDED



#define APP_TABLE 102
#define APP_GRAPH 103
#define APP_EXIT  104


#endif // RESOURCE_H_INCLUDED



This section of code gives values to our
macros APP_TABLE, APP_GRAPH and
APP_EXIT for use with our resource.rc file.

8.Resource.rc file.

Our resource.rc file

//MENU
//CREATION
#include "resource.h"

APP_MENU MENU DISCARDABLE
BEGIN
    POPUP "&Print"
    BEGIN
        MENUITEM "&Invoice Table",           APP_TABLE
        MENUITEM "&Sine Graph",           APP_GRAPH
        MENUITEM "&Exit",             APP_EXIT
   END
END



The above code is for our Menu called APP_MENU which is
referenced in WinMain as the class menu. It uses the macros
APP_TABLE, APP_GRAPH and APP_EXIT. Defined in our resource.h
file.

And thats it hopefully you now have a working app that can print
out an Invoice and a Sine Graph.

References:

Programming Windows Fifth Edition by Charles Petzold.
MSDN.


Is This A Good Question/Topic? 0
  • +

Page 1 of 1