top of page

Get and set the active printer ... and list all available printers

  • Writer: John
    John
  • 4 minutes ago
  • 4 min read
This post is one of a series providing implementation examples of Windows API Functions, Types, Enums and Consts using VBA. The code in this post can be used as-is, however, if you regularly (or even just occasionally) work with Windows API declarations in VBA, you may want to see the posts Automatically add Windows API declaration(s) and Using 'F1' to view Windows API web pages which explain some of the functionality that can be added to the VBE by VBE_Extras.

This post demonstrates how to use the GetDefaultPrinter, SetDefaultPrinter and EnumPrinters Windows API Functions to, respectively, get the name of the active printer, set the active printer and list all available printers. As printer names can include Unicode characters then the "W" variants (W stands for "wide" meaning Unicode; the alternative variant is "A" meaning ANSI) of these Functions are used.


To support these Functions, the lstrlen, lstrcpy and RtlMoveMemory (aka "CopyMemory") Windows API Functions are also used to, respectively, get the length of a string, copy a string and copy a block of memory.


Here's a quick summary explanation of each of these Functions and a link to the Microsoft docs to read further.


GetDefaultPrinter uses a familiar pattern with Windows API Functions that "return" a String. You call it once passing in arguments being a pointer to a buffer and a numeric being the size of the buffer. If the buffer is large enough, the String is loaded into the buffer and the numeric is updated to the size (in characters, including terminating null character) and the Function returns True (in this case, any non-zero value). If the buffer is not large enough, the numeric is updated to the required size and the Function returns False (in this case, zero). In that case, we increase the size of the buffer (to that required size) and call the Function for a second time. See the code in GetActivePrinter for the implementation of this.


SetDefaultPrinter takes a single parameter being the name of the printer as a null-terminated String and returns True (in this case, any non-zero value) if successful.


EnumPrinters lists all of the available printers. It is quite a complex Function having many options. It has similarities with GetDefaultPrinter in that it uses a buffer (in this case, of bytes) to receive information and the Function is called twice ... initially to get the size of the buffer then again to receive the bytes into the buffer. Here we use the PRINTER_ENUM_LOCAL and PRINTER_ENUM_CONNECTIONS flags to get a list of the locally installed and connected printers. You can, of course, use different flags if you wish.


The most complex aspect of this Function is processing the returned buffer of bytes. As we pass the number 1 to the 'Level' parameter then the buffer of bytes actually contains information to populate an array of PRINTER_INFO_1 Types (you can use different Types, and matching 'Level's, to get different information). However, we need to do this manually (we can't just somehow set the array of PRINTER_INFO_1 Types to be the buffer of bytes) which is done in the VBA GetAvailablePrinters Function in which we read the values (for 'flags'), and the pointers (for the Strings 'pDescription', 'pName' and 'pComment') from the buffer and load them into individual members of the PRINTER_INFO_1 Types with the help of the CopyMemory Function and, for the Strings, the lstrlen and lstrcpy Functions. See the comments in the VBA code for further details.


RtlMoveMemory (aka "CopyMemory") is one of the few Windows API Functions that is actually a "Sub" (it returns no value) and it's also almost universally known as "CopyMemory" even though it's actual name is "RtlMoveMemory" (right-to-left move memory). It does what it says on the tin ... copies memory from one place to another. Use carefully ... this is a quick way to crash the host application if used incorrectly!


lstrlen just gets the length of a String (excluding terminating null character).


lstrcpy just copies a String ... but you need to ensure the place it is being copied to has enough capacity ... which is why we use lstrlen.


So here is an example for each of the Functions. Each aspect of the functionality has a procedure (or two) that you can run to test it:


  • TestGetActivePrinter to test using the Windows API Function GetDefaultPrinter

  • TestSetActivePrinter to test using the Windows API Function SetDefaultPrinter ... update String to be the name of a printer that is available from your device!

  • TestGetAvailablePrinters1 and TestGetAvailablePrinters2 to test using the Windows API Function EnumPrinters (which uses CopyMemory, lstrlen and lstrcpy as noted above). TestGetAvailablePrinters1 just prints information to the Immediate window. However, to use TestGetAvailablePrinters2, you will need to import "UserFormComboBox" from the Multiple choice UserForm in VBA using a ComboBox post: scroll to the end of the post, download "modules.zip", extract the files from the zip, then (in the VBE menu), click on File > Import File and open (ie to import) the "UserFormComboBox.frm" file ... when you then run TestGetAvailablePrinters2, it will then show the available printers in a ComboBox in a UserForm (with the currently active printer being initially shown in the ComboBox), you can then select a new active printer.




bottom of page