|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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_CONTAINER PRINTER_ENUM_ICON1 PRINTER_ENUM_ICON2 PRINTER_ENUM_ICON3 PRINTER_ENUM_ICON4, 5, 6, 7 PRINTER_ENUM_ICON8 EnumPrinters
Remarks 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. |
|
|
|
|||||
|
|||||
|
|||||
Copyright ©1996-2011 VBnet and Randy Birch. All Rights Reserved. |