Visual Basic Enumeration/Callback Routines

EnumPrinters: Enumerating Local and Network Printers
     
Posted:   Friday September 17, 1999
Updated:   Monday December 26, 2011
     
Applies to:   VB5, VB6
Developed with:   VB6, Windows NT4, Windows XP
OS restrictions:   None
Author:   VBnet - Randy Birch
     
Related:   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
     
 Prerequisites
None. As coded requires choosing one of the two OS versions - the NT version will fail on 9x.  For code to programmatically determine the Windows version, use the IsWinNT4Plus function from GetVersionEx: Windows Version Info (Wrapper Routines). If IsWinNT4Plus fails, call the 9x routine, otherwise call the NT routine.

The EnumPrinters API is used to enumerate available printers, print servers, domains, or print providers. Different information can be obtained depending on the network setup and the flags passed in the EnumPrinters call. This sample is based on code posted to the msnews groups.

A caveat is needed with the EnumPrinters method on NT3.51 or NT4. As outlined in Q153101, when the calling process is a service, or an application running in the context of a service (for example, started by the scheduler service), calls EnumPrinters with the PRINTER_ENUM_CONNECTIONS flag to get printer connections to a Windows NT Server, the API might fail with error code 122 (ERROR_INSUFFICIENT_BUFFER) even if you have specified plenty of buffer space. It will return an arbitrary size of memory needed in pcbNeeded. This is a confirmed problem, and interestingly the article states "The problem will be fixed in Windows NT 4.0. The API will not return connected printers if it cannot access the current user hive".    

 BAS Module Code
Place the following code into the general declarations area of a bas module:

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.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Type PRINTER_INFO_1
   Flags As Long
   pDescription As Long
   Pane As Long
   Comment As Long
End Type

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

'SIZEOFxxx are non-windows constants defined for this method
Public Const SIZEOFPRINTER_INFO_1 = 16
Public Const SIZEOFPRINTER_INFO_4 = 12

Public Const PRINTER_LEVEL1 = &H1
Public Const PRINTER_LEVEL4 = &H4

'EnumPrinters enumerates available printers,
'print servers, domains, or print providers.
Public Declare Function EnumPrinters Lib "winspool.drv" _
   Alias "EnumPrintersA" _
  (ByVal Flags As Long, _
   ByVal Name As String, _
   ByVal Level As Long, _
   pPrinterEnum As Any, _
   ByVal cbBuffer As Long, _
   pcbNeeded As Long, _
   pcReturned As Long) As Long

'EnumPrinters Parameters:
'Flags - Specifies the types of print objects that the function should enumerate.
Public Const PRINTER_ENUM_DEFAULT = &H1     'Windows 95: The function returns
                                            'information about the default printer.
Public Const PRINTER_ENUM_LOCAL = &H2       'function ignores the Name parameter,
                                            'and enumerates the locally installed
                                            'printers. Windows 95: The function will
                                            'also enumerate network printers because
                                            'they are handled by the local print provider
Public Const PRINTER_ENUM_CONNECTIONS = &H4 'Windows NT/2000: The function enumerates the
                                            'list of printers to which the user has made
                                            'previous connections
Public Const PRINTER_ENUM_NAME = &H8        'enumerates the printer identified by Name.
                                            'This can be a server, a domain, or a print
                                            'provider. If Name is NULL, the function
                                            'enumerates available print providers
Public Const PRINTER_ENUM_REMOTE = &H10     'Windows NT/2000: The function enumerates network
                                            'printers and print servers in the computer's domain.
                                            'This value is valid only if Level is 1
Public Const PRINTER_ENUM_SHARED = &H20     'enumerates printers that have the shared attribute.
                                            'Cannot be used in isolation; use an OR operation
                                            'to combine with another PRINTER_ENUM type
Public Const PRINTER_ENUM_NETWORK = &H40    'Windows NT/2000: The function enumerates network
                                            'printers in the computer's domain. This value is
                                            'valid only if Level is 1.

'''''''''''''''''''''''
'Name:
'If Level is 1, Flags contains PRINTER_ENUM_NAME, and Name is non-NULL,
'then Name is a pointer to a null-terminated string that specifies the
'name of the object to enumerate. This string can be the name of a server,
'a domain, or a print provider.
'
'If Level is 1, Flags contains PRINTER_ENUM_NAME, and Name is NULL, then
'the function enumerates the available print providers.
'
'If Level is 1, Flags contains PRINTER_ENUM_REMOTE, and Name is NULL, then
'the function enumerates the printers in the user's domain.
'
'If Level is 2 or 5, Name is a pointer to a null-terminated string that
'specifies the name of a server whose printers are to be enumerated. If
'this string is NULL, then the function enumerates the printers installed
'on the local machine.
'
'If Level is 4, Name should be NULL. The function always queries on
'the local machine.

'When Name is NULL, it enumerates printers that are installed on the
'local machine. These printers include those that are physically attached
'to the local machine as well as remote printers to which it has a
'network connection.

'''''''''''''''''''''''
'Level:
'Specifies the type of data structures pointed to by pPrinterEnum.
'Valid values are 1, 2, 4, and 5, which correspond to the
'PRINTER_INFO_1, PRINTER_INFO_2, PRINTER_INFO_4, and PRINTER_INFO_5
'data structures.
'
'Windows 95: The value can be 1, 2, or 5.
'
'Windows NT/Windows 2000: This value can be 1, 2, 4, or 5.

'''''''''''''''''''''''
'pPrinterEnum:
'Pointer to a buffer that receives an array of PRINTER_INFO_1,
'PRINTER_INFO_2, PRINTER_INFO_4, or PRINTER_INFO_5 structures.
'Each structure contains data that describes an available print object.
'
'If Level is 1, the array contains PRINTER_INFO_1 structures.
'If Level is 2, the array contains PRINTER_INFO_2 structures.
'If Level is 4, the array contains PRINTER_INFO_4 structures.
'If Level is 5, the array contains PRINTER_INFO_5 structures.
'
'The buffer must be large enough to receive the array of data
'structures and any strings or other data to which the structure
'members point. If the buffer is too small, the pcbNeeded parameter
'returns the required buffer size.
'
'Windows 95: The buffer cannot receive PRINTER_INFO_4 structures.
'It can receive any of the other types.

'''''''''''''''''''''''
'cbBuf
'Specifies the size, in bytes, of the buffer pointed to by pPrinterEnum.
'''''''''''''''''''''''
'pcbNeeded
'Pointer to a value that receives the number of bytes copied if the
'function succeeds or the number of bytes required if cbBuffer is too small.
'''''''''''''''''''''''
'pcReturned
'Pointer to a value that receives the number of PRINTER_INFO_1,
'PRINTER_INFO_2, PRINTER_INFO_4, or PRINTER_INFO_5 structures that
'the function returns in the array to which pPrinterEnum points.


'PRINTER_INFO_4 returned Attribute values
Public Const PRINTER_ATTRIBUTE_DEFAULT = &H4
Public Const PRINTER_ATTRIBUTE_DIRECT = &H2
Public Const PRINTER_ATTRIBUTE_ENABLE_BIDI = &H800&
Public Const PRINTER_ATTRIBUTE_LOCAL = &H40
Public Const PRINTER_ATTRIBUTE_NETWORK = &H10
Public Const PRINTER_ATTRIBUTE_QUEUED = &H1
Public Const PRINTER_ATTRIBUTE_SHARED = &H8
Public Const PRINTER_ATTRIBUTE_WORK_OFFLINE = &H400

'PRINTER_INFO_1 returned Flag values
Public Const PRINTER_ENUM_CONTAINER = &H8000&
Public Const PRINTER_ENUM_EXPAND = &H4000
Public Const PRINTER_ENUM_ICON1 = &H10000
Public Const PRINTER_ENUM_ICON2 = &H20000
Public Const PRINTER_ENUM_ICON3 = &H40000
Public Const PRINTER_ENUM_ICON4 = &H80000
Public Const PRINTER_ENUM_ICON5 = &H100000
Public Const PRINTER_ENUM_ICON6 = &H200000
Public Const PRINTER_ENUM_ICON7 = &H400000
Public Const PRINTER_ENUM_ICON8 = &H800000

Public Const LB_SETTABSTOPS As Long = &H192

Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
  (ByVal hwnd As Long, _
   ByVal wMsg As Long, _
   ByVal wParam As Long, _
   lParam As Any) As Long
   
Public Declare Function lstrcpyA Lib "kernel32" _
  (ByVal RetVal As String, ByVal Ptr As Long) As Long
                        
Public Declare Function lstrlenA Lib "kernel32" _
  (ByVal Ptr As Any) As Long
 Form Code
On a form add two command buttons (Command1, Command2), a list box (List1) and a label for the number of printers returned (Label1). The labels above the list box are optional and are commented out in Command1. 

Add the following to the form:


Option Explicit

Private Sub Form_Load()

   ReDim TabArray(0 To 1) As Long
   
   TabArray(0) = 120
   TabArray(1) = 191

   Call SendMessage(List1.hwnd, LB_SETTABSTOPS, 0&, ByVal 0&)
   Call SendMessage(List1.hwnd, LB_SETTABSTOPS, 2&, TabArray(0))
   List1.Refresh
   
   Command1.Caption = "Enum Printers WinNT"   
   Command2.Caption = "Enum Printers Win9x"
   
End Sub


Private Sub Command1_Click()

   Dim numPrinters As Long

  'Label2(1).Caption = "Printer"
  'Label2(2).Caption = "Server (if any)"
  'Label2(3).Caption = "Attributes"
   
   numPrinters = EnumPrintersWinNT(List1)
   
   Label1.Caption = "There are " & numPrinters & " local and connected printers."

End Sub


Private Sub Command2_Click()

   Dim numPrinters As Long

  'Label2(1).Caption = "Printer"
  'Label2(2).Caption = "Flags"
  'Label2(3).Caption = "Description"
   
   numPrinters = EnumPrintersWin9x(List1)
   
   Label1.Caption = "There are " & numPrinters & " local and connected printers."

End Sub


Private Function GetStrFromPtrA(ByVal lpszA As Long) As String

   GetStrFromPtrA = String$(lstrlenA(ByVal lpszA), 0)
   Call lstrcpyA(ByVal GetStrFromPtrA, ByVal lpszA)
   
End Function


Private Function EnumPrintersWinNT(ctl As ListBox) As Long
    
   Dim Success As Boolean
   Dim cbRequired As Long
   Dim cbBuffer As Long
   Dim ptr() As PRINTER_INFO_4
   Dim nEntries As Long
   Dim cnt As Long
   Dim sAttr As String
   
   ctl.Clear
   
  'To determine the required buffer size, call EnumPrinters with
  'cbBuffer set to zero. EnumPrinters fails, and Err.LastDLLError
  'returns ERROR_INSUFFICIENT_BUFFER, filling in the cbRequired
  'parameter with the size, in bytes, of the buffer required to
  'hold the array of structures and their data.
   Call EnumPrinters(PRINTER_ENUM_CONNECTIONS Or PRINTER_ENUM_LOCAL, _
                     vbNullString, PRINTER_LEVEL4, _
                     0, 0, cbRequired, nEntries)
            
   
  'The strings pointed to by each PRINTER_INFO_4 struct's members
  'reside in memory after the end of the array of structs. So we're
  'not only allocating memory for the structs themselves, but all the
  'strings pointed to by each struct's member as well.
   ReDim ptr((cbRequired \ SIZEOFPRINTER_INFO_4))
       
  'Set cbBuffer equal to the size of the buffer
   cbBuffer = cbRequired
    
  'Enumerate the printers. If the function succeeds,
  'the return value is nonzero. If the function fails,
  'the return value is zero.
   If EnumPrinters(PRINTER_ENUM_CONNECTIONS Or PRINTER_ENUM_LOCAL, _
                   vbNullString, PRINTER_LEVEL4, _
                   ptr(0), cbBuffer, _
                   cbRequired, nEntries) Then
              
      For cnt = 0 To nEntries - 1
           
         With ptr(cnt)
            
            sAttr = ""
               
            If (.Attributes And PRINTER_ATTRIBUTE_DEFAULT) Then sAttr = "default "
            If (.Attributes And PRINTER_ATTRIBUTE_DIRECT) Then sAttr = sAttr & "direct "
            If (.Attributes And PRINTER_ATTRIBUTE_ENABLE_BIDI) Then sAttr = sAttr & "bidirectional "
            If (.Attributes And PRINTER_ATTRIBUTE_LOCAL) Then sAttr = sAttr & "local "
            If (.Attributes And PRINTER_ATTRIBUTE_NETWORK) Then sAttr = sAttr & "net "
            If (.Attributes And PRINTER_ATTRIBUTE_QUEUED) Then sAttr = sAttr & "queued "
            If (.Attributes And PRINTER_ATTRIBUTE_SHARED) Then sAttr = sAttr & "shared "
            If (.Attributes And PRINTER_ATTRIBUTE_WORK_OFFLINE) Then sAttr = sAttr & "offline "
            
            ctl.AddItem GetStrFromPtrA(.pPRinterName) & vbTab & _
                        GetStrFromPtrA(.pSErverName) & vbTab & _
                        sAttr

         End With
              
      Next cnt
        
   Else
      ctl.AddItem "Error enumerating printers."
   End If  'EnumPrinters
   
   EnumPrintersWinNT = nEntries
    
End Function


Private Function EnumPrintersWin9x(ctl As ListBox) As Long
    
   Dim cbRequired As Long
   Dim cbBuffer As Long
   Dim ptr() As PRINTER_INFO_1
   Dim nEntries As Long
   Dim cnt As Long
   Dim sFlags As String
    
   ctl.Clear
   
  'To determine the required buffer size, call EnumPrinters with
  'cbBuffer set to zero. EnumPrinters fails, and Err.LastDLLError
  'returns ERROR_INSUFFICIENT_BUFFER, filling in the cbRequired
  'parameter with the size, in bytes, of the buffer required to
  'hold the array of structures and their data.
   Call EnumPrinters(PRINTER_ENUM_CONNECTIONS Or PRINTER_ENUM_LOCAL, _
                     vbNullString, PRINTER_LEVEL1, _
                     0, 0, cbRequired, nEntries)
                     
  'The strings pointed to by each PRINTER_INFO_4 struct's members
  'reside in memory after the end of the array of structs. So we're
  'not only allocating memory for the structs themselves, but all the
  'strings pointed to by each struct's member as well.
   ReDim ptr((cbRequired \ SIZEOFPRINTER_INFO_1))
       
  'Set cbBuffer equal to the size of the buffer
   cbBuffer = cbRequired
    
  'Enumerate the printers. If the function succeeds,
  'the return value is nonzero. If the function fails,
  'the return value is zero.
   If EnumPrinters(PRINTER_ENUM_CONNECTIONS Or PRINTER_ENUM_LOCAL, _
                   vbNullString, PRINTER_LEVEL1, _
                   ptr(0), cbBuffer, _
                   cbRequired, nEntries) Then
          
      For cnt = 0 To nEntries - 1
           
         With ptr(cnt)

            sFlags = ""
               
           'see Comments for info on these flags
            If (.Flags And PRINTER_ENUM_CONTAINER) Then sFlags = "enumerable "
            If (.Flags And PRINTER_ENUM_EXPAND) Then sFlags = sFlags & "expand "
            If (.Flags And PRINTER_ENUM_ICON1) Then sFlags = sFlags & "icon1 "
            If (.Flags And PRINTER_ENUM_ICON2) Then sFlags = sFlags & "icon2 "
            If (.Flags And PRINTER_ENUM_ICON3) Then sFlags = sFlags & "icon3 "
            If (.Flags And PRINTER_ENUM_ICON8) Then sFlags = sFlags & "icon8 "
            
            ctl.AddItem GetStrFromPtrA(.paNe) & vbTab & _
                         sFlags & vbTab & _
                         GetStrFromPtrA(.pDescription)
                          
         End With
      Next cnt
           
   Else
   	  ctl.AddItem "Error enumerating printers."
   End If  'EnumPrinters
    
    EnumPrintersWin9x = nEntries
    
End Function
 Comments
The following is taken from the MSDN:

Flags Meaning (Win9x method)
PRINTER_ENUM_EXPAND
A print provider can set this flag as a hint to a calling application to enumerate this object further if default expansion is enabled. For example, when domains are enumerated, a print provider might indicate the user's domain by setting this flag.

PRINTER_ENUM_CONTAINER
If this flag is set, the printer object may contain enumerable objects. For example, the object may be a print server that contains printers.

PRINTER_ENUM_ICON1
Indicates that, where appropriate, an application should display an icon identifying the object as a top-level network name, such as Microsoft Windows Network.

PRINTER_ENUM_ICON2
Indicates that, where appropriate, an application should display an icon that identifies the object as a network domain.

PRINTER_ENUM_ICON3
Indicates that, where appropriate, an application should display an icon that identifies the object as a print server.

PRINTER_ENUM_ICON4, 5, 6, 7
Reserved for future use.

PRINTER_ENUM_ICON8
Indicates that, where appropriate, an application should display an icon that identifies the object as a printer.

EnumPrinters Remarks
If EnumPrinters returns a PRINTER_INFO_1 structure in which PRINTER_ENUM_CONTAINER is specified, this indicates that there is a hierarchy of printer objects. An application can enumerate the hierarchy by calling EnumPrinters again, setting Name to the value of the PRINTER_INFO_1 structure's pName member.

The EnumPrinters function does not retrieve security information. If PRINTER_INFO_2 structures are returned in the array pointed to by pPrinterEnum, their pSecurityDescriptor members will be set to NULL.

To get information about the default printer, call the GetProfileString function with the section name string set to "windows" and the key name string set to "device". The returned string contains the name of the default printer, the name of the printer DR file, and the port to which the printer is attached.

Windows NT/Windows 2000: The PRINTER_INFO_4 structure provides an easy and extremely fast way to retrieve the names of the printers installed on a local machine, as well as the remote connections that a user has established. When EnumPrinters is called with a PRINTER_INFO_4 data structure, that function queries the registry for the specified information, then returns immediately. This differs from the behaviour of EnumPrinters when called with other levels of PRINTER_INFO_* data structures. In particular, when EnumPrinters is called with a level 2 (PRINTER_INFO_2) data structure, it performs an OpenPrinter call on each remote connection. If a remote connection is down, or the remote server no longer exists, or the remote printer no longer exists, the function must wait for RPC to time out and consequently fail the OpenPrinter call. This can take a while. Passing a PRINTER_INFO_4 structure lets an application retrieve a bare minimum of required information; if more detailed information is desired, a subsequent EnumPrinter level 2 call can be made.

Windows 95: To quickly enumerate local and network printers, use the PRINTER_INFO_5 structure. This causes EnumPrinters to query the registry rather than make remote calls, and is similar to using the PRINTER_INFO_4 structure on Windows NT as described in the preceding paragraph.


 
 

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