Visual Basic System Services
SetPrinter: Rename Local and Remote Printers
Posted:   Saturday August 06, 2005
Updated:   Monday December 26, 2011
Applies to:   VB4-32, VB5, VB6
Developed with:   VB6, Windows XP
OS restrictions:   See Prerequisites
Author:   VBnet - Randy Birch


AddPrinter: Add/Delete Local/Remote Printers using Existing Drivers
EnumPrinters: Enumerating Local and Network Printers
EnumPrinterDrivers: Enumerate Print Drivers on Specific Printer Server
SetDefaultPrinter: Changing Windows' Default Printer
SetPrinter: Rename Local and Remote Printers /2
WriteProfileString: Changing Windows' Default Printer
None if PRINTER_LEVEL2 is used (see below); for this demo as coded, Windows NT/2000/XP or later.

This is a quick routine that can rename either a local or a remote printer using SetPrinter. In addition to renaming, SetPrinter can be used to set the data for a specified printer or set the state of the specified printer by pausing printing, resuming printing, or clearing all print jobs.

Typical of many APIs, SetPrinter performs specific actions utilizing different levels of data passed. As coded, this demo uses the PRINTER_LEVEL4 structure that is specific to Windows NT/2000/XP as it is the smallest structure that contains the UDT members of interest - pPrinterName and pServerName for remote printers. For other Windows versions the PRINTER_LEVEL2 is required, and although not shown in the demo the UDT is provided below. Just a change to the declarations in the RenamePrinter routine is all that is required to use the level 2 structure.

The Command2 button will open your printer folder so you can add a test printer and view the change applied by the rename function. To minimize the code needed for this demo, the existing and new printer names are hard-coded in Command1, so change as necessary for your test platform. In a production setting where you were providing the renaming ability to a user, you would typically also employ the code from EnumPrinters listed above to determine the installed printers.

There is no "undo" functionality, so keep a copy of the original name handy if required. In order to rename a printer the user must have the rights to use PRINTER_ACCESS_ADMINISTER.

Update: Karl E. Peterson and Randy Smith of MS identified the potential for a problem with the code posted here prior to January 13, 2006. In the original code, I had used both StrPtr and StrConv to assign a pointer to the new printer name directly into the PRINTER_INFO_4 structure.  It was agreed that under some circumstances the memory allotted to the value returned by StrPtr could be de-allocated or moved by Windows, raising the potential for a GPF in the application.  To work around this problem, Karl suggested a mod that would cast the new printer name to a byte buffer, then assign that buffer to the PRINTER_INFO_4 pPrintername member. 

After working up this change, I re-examined the MSDN and based on the info there, have also developed an alternate way of calling SetPrinter using a different declaration of the PRINTER_INFO_4 structure, which can be found at SetPrinter: Rename Local and Remote Printers /2.  In testing this revised method vs. the code below I have found no inherent benefit in using one method over the other, so both are presented for you to choose from.

 BAS Module Code

 Form Code
Add two command buttons (Command1, Command2) to a form along with the following code:

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 SW_SHOWNORMAL As Long = 1

Private Const PRINTER_LEVEL2 As Long = &H2
Private Const PRINTER_LEVEL4 As Long = &H4

Private Const STANDARD_RIGHTS_REQUIRED As Long = &HF0000
Private Const PRINTER_ACCESS_USE As Long = &H8

   pDatatype As String
   pDevMode As Long
   DesiredAccess As Long
End Type

Private Type PRINTER_INFO_2
   pServerName As Long
   pPrinterName As Long
   pShareName As Long
   pPortName As Long
   pDriverName As Long
   pComment As Long
   pLocation As Long
   pDevMode As Long
   pSepFile As Long
   pPrintProcessor As Long
   pDatatype As Long
   pParameters As Long
   pSecurityDescriptor As Long
   Attributes As Long
   Priority As Long
   DefaultPriority As Long
   StartTime As Long
   UntilTime As Long
   Status As Long
   cJobs As Long
   AveragePPM As Long
End Type

Private Type PRINTER_INFO_4
   pPrinterName As Long
   pServerName As Long
   Attributes As Long
End Type

Private Declare Function OpenPrinter Lib "winspool.drv" _
   Alias "OpenPrinterA" _
  (ByVal pPrinterName As String, _
   phPrinter As Long, _
   pDefault As Any) As Long
Private Declare Function ClosePrinter Lib "winspool.drv" _
  (ByVal hPrinter As Long) As Long
Private Declare Function GetPrinter Lib "winspool.drv" _
   Alias "GetPrinterA" _
  (ByVal hPrinter As Long, _
   ByVal Level As Long, _
   pPrinter As Any, _
   ByVal cbBuf As Long, _
   pcbNeeded As Long) As Long
Private Declare Function SetPrinter Lib "winspool.drv" _
   Alias "SetPrinterA" _
  (ByVal hPrinter As Long, _
   ByVal Level As Long, _
   pPrinter As Any, _
   ByVal Command As Long) As Long

Private Declare Sub CopyMemory Lib "kernel32" _
   Alias "RtlMoveMemory" _
  (pTo As Any, uFrom As Any, _
   ByVal lSize As Long)

Private Declare Function ShellExecute Lib "shell32.dll" _
   Alias "ShellExecuteA" _
  (ByVal hwnd As Long, _
   ByVal lpOperation As String, _
   ByVal lpFile As String, _
   ByVal lpParameters As String, _
   ByVal lpDirectory As String, _
   ByVal nShowCmd As Long) As Long

Private Sub Form_Load()

   Command1.Caption = "Rename Printer"
   Command2.Caption = "Open Printers Folder"
End Sub

Private Sub Command1_Click()

   Debug.Print RenamePrinter("Lexmark Z42 Color Jetprinter", "LMColourZ42")
  'Debug.Print RenamePrinter("LMColourZ42", "Lexmark Z42 Color Jetprinter") 
End Sub

Private Sub Command2_Click()

  'just opens the printers folder
   Dim sParams As String
   Dim sDirectory As String

   sParams = vbNullString
   sDirectory = vbNullString
   sParams = "/e,::{2227A280-3AEA-1069-A2DE-08002B30309D}"
   Call ShellExecute(0&, "Open", "explorer.exe", sParams, sDirectory, SW_SHOWNORMAL)

End Sub

Private Function RenamePrinter(sCurrentPrinterName As String, sNewPrinterName As String) As Boolean

   Dim hPrinter As Long
   Dim cbRequired As Long
   Dim cbBuffer As Long
   Dim pi4 As PRINTER_INFO_4
   Dim ptr() As PRINTER_INFO_4
   Dim buff() As Byte   
  'open the printer 
   If OpenPrinter(sCurrentPrinterName, hPrinter, pd) <> 0 Then
     'determine the size of the data for the structure 
      If GetPrinter(hPrinter, PRINTER_LEVEL4, 0&, 0&, cbRequired) = 0 Then
         If cbRequired <> 0 Then
           'create the buffer 
            ReDim ptr((cbRequired \ Len(pi4)))
            cbBuffer = cbRequired
           'and call again 
            If GetPrinter(hPrinter, PRINTER_LEVEL4, ptr(0), cbBuffer, cbRequired) <> 0 Then
               buff = StrConv(sNewPrinterName & vbNullChar, vbFromUnicode)
               pi4.pPrinterName = VarPtr(buff(0))
               CopyMemory ptr(0), pi4, Len(pi4)
              'success will be non-zero; failure is zero      
               RenamePrinter = SetPrinter(hPrinter, PRINTER_LEVEL4, ptr(0), 0&) <> 0
            End If  'GetPrinter/2
         End If  'cbRequired
      End If  'GetPrinter/1
      ClosePrinter hPrinter
   End If  'OpenPrinter

End Function


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