How to Print a Form or Control using StretchDIBits
See Also
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.