|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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 | |
Related: |
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 |
|
Prerequisites |
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 |
None. |
|
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_ADMINISTER As Long = &H4 Private Const PRINTER_ACCESS_USE As Long = &H8 Private Type PRINTER_DEFAULTS 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 pd As PRINTER_DEFAULTS Dim buff() As Byte pd.DesiredAccess = PRINTER_ACCESS_USE Or PRINTER_ACCESS_ADMINISTER '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 'clean-up ClosePrinter hPrinter End If 'OpenPrinter End Function |
Comments |
|
|
|
|||||
|
|||||
|
|||||
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |