How to Print a Form or Control using StretchDIBits

See AlsoVMZ7S4

 

This article is reprinted from the Microsoft Knowledge Base.  To view the article, maximize your help window.  This information applies to Visual Basic for Windows, versions 2.0 and 3.0.

 

Summary:

 

This article demonstrates two Visual Basic procedures: PrintWindow and PrintClient.  Both procedures allow you to print a control or form at a specified location and size on a printed page or to another form or picture control.  The PrintWindow procedure allows you to print the entire control including border, caption and menus.  Whereas, the PrintClient procedure prints everything contained within the form or control excluding the border, caption and menus.  When passed a form, the PrintClient procedure works just like Visual Basic's PrintForm method.  Both procedures will print all child controls contained on the form or control.

 

The PrintWindow and PrintClient procedures use the StretchDIBits Window API function as well as other Windows API functions to print a form or control.  These functions will print to both Postscript and PCL (printer control language) or HP-type LaserJet

printers.

 

More Information:

 

Perform the following steps to create a sample program that demonstrates how to print a form to the printer.

 

1. Run Visual Basic, or from the File menu select New Project (ALT, F, N) if Visual

   Basic is already running.  Form1 will be created by default.

 

2. Add two command buttons (Command1 and Command2) to Form1

 

3. Load the WINLOGO.BMP file (or some other large bitmap) into the Picture property

   of Form1.  WINLOGO.BMP should be located in your Windows directory.

 

4. From the File menu, select New Module (ALT, F, M).  Module1 will be created.

 

5. Add the following code to the general declarations section of Module1

 

DefInt A-Z

 

Type BITMAPINFOHEADER_Type

    biSize As Long

    biWidth As Long

    biHeight As Long

    biPlanes As Integer

    biBitCount As Integer

    biCompression As Long

    biSizeImage As Long

    biXPelsPerMeter As Long

    biYPelsPerMeter As Long

    biClrUsed As Long

    biClrImportant As Long

End Type

 

Type BITMAPINFO_Type

    BitmapInfoHeader As BITMAPINFOHEADER_Type

    bmiColors As String * 1024

End Type

 

Type RectType

    Left As Integer

    Top As Integer

    Right As Integer

    Bottom As Integer

End Type

 

Type PointType

    x As Integer

    y As Integer

End Type

 

' DC related API

Declare Function CreateCompatibleDC Lib "gdi" (ByVal hDC)

Declare Function GetWindowDC Lib "user" (ByVal hWnd)

Declare Function GetDC Lib "user" (ByVal hWnd)

Declare Function ReleaseDC Lib "user" (ByVal hWnd, ByVal hDC)

Declare Function DeleteDC Lib "gdi" (ByVal hDC)

 

' Graphics related API

Declare Function BitBlt Lib "gdi" (ByVal hDC, ByVal x, ByVal y,

ByVal w, ByVal h, ByVal hDC, ByVal x, ByVal y, ByVal o As Long)

Declare Function GetDIBits Lib "gdi" (ByVal hDC, ByVal hBitmap,

ByVal nStartScan, ByVal nNumScans, ByVal lpBits As Long,

BitmapInfo As BITMAPINFO_Type, ByVal wUsage)

Declare Function StretchDIBits Lib "gdi" (ByVal hDC, ByVal DestX,

ByVal DestY, ByVal wDestWidth, ByVal wDestHeight, ByVal SrcX,

ByVal SrcY, ByVal wSrcWidth, ByVal wSrcHeight, ByVal lpBits&,

BitsInfo As BITMAPINFO_Type, ByVal wUsage, ByVal dwRop&)

 

' General attribute related API

Declare Function GetDeviceCaps Lib "gdi" (ByVal hDC, ByVal nIndex)

Declare Function GetWindowRect Lib "user" (ByVal hWnd, lpRect As

RectType)

Declare Function GetClientRect Lib "user" (ByVal hWnd, lpRect As

RectType)

 

' Memory allocation related API

Declare Function GlobalAlloc Lib "kernel" (ByVal wFlags, ByVal lMem&)

Declare Function GlobalLock Lib "kernel" (ByVal hMem) As Long

Declare Function GlobalUnlock Lib "kernel" (ByVal hMem)

Declare Function GlobalFree Lib "kernel" (ByVal hMem)

 

' Graphics object related API

Declare Function CreateCompatibleBitmap Lib "gdi" (ByVal hDC,

ByVal nWidth, ByVal nHeight)

Declare Function DeleteObject Lib "gdi" (ByVal hObject)

Declare Function SelectObject Lib "gdi" (ByVal hDC, ByVal hObject)

 

Declare Function ClientToScreen Lib "user" (ByVal hWnd,

As PointType)

Declare Function LPToDP Lib "gdi" (ByVal hDC, p As PointType,

ByVal nCount)

 

Const HORZRES = 8

Const VERTRES = 10

 

Const SRCCOPY = &HCC0020

Const NEWFRAME = 1

Const BITSPIXEL = 12

Const PLANES = 14

 

Const BI_RGB = 0

Const BI_RLE8 = 1

Const BI_RLE4 = 2

 

Const DIB_PAL_COLORS = 1

Const DIB_RGB_COLORS = 0

 

Const GMEM_MOVEABLE = 2

 

4. Add the following function, PrintWindow, to Module1

 

Function PrintWindow (ByVal hDC_Dest, ByVal DestX, ByVal DestY,

ByVal DestDevWidth, ByVal DestDevHeight, ByVal hWnd_SrcWindow)

 

    Dim Rect As RectType

    Dim BitmapInfo As BITMAPINFO_Type

 

    cr$ = Chr$(13)

   

    ' Get the DC for the entire window including the non-client area.

    hDC_Window = GetWindowDC(hWnd_SrcWindow)

    hDC_Mem = CreateCompatibleDC(hDC_Window)

   

    ' Get the pixel dimensions of the screen.  This is necessary so

    ' that we can determine the relative size of the window compared to

    ' the screen

    ScreenWidth = GetDeviceCaps(hDC_Window, HORZRES)

    ScreenHeight = GetDeviceCaps(hDC_Window, VERTRES)

 

    ' Get the pixel dimensions of the window to be printed.

    r = GetWindowRect(hWnd_SrcWindow, Rect)

    Window_Width = Abs(Rect.Right - Rect.Left)

    Window_Height = Abs(Rect.Bottom - Rect.Top)

   

    ' Create a bitmap compatible with the window DC.

    hBmp_Window = CreateCompatibleBitmap(hDC_Window, Window_Width,

                                          Window_Height)

 

    ' Select the bitmap to hold the window image into the memory DC.

    hPrevBmp = SelectObject(hDC_Mem, hBmp_Window)

 

    ' Copy the image of the window to the memory DC.

    r1 = BitBlt(hDC_Mem, 0, 0, Window_Width, Window_Height,

                 hDC_Window, 0, 0, SRCCOPY)

 

    BitsPerPixel = GetDeviceCaps(hDC_Mem, BITSPIXEL)

    ColorPlanes = GetDeviceCaps(hDC_Mem, PLANES)

 

    BitmapInfo.BitmapInfoHeader.biSize = 40

    BitmapInfo.BitmapInfoHeader.biWidth = Window_Width

    BitmapInfo.BitmapInfoHeader.biHeight = Window_Height

    BitmapInfo.BitmapInfoHeader.biPlanes = 1

    BitmapInfo.BitmapInfoHeader.biBitCount = BitsPerPixel

                                              * ColorPlanes

    BitmapInfo.BitmapInfoHeader.biCompression = BI_RGB

    BitmapInfo.BitmapInfoHeader.biSizeImage = 0

    BitmapInfo.BitmapInfoHeader.biXPelsPerMeter = 0

    BitmapInfo.BitmapInfoHeader.biYPelsPerMeter = 0

    BitmapInfo.BitmapInfoHeader.biClrUsed = 0

    BitmapInfo.BitmapInfoHeader.biClrImportant = 0

   

    ' Calculate the ratios based on the source and destination

    ' devices. This will help to cause the size of the window image

    ' to be approximately the same proportion on another device

    ' such as a printer.

    WidthRatio! = Window_Width / ScreenWidth

    HeightAspectRatio! = Window_Height / Window_Width

 

    PrintWidth = WidthRatio! * DestDevWidth

    PrintHeight = HeightAspectRatio! * PrintWidth

 

    ' Calculate the number of bytes needed to store the image assuming

    ' 8 bits/pixel.

    BytesNeeded& = CLng(Window_Width + 1) * (Window_Height + 1)

 

    ' Allocate a buffer to hold the bitmap bits.

    hMem = GlobalAlloc(GMEM_MOVEABLE, BytesNeeded&)

   

    If hDC_Window <> 0 And hBmp_Window <> 0 And hDC_Dest <> 0 And

       hMem <> 0 Then

       

        lpBits& = GlobalLock(hMem)

       

        ' Get the bits that make up the image and copy them to the

        ' destination device.

        r2 = GetDIBits(hDC_Mem, hBmp_Window, 0, Window_Height, lpBits&,

                        BitmapInfo, DIB_RGB_COLORS)

        r3 = StretchDIBits(hDC_Dest, DestX, DestY, PrintWidth,

                            PrintHeight, 0, 0, Window_Width,

                            Window_Height, lpBits&, BitmapInfo,

                            DIB_RGB_COLORS, SRCCOPY)

    End If

 

    ' Reselect in the previous bitmap and select out the source

    ' image bitmap.

    r = SelectObject(hDC_Mem, hPrevBmp)

 

    ' Release or delete DC's, memory and objects.

    r = GlobalUnlock(hMem)

    r = GlobalFree(hMem)

    r = DeleteDC(hDC_Window)

    r = DeleteObject(hBmp_Window)

    r = ReleaseDC(hWnd_SrcWindow, hDC_Form)

   

    ' Return true if the window was successfully printed.

    If r2 <> 0 And r3 <> 0 Then

        PrintWindow = True

    Else

        PrintWindow = False

    End If

 

End Function

 

6. Add the following function, PrintClient, to Module1

 

Function PrintClient (ByVal hDC_Dest, ByVal DestX, ByVal DestY,

ByVal DestDevWidth, ByVal DestDevHeight, ByVal hWnd_SrcWindow)

 

    Dim Rect As RectType, RectClient As RectType

    Dim BitmapInfo As BITMAPINFO_Type

    Dim pWindow As PointType, pClient As PointType, pDiff As PointType

 

    cr$ = Chr$(13)

   

    ' Get the DC for the entire window including the non-client area.

    hDC_Window = GetWindowDC(hWnd_SrcWindow)

    hDC_Mem = CreateCompatibleDC(hDC_Window)

   

    ' Get the pixel dimensions of the screen.

    ScreenWidth = GetDeviceCaps(hDC_Window, HORZRES)

    ScreenHeight = GetDeviceCaps(hDC_Window, VERTRES)

 

    ' Get the pixel dimensions of the window to be printed.

    r = GetWindowRect(hWnd_SrcWindow, Rect)

    Window_Width = Abs(Rect.Right - Rect.Left)

    Window_Height = Abs(Rect.Bottom - Rect.Top)

   

    ' Create a bitmap compatible with the window DC.

    hBmp_Window = CreateCompatibleBitmap(hDC_Window, Window_Width,

                                          Window_Height)

 

    ' Select the bitmap to hold the window image into the memory DC.

    hPrevBmp = SelectObject(hDC_Mem, hBmp_Window)

 

    ' Copy the image of the window to the memory DC.

    r1 = BitBlt(hDC_Mem, 0, 0, Window_Width, Window_Height,

                hDC_Window, 0, 0, SRCCOPY)

   

    ' Get the dimensions of the client area.

    r = GetClientRect(hWnd_SrcWindow, RectClient)

    Client_Width = Abs(RectClient.Right - RectClient.Left)

    Client_Height = Abs(RectClient.Bottom - RectClient.Top)

 

    ' Calculate the pixel difference (x and y) between the upper-left

    ' corner of the non-client area and the upper-left corner of the

    ' client area.

    pClient.x = RectClient.Left

    pClient.y = RectClient.Top

    r = ClientToScreen(hWnd_SrcWindow, pClient)

 

    xDiff = Abs(pClient.x - Rect.Left)

    yDiff = Abs(pClient.y - Rect.Top)

   

    ' Create a DC and bitmap to represent the client area of the

    ' window.

    hDC_MemClient = CreateCompatibleDC(hDC_Window)

    hBmp_Client = CreateCompatibleBitmap(hDC_Window, Client_Width,

                                          Client_Height)

    hBmpClientPrev = SelectObject(hDC_MemClient, hBmp_Client)

 

    ' Bitblt client area of window to memory bitmap representing the

    ' client area.

    r = BitBlt(hDC_MemClient, 0, 0, Client_Width, Client_Height,

                hDC_Mem, xDiff, yDiff, SRCCOPY)

   

    ' Reselect in the previous bitmap and select out the source

    ' image bitmap.

    r = SelectObject(hDC_Mem, hPrevBmp)

 

    ' Delete the DC a and bitmap associated with the window.

    r = DeleteDC(hDC_Window)

    r = DeleteObject(hBmp_Window)

 

    BitsPerPixel = GetDeviceCaps(hDC_MemClient, BITSPIXEL)

    ColorPlanes = GetDeviceCaps(hDC_MemClient, PLANES)

   

    BitmapInfo.BitmapInfoHeader.biSize = 40

    BitmapInfo.BitmapInfoHeader.biWidth = Client_Width

    BitmapInfo.BitmapInfoHeader.biHeight = Client_Height

    BitmapInfo.BitmapInfoHeader.biPlanes = 1

    BitmapInfo.BitmapInfoHeader.biBitCount = BitsPerPixel

                                              * ColorPlanes

    BitmapInfo.BitmapInfoHeader.biCompression = BI_RGB

    BitmapInfo.BitmapInfoHeader.biSizeImage = 0

    BitmapInfo.BitmapInfoHeader.biXPelsPerMeter = 0

    BitmapInfo.BitmapInfoHeader.biYPelsPerMeter = 0

    BitmapInfo.BitmapInfoHeader.biClrUsed = 0

    BitmapInfo.BitmapInfoHeader.biClrImportant = 0

   

    ' Calculate the ratios based on the source and destination

    ' devices. This will help to cause the size of the window image to

    ' be approximately the same proportion on another device such as

    ' a printer.

    WidthRatio! = Client_Width / ScreenWidth

    HeightAspectRatio! = Client_Height / Client_Width

 

    PrintWidth = WidthRatio! * DestDevWidth

    PrintHeight = HeightAspectRatio! * PrintWidth

 

    ' Calculate the number of bytes needed to store the image assuming

    ' 8 bits/pixel.

    BytesNeeded& = CLng(Window_Width + 1) * (Window_Height + 1)

 

    ' Allocate a buffer to hold the bitmap bits.

    hMem = GlobalAlloc(GMEM_MOVEABLE, BytesNeeded&)

   

    If hDC_Window <> 0 And hBmp_Window <> 0 And hDC_Dest <> 0 And

        hMem <> 0 Then

       

        lpBits& = GlobalLock(hMem)

       

        ' Get the bits that make up the image and copy them to the

        ' destination device.

        r2 = GetDIBits(hDC_MemClient, hBmp_Client, 0, Client_Height,

                        lpBits&, BitmapInfo, DIB_RGB_COLORS)

        r3 = StretchDIBits(hDC_Dest, DestX, DestY, PrintWidth,

                            PrintHeight, 0, 0, Client_Width,

                            Client_Height, lpBits&, BitmapInfo,

                            DIB_RGB_COLORS, SRCCOPY)

    End If

 

    ' Select in the previous bitmap.

    r = SelectObject(hDC_MemClient, hBmpClientPrev)

 

    ' Release or delete DC's, memory and objects.

    r = GlobalUnlock(hMem)

    r = GlobalFree(hMem)

    r = DeleteDC(hDC_MemClient)

    r = DeleteObject(hBmp_Client)

    r = ReleaseDC(hWnd_SrcWindow, hDC_Form)

    

    ' Return true if the window was successfully printed.

    If r2 <> 0 And r3 <> 0 Then

        PrintClient = True

    Else

        PrintClient = False

    End If

 

End Function

 

7. Add DefInt A-Z to the general declarations section of Form1.

 

8. Add the following code to the Command1_Click event:

 

Sub Command1_Click ()

    ' The ScaleMode must be set to pixels for the PrintWindow

    ' routine to print correctly.

    Printer.ScaleMode = 3

      

    ' Change MousePointer to an hourglass.

    Screen.MousePointer = 11

 

    ' Initialize the printer.

    Printer.Print ""

 

    ' Copy the image of the form to the printer.

    ' To print Command1 instead, you can substitute Command1.hWnd for Form1.hWnd

    ' as the last argument.

    r = PrintClient(Printer.hDC, 100, 100, 

                     Printer.ScaleWidth, Printer.ScaleHeight,

                     Form1.hWnd)

   

    ' Display an error if the return value from PrintWindow is zero.

    If Not r Then

        MsgBox "Unable to print the form"

    Else

        Printer.EndDoc

    End If

   

    Screen.MousePointer = 0

 

End Sub

 

9. Add the following code to the Command2_Click event:

 

Sub Command2_Click ()

    ' The ScaleMode must be set to pixels for the PrintWindow

    ' routine to print correctly.

    Printer.ScaleMode = 3

      

    ' Change MousePointer to an hourglass.

    Screen.MousePointer = 11

 

    ' Initialize the printer.

    Printer.Print ""

 

    ' Copy the image of the form to the printer.

    ' To print Command1 instead, you can substitute Command1.hWnd for Form1.hWnd

    ' as the last argument.

    r = PrintWindow(Printer.hDC, 100, 100,

                     Printer.ScaleWidth, Printer.ScaleHeight,

                     Form1.hWnd)

   

    ' Display an error if the return value from PrintWindow is zero.

    If Not r Then

        MsgBox "Unable to print the form"

    Else

        Printer.EndDoc

    End If

   

    Screen.MousePointer = 0

 

End Sub

 

Run the program. Choosing Command1 prints just the client area of

form1. Choosing Command2 prints the entire area of the form. Note

that you can print any of the forms or controls in a project using

this method and control the size and placement of the forms by

changing the second, third, fourth and fifth parameters of the call

to StretchDIBits.  In the example above, the form or control is sized

in proportion to the size of the screen.