Visual Basic System Services
WriteProfileString: Changing Windows' Default Printer
Posted:   Monday March 20, 2000
Updated:   Monday December 26, 2011
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows NT4
OS restrictions:   None
Author:   VBnet - Randy Birch


AddPort: Adding and Deleting Application-Defined Ports
AddPrinter: Add/Delete Local/Remote Printers using Existing Drivers
EnumPorts: Identify Windows' Available Ports
EnumPrinters: Enumerating Local and Network Printers
EnumPrinterDrivers: Enumerate Print Drivers on Specific Printer Server
SetDefaultPrinter: Changing Windows' Default Printer
WriteProfileString: Changing Windows' Default Printer

I suppose that this page could really be listed as a FAQ - it seems not a week goes by without a newsgroup post asking how to change the default printer from VB. This page details the steps to achieve this using any Windows version, and is loosely based on the MSKB article Q167735 - FIX: Setting Printer to Item in the Printers Collection Fails. The article also discusses the more complex method of doing this under Win9x, for those interested.

The traditional approach to setting the default printer, and one that should work according to documentation, is to enumerate the Printers collection until the desired printer is located, then assigning that to the Printer object. This doesn't work as advertised. The printer name will change, but output still goes to the original default device.

Under Win9x, installed printers are listed in win.ini under the PrinterPorts key, with the system default printer listed under the Device key, and various documentation suggests that changing this key sets the default printer. But if you examine the win.ini file on a Window NT or later system, the file does not contain any printer port or default printer info. Not to worry.

By default, using either GetProfileString / WriteProfileString or GetPrivateProfileString / WritePrivateProfileString on Window NT or later, the APIs actually first look to the registry under the key

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\IniFileMapping

for the ini file name of interest. If it's not found, then the physical disk ini file itself is checked.

In the case of win.ini, if it's found in the registry under the key above, it is further examined for the section name desired (PrinterPorts). The registry value assigned to this key point to the location within the registry of the actual installed printers data (which in my case this points to "USR:Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts").

Under the PrinterPorts key the installed printers are listed. As well, the default printer setting corresponding to the expected ini file "Device" entry is stored in the \windows section under that same parent key. When an operation has been mapped, the Get(Private)ProfileString functions retrieve information from the registry, not from the physical initialization file. This change in the storage location has no effect on the function's behavior and is transparent to the calling application.

What this all means is that for Windows 9x the code sets the default printer. And for Window NT or later it means there is no need to resort to registry-reading/writing calls - the familiar ini file APIs can be utilized to change the default printer as outlined below. The code provides the means to enumerate the installed printers and to set a new default. Note that in performing this that changing the default printer affects all applications on the system. Therefore, it would be prudent to provide additional code to reset the original user's preference once printing has completed.

To see the effect of this demo, I recommend constructing as below, then start an additional new project (so that there is no interaction between VB projects open), add a VB Common Dialog control and single button with the single command "CommonDialog1.ShowPrinter". Run this and keep it open. Also open your Printers folder and watch the default printer settings change.

 BAS Module Code

 Form Code
Create a form containing a list box and command button, using the default names. Add the following code to the form:

Option Explicit
' Copyright 1996-2011 VBnet/Randy Birch, All Rights Reserved.
' Some pages may also contain other copyrights by the author.
' Distribution: You can freely use this code in your own
'               applications, but you may not reproduce 
'               or publish this code on any web site,
'               online service, or distribute as source 
'               on any media without express permission.
Private Const HWND_BROADCAST As Long = &HFFFF&
Private Const WM_WININICHANGE As Long = &H1A

Private Declare Function GetProfileString Lib "kernel32" _
   Alias "GetProfileStringA" _
  (ByVal lpAppName As String, _
   ByVal lpKeyName As String, _
   ByVal lpDefault As String, _
   ByVal lpReturnedString As String, _
   ByVal nSize As Long) As Long

Private Declare Function WriteProfileString Lib "kernel32" _
   Alias "WriteProfileStringA" _
  (ByVal lpszSection As String, _
   ByVal lpszKeyName As String, _
   ByVal lpszString As String) As Long
Private Declare Function SendNotifyMessage Lib "user32" _
   Alias "SendNotifyMessageA" _
  (ByVal hwnd As Long, _
   ByVal msg As Long, _
   ByVal wParam As Long, _
   lParam As Any) As Long   

Private Sub Form_Load()
    ProfileLoadWinIniList List1, "PrinterPorts"
    Command1.Enabled = False
End Sub

Private Sub Command1_Click()

   Call SetDefaultPrinterWinNT

End Sub

Private Sub List1_Click()

   Command1.Enabled = List1.ListIndex > -1
End Sub

Private Sub SetDefPrinter(ByVal PrinterName As String, _
                          ByVal DriverName As String, _
                          ByVal PrinterPort As String)
   Dim DeviceLine As String
  'rebuild a valid device line string
   DeviceLine = PrinterName & "," & DriverName & "," & PrinterPort
  'Store the new printer information in the
  '[WINDOWS] section of the WIN.INI file for
  'the DEVICE= item
   Call WriteProfileString("windows", "Device", DeviceLine)
  'Cause all applications to reload the INI file
   Call SendNotifyMessage(HWND_BROADCAST, WM_WININICHANGE, 0, ByVal "windows")
End Sub

Private Sub GetDriverAndPort(ByVal Buffer As String, _
                             DriverName As String, _
                             PrinterPort As String)

   Dim posDriver As Long
   Dim posPort As Long
   DriverName = ""
   PrinterPort = ""

  'The driver name is first in the string
  'terminated by a comma
   posDriver = InStr(Buffer, ",")
   If posDriver > 0 Then

     'Strip out the driver name
      DriverName = Left(Buffer, posDriver - 1)

     'The port name is the second entry after
     'the driver name separated by commas.
      posPort = InStr(posDriver + 1, Buffer, ",")

      If posPort > 0 Then
        'Strip out the port name
         PrinterPort = Mid(Buffer, posDriver + 1, posPort - posDriver - 1)
       End If
   End If
End Sub

Private Function ProfileLoadWinIniList(lst As ListBox, _
                                       lpSectionName As String) As Long

'Load the listbox data from win.ini.

   Dim success As Long
   Dim nSize As Long
   Dim lpKeyName As String
   Dim ret As String
  'call the API passing null as the parameter
  'for the lpKeyName parameter. This causes
  'the API to return a list of all keys under
  'that section. Pad the passed string large
  'enough to hold the data. Adjust to suit.
   ret = Space$(8102)
   nSize = Len(ret)
   success = GetProfileString(lpSectionName, _
                              vbNullString, _
                              "", _
                              ret, _
  'The returned string is a null-separated
  'list terminated by a pair of null characters.
   If success Then
     'trim terminating null and trailing spaces
      ret = Left$(ret, success)
        'with the resulting string,
        'extract each element
         Do Until ret = ""
           'strip off an item and
           'add the item to the listbox
            lpKeyName = StripNulls(ret)
            lst.AddItem lpKeyName
   End If
  'return the number of items as an
  'indicator of success
   ProfileLoadWinIniList = lst.ListCount

End Function

Private Function StripNulls(startstr As String) As String

 'Take a string separated by chr$(0)
 'and split off 1 item, shortening the
 'string so next item is ready for removal.
  Dim pos As Long

  pos = InStr(startstr$, Chr$(0))
  If pos Then
      StripNulls = Mid$(startstr, 1, pos - 1)
      startstr = Mid$(startstr, pos + 1, Len(startstr))
  End If

End Function

Private Sub SetDefaultPrinterWinNT()

   Dim Buffer As String
   Dim DeviceName As String
   Dim DriverName As String
   Dim PrinterPort As String
   Dim PrinterName As String
   Dim r As Long
   If List1.ListIndex > -1 Then
     'Get the printer information for the currently selected
     'printer in the list. The information is taken from the
     'WIN.INI file.
      Buffer = Space(1024)
      PrinterName = List1.list(list1.ListIndex)
      Call GetProfileString("PrinterPorts", _
                             PrinterName, "", _
                             Buffer, Len(Buffer))
     'Parse the driver name and port name out of the buffer
      GetDriverAndPort Buffer, DriverName, PrinterPort

      If (Len(DriverName) > 0) And (Len(PrinterPort) > 0) Then
         SetDefPrinter PrinterName, DriverName, PrinterPort
      End If
    End If
End Sub
Save the program and run. With the Printers folder open, change the default printer from the project. The appropriate printer icon should immediately reflect the change. If you have constructed a second project to show the VB common dialog, it will open with the current default printer selected. In addition, checking the Printer object's DeviceName property will also return the current selection.


PayPal Link
Make payments with PayPal - it's fast, free and secure!


Copyright 1996-2011 VBnet and Randy Birch. All Rights Reserved.
Terms of Use  |  Your Privacy


Hit Counter