1. Computing

How to use the System.Drawing.Printing components in VB.NET

But beware the bug Microsoft says isn't there!


Updated September 21, 2008

If you need to print in VB.NET, there are great components available to help you do the job. But there are some traps too. In particular, I found a way to trigger an exception that, as I read Microsoft's documentation, is impossible.

All will be explained!

First, the PrintDocument object. PrintDocument can be added to your project from the ToolBox. It contains everything you need for printing from a Windows Forms application. (Unfortunately, Classes in System.Drawing.Printing don't work well with a Windows service or an ASP.NET application or service.)

The main properties you will need to pay attention to when using it are:

  • PrinterSettings
  • DefaultPageSettings
  • PrintController

For example, if your printer supports it, you can specify double sided printing (use the Duplex property) with the PrinterSettings property. You can control things like margins, paper size and landscape or portrait mode using the DefaultPageSettings properties. The PrintController object consists mostly of methods such as OnStartPrint and OnEndPage that you override in your code if you want to use them. Here's some example code:

Public Class myPrintControllerClass
   Inherits Printing.PrintController
   Public Overrides Sub OnStartPrint( _
      ByVal document As PrintDocument, _
      ByVal e As PrintEventArgs)
      MyBase.OnStartPrint(document, e)
   End Sub
End Class

VB.NET provides three dialogs to help you get the job done:

  • PageSetupDialog
  • PrintDialog
  • PrintPreviewDialog

The PageSetupDialog looks like this:

Click Here to display the illustration
Click the Back button on your browser to return

The PrintDialog looks like this:

Click Here to display the illustration
Click the Back button on your browser to return

Setting up the dialogs is easy. Just assign your document to the dialog object and use the ShowDialog method to display it.

myPrintDialog.Document = myDocument

Ordinarily, when printing from VB.NET, you handle the PrintPage event. In this example, some print output is created using GDI+ just to demonstrate the print preview technique.

Private Sub myDocument_PrintPage( _
ByVal sender As Object, _
ByVal e As System.Drawing.Printing.PrintPageEventArgs) _
   Handles myDocument.PrintPage
   e.Graphics.DrawString(vbNewLine & "About Visual Basic" & _
   vbNewLine & "http://visualbasic.about.com", _
   New Font("arial", 32, FontStyle.Italic), _
   Brushes.Red, 0, 0)
e.HasMorePages = False
End Sub

The end result can be seen below:

Click Here to display the illustration
Click the Back button on your browser to return

This short example wouldn't be complete if I didn't cover the discovery of a really subtle bug while working on the examples here.

My first attempts to code an example met with utter failure because I kept getting a System.AccessViolationException:

Click Here to display the illustration
Click the Back button on your browser to return

Microsoft's documentation about it, to my eye, says it can't even happen:

In programs consisting entirely of verifiable managed code, all references are either valid or null, and access violations are impossible. An AccessViolationException occurs only when verifiable managed code interacts with unmanaged code or with unsafe managed code.

But happen it does. And I was able to find dozens of cases where it had happened to other people too. (But absolutely no solutions.) Microsoft does provide an update for XP and Framework 2.0, but I was running Vista and Framework 3.5. The documentation of that patch suggests that it fixes a fairly rare problem as well.

The answer reveals something interesting about .NET. As it turns out, I normally use a printer that is attached to a different computer on a network. And that computer was turned off. I believe that, even to display a print preview, the .NET System.Drawing.Printing object attempts to write to the print buffer of the Windows default printer. That memory is, for some reason, inaccessable, and an AccessViolationException is the result. In XP, a different error, InvalidPrinterException, is the result instead.

I was able to fix the error by simply making the default printer a software XPS printer instead. After that, things worked fine.

If you know how to fix this problem in some other way, let me know!

  1. About.com
  2. Computing
  3. Visual Basic
  4. Using VB.NET
  5. Printing in VB.NET

©2014 About.com. All rights reserved.