VBcoders Guest



Don't have an account yet? Register
 


Forgot Password?



Getting the status of the selected printer from Visual Basic

by Duncan Jones (19 Submissions)
Category: Windows API Call/Explanation
Compatability: Visual Basic 3.0
Difficulty: Advanced
Date Added: Wed 3rd February 2021
Rating: (3 Votes)

Shows how you can use the Windows API to return additional information about the printer above and beyond that which is available through the Visual Basic Printer object.

Rate Getting the status of the selected printer from Visual Basic

 1. What the Printer object missed


    Printing has long been a very problematic part of developing complete 
   and professional applications in Visual Basic. This was redressed to a large degree with the new Printer 
   object introduced in Visual Basic 4.

    However, there are shortcomings with this object. The biggest shortcoming is that you cannot find out whether 
   the printer is ready, busy, out of paper etc. from your application.

   However, there is an API call, GetPrinter which returns a great deal more information about a printer.
   


   
   
   
   Private Declare Function GetPrinterApi Lib "winspool.drv" Alias _ 

          "GetPrinterA" (ByVal hPrinter As Long, _ 

            ByVal Level As Long, _ 

            buffer As Long, _ 

            ByVal pbSize As Long, _ 

            pbSizeNeeded As Long) As Long
   
   


    This takes the handle to a printer in hPrinter and fills
   the buffer provided to it with information from the printer driver. To get the handle from the Printer object,
   you need to use the OpenPrinter API call. 

    This handle must be released using the ClosePrinter API call as soon as you are finished with it.
   


   
   
   
   Private Type PRINTER_DEFAULTS 
 
     pDatatype As String 

     pDevMode As DEVMODE 

     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 

   

   Private Declare Function ClosePrinter Lib "winspool.drv" _ 

       (ByVal hPrinter As Long) As Long 

   


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


   
   
   
    Dim lret As Long 

    Dim pDef As PRINTER_DEFAULTS 

    

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


    2. The different statuses


    There are a number of standard statuses that can be returned by the 
   printer driver.
   


   
   
   
   Public Enum Printer_Status 

      PRINTER_STATUS_READY = &H0 

      PRINTER_STATUS_PAUSED = &H1 

      PRINTER_STATUS_ERROR = &H2 
   PRINTER_STATUS_PENDING_DELETION =
    &H4 

      PRINTER_STATUS_PAPER_JAM = &H8 
   PRINTER_STATUS_PAPER_OUT =
    &H10 
   PRINTER_STATUS_MANUAL_FEED =
    &H20 
   PRINTER_STATUS_PAPER_PROBLEM =
    &H40 

      PRINTER_STATUS_OFFLINE = &H80 
   PRINTER_STATUS_IO_ACTIVE =
    &H100 
   PRINTER_STATUS_BUSY =
    &H200 

      PRINTER_STATUS_PRINTING = &H400 

      PRINTER_STATUS_OUTPUT_BIN_FULL = &H800 
   PRINTER_STATUS_NOT_AVAILABLE =
    &H1000 

      PRINTER_STATUS_WAITING = &H2000 
   PRINTER_STATUS_PROCESSING =
    &H4000 
   PRINTER_STATUS_INITIALIZING =
    &H8000 
   PRINTER_STATUS_WARMING_UP =
    &H10000 
   PRINTER_STATUS_TONER_LOW =
    &H20000 
   PRINTER_STATUS_NO_TONER =
    &H40000 
   PRINTER_STATUS_PAGE_PUNT =
    &H80000 
   PRINTER_STATUS_USER_INTERVENTION =
    &H100000 
   PRINTER_STATUS_OUT_OF_MEMORY =
    &H200000 
   PRINTER_STATUS_DOOR_OPEN =
    &H400000 
   PRINTER_STATUS_SERVER_UNKNOWN =
    &H800000 
   PRINTER_STATUS_POWER_SAVE =
    &H1000000 

   End Enum
   
   


   3. The data structures


   
  
   As each printer driver is responsible for returning 
   this data there has to be a standard to which this returned data conforms 
   in order for one application to be able to query a number of different 
   types of printers. As it happens, there are nine different standard data 
   types that can be returned by the GetPrinter
   API call in Windows 2000 (only the first two are universal to all current versions of Windows). 

  Of these, the second is the most interesting - named PRINTER_INFO_2 
  
  
  
  
  Private Type PRINTER_INFO_2 

     pServerName As String 

     pPrinterName As String 

     pShareName As String 

     pPortName As String 

     pDriverName As String 

     pComment As String 

     pLocation As String 

     pDevMode As Long 

     pSepFile As String 

     pPrintProcessor As String 

     pDatatype As String 

     pParameters As String 

     pSecurityDescriptor As Long 

     Attributes As Long 

     Priority As Long 

     DefaultPriority As Long 

     StartTime As Long 

     UntilTime As Long 

     Status As Long 

     JobsCount As Long 

     AveragePPM As Long 

  End Type
  
  


   
  
  However, it is not as simple as just passing this structure to the GetPrinter API call as a printer can
  return more information than is in that structure and if you do not allocate sufficent buffer space for it to 
  do so your application will crash. 

  Fortunately the API call caters for this - if you pass zero in the pbSize parameter then the API call will
  tell you how big a buffer you will require in the pbSizeNeeded

  This means that filling the information from the printer driver becomes a two step process:
   
  


  
    
    Dim lret As Long 

    Dim SizeNeeded As Long

  

    Dim buffer() As Long

  

    ReDim Preserve buffer(0 To 1) As Long 

    lret = GetPrinterApi(mhPrinter, Index, buffer(0), UBound(buffer), SizeNeeded) 

    ReDim Preserve buffer(0 To (SizeNeeded / 4) + 3) As Long 

    lret = GetPrinterApi(mhPrinter, Index, buffer(0), UBound(buffer) * 4, SizeNeeded) 

  


  
   
  
  However the buffer is just an array of Long data types. Some of the data within the PRINTER_INFO_2 
  data structure is String data. This must be collected from the addresses which are stored in the appropriate
  buffer position. 
  
  

 
  
  To get a string from a pointer the CopyMemory API call is used and there is also an API call,
  IsBadStringPtr, which can be used to verify that the address pointed to does actually contain a valid
  string.
   
  


  
  
  
  '\\ Memory manipulation routines 

  Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) 

  '\\ Pointer validation in StringFromPointer 

  Private Declare Function IsBadStringPtrByLong Lib "kernel32" Alias "IsBadStringPtrA" (ByVal lpsz As Long, ByVal ucchMax As Long) As Long 

  


  
  
  Retrieving the string from a pointer is a common thing to have to do so it is worth having this utility function
  in your arsenal.
  
  


  
  
  Public Function StringFromPointer(lpString As Long, lMaxLength As Long) As String

  

    Dim sRet As String

    Dim lret As Long

  

    If lpString = 0 Then

      StringFromPointer = ""

      Exit Function

    End If

  

    If IsBadStringPtrByLong(lpString, lMaxLength) Then

      '\\ An error has occured - do not attempt to use this pointer

        StringFromPointer = ""

      Exit Function

    End If

  

    '\\ Pre-initialise the return string...

    sRet = Space$(lMaxLength)

    CopyMemory ByVal sRet, ByVal lpString, ByVal Len(sRet)

    If Err.LastDllError = 0 Then

      If InStr(sRet, Chr$(0)) > 0 Then 

        sRet = Left$(sRet, InStr(sRet, Chr$(0)) - 1)

      End If

    End If

  

    StringFromPointer = sRet

  

  End Function

  


   So to use this to populate your PRINTER_INFO_2 variable:
  


  
  With mPRINTER_INFO_2 '\\ This variable is of type PRINTER_INFO_2

     .pServerName = StringFromPointer(buffer(0), 1024)

     .pPrinterName = StringFromPointer(buffer(1), 1024)

     .pShareName = StringFromPointer(buffer(2), 1024)

     .pPortName = StringFromPointer(buffer(3), 1024)

     .pDriverName = StringFromPointer(buffer(4), 1024)

     .pComment = StringFromPointer(buffer(5), 1024)

     .pLocation = StringFromPointer(buffer(6), 1024)

     .pDevMode = buffer(7)

     .pSepFile = StringFromPointer(buffer(8), 1024)

     .pPrintProcessor = StringFromPointer(buffer(9), 1024)

     .pDatatype = StringFromPointer(buffer(10), 1024)

     .pParameters = StringFromPointer(buffer(11), 1024)

     .pSecurityDescriptor = buffer(12)

     .Attributes = buffer(13)

     .Priority = buffer(14)

     .DefaultPriority = buffer(15)

     .StartTime = buffer(16)

     .UntilTime = buffer(17)

     .Status = buffer(18)

     .JobsCount = buffer(19)

     .AveragePPM = buffer(20)

  End With
  


    


   
   The complete source code for these examples is available for download ">here

   You will be asked to register with Yahoo!Groups in order to access it.
   
   

Download this snippet    Add to My Saved Code

Getting the status of the selected printer from Visual Basic Comments

No comments have been posted about Getting the status of the selected printer from Visual Basic. Why not be the first to post a comment about Getting the status of the selected printer from Visual Basic.

Post your comment

Subject:
Message:
0/1000 characters