Changing the status of the selected printer from Visual Basic

Getting permission to administer the printer

 Every user is allowed to get the printer status but only users with sufficient access rights may change the printer settings.
 To request this level of access to a printer you must set the DesiredAccess member of the PRINTER_DEFAULTS structure passed to the OpenPrinter call.

Private Type PRINTER_DEFAULTS
  pDatatype As String
  pDevMode As Long
  DesiredAccess As Long
End Type

Private Declare Function OpenPrinter Lib "winspool.drv" _
    Alias "OpenPrinterA" (ByVal pPrinterName As String, _
    phPrinter As Long, pDefault As PRINTER_DEFAULTS) As Long

Public Enum PrinterAccessRights
  PRINTER_ACCESS_ADMINISTER = &H4
  PRINTER_ACCESS_USE = &H8
  PRINTER_ALL_ACCESS = &HF000C
End Enum

 You pass the Printer.DeviceName to OpenPrinter this to get the handle.

Dim lret As Long
Dim pDef As PRINTER_DEFAULTS
pdef.DesiredAccess = PRINTER_ALL_ACCESS

lret = OpenPrinter(Printer.DeviceName, mhPrinter, pDef)

If the user has sufficient priveledge to administer the printer then the handle mhPrinter will be set and can be used in subsequent calls to GetPrinter and SetPrinter
You should check that no error has occured by checking that Err.LastDllError is zero.

Controlling the printer

 The printer can be paused and resumed and the jobs may be purged from it using the printer control commands.  

Public Enum Printer_Control_Commands
  PRINTER_CONTROL_PAUSE = 1
  PRINTER_CONTROL_RESUME = 2
  PRINTER_CONTROL_PURGE = 3
  PRINTER_CONTROL_SET_STATUS = 4
End Enum

 These commands are sent to the printer using the SetPrinter api call.

Private Declare Function SetPrinterApi Lib "winspool.drv" Alias _
  "SetPrinterA" (ByVal hPrinter As Long, _
  ByVal Level As Long, _
  buffer As Long, _
  ByVal Command As Long) As Long

 Thus the code to control the printer would look like:

Public Sub PrinterCommand (ByVal DeviceName As String, ByVal cmd As Printer_Control_Commands )

Dim lRet As Long
Dim mhPrinter As Long
Dim pDef As Printer_Defaults

'\\ Open the printer in admin. mode
 pdef.DesiredAccess = PRINTER_ALL_ACCESS
 lret = OpenPrinter(DeviceName, mhPrinter, pDef)

'\\ Send the command
' (both Level and Buffer are empty to send commands)
 lRet = SetPrinterApi(mhPrinter, 0, 0,cmd)
'\\ Close the printer
 lRet = ClosePrinter(mhPrinter)

End Sub

Setting the status of the current job

  In the above code passing Printer_Control_Set_Status in the cmd parameter has no effect.  This is because to set the status of the current job you need to pass the new status in the pPrinter parameter of the SetPrinter call:

Public Sub SetPrinterStatus(ByVal DeviceName As String, ByVal newStatus As Printer_Status)

Dim lRet As Long
Dim mhPrinter As Long
Dim pDef As Printer_Defaults

'\\ Open the printer in admin. mode
 pdef.DesiredAccess = PRINTER_ALL_ACCESS
 lret = OpenPrinter(DeviceName, mhPrinter, pDef)

'\\ Set the new status
 lRet = SetPrinterApi(mhPrinter, newStatus, 0,PRINTER_CONTROL_SET_STATUS)
'\\ Close the printer
 lRet = ClosePrinter(mhPrinter)

End Sub

Changing other properties of the selected printer

  As seen in this article there a number of additional properties that you can get related to the printer - such as its location, comment etc.
 However, if the value of the buffer is not zero then you need to get the buffer from the printer, modify the bits you want to change and then pass that buffer back to the printer driver.  If you change the size of the buffer then you will cause a GPF.

 In this example we change the comment for the printer:

  Dim lret As Long
  Dim SizeNeeded As Long

  Dim buffer() As Long

  ReDim Preserve buffer(0 To 1) As Long
  lret = GetPrinterApi(mhPrinter, 2, buffer(0), UBound(buffer), SizeNeeded)
  ReDim Preserve buffer(0 To (SizeNeeded / 4) + 3) As Long
  lret = GetPrinterApi(mhPrinter, 2, buffer(0), UBound(buffer) * 4, SizeNeeded)

  The result from this is an array of Long data types, part of which corresponds to a PRINTER_INFO_2 data structure.  From this structure we can deduce that the comment is held in the memory address pointed to by buffer(5).  To change the comment we just set this buffer to the address of a string that contains the new comment.

Getting a pointer to a string

 You can get the memory address (pointer) for any Visual basic string using the undocumented StrPtr function:

lpString = StrPtr(sVar)

  However, since we are using the ANSI version of the Api call, and Visual Basic 4 or above always stores strings as unicode internally, we must convert the string to ANSI before getting the resultant string's address. To do this we use the StrConv function:

sVar = StrConv(sVar, vbFromUnicode)

  Thus the resulting code is:

Dim lpString As Long
Dim sVar As String

sVar = "New printer comment"
sVar = StrConv(sVar, vbFromUnicode)
lpString = StrPtr(sVar)
buffer(5) = lpString
'\\ Save the new setting to the printer
lRet = SetPrinter(mhPrinter,2,buffer(0),0)

If you have any feedback on this article, or any other on this site, you can contact us with this form