Solved

Locate printer on the network given printer device name

Posted on 2001-06-12
14
1,499 Views
Last Modified: 2010-05-18
Suppose I have the device name of a printer taken from Printer.devicename on a client machine and I would like to check if the same printer is installed on another machine. The two machines may have differrent operating systems installed (but for sure both are Microsoft OS). I would like to know if there is a standard convension such as UNC that is used under all OS's to uniquely identify a printer in a LAN and how can I find this identifier.
0
Comment
Question by:rsuzi
  • 5
  • 4
  • 3
  • +2
14 Comments
 
LVL 9

Expert Comment

by:Valliappan AN
Comment Utility
You could check for the printer driver name, instead of the printer name. Since, the printer name, can be changed by the user, and given any name.

The code could be something like this:

    Dim prn
    For Each prn In Printers
        If prn.DriverName = "NEC24PIN" then
           MsgBox "NEC 24 Pin Printer driver found."
           Exit For
        End If
    Next

thank you.
0
 
LVL 2

Expert Comment

by:WalterM
Comment Utility
Use the Printer.Port property, as this returns the unique UNC identifier for the printer port. Optionally, you could also check against the DriverName.

Try this in the VB immediate pane:

For Each Prn In VB.Global.Printers: Debug.Print Prn.DeviceName, Prn.DriverName, Prn.Port:Next Prn

Example output:

Acrobat Distiller           ADOBEPS4      C:\Program Files\Adobe\Acrobat 4.0\PDF Output\*.pdf
Acrobat PDFWriter           PDFWRITR      LPT1:
Xerox DocuColor 5750        ADOBEPS4      \\Netserver\xerox
Lexmark Optra SC 1275 PS2   LEXPS         \\PRINTSERVER\C903e_4
Lexmark Optra S 1855 PS2    LEXPS         \\Printserver\918_4

As you can see, network printers are clearly and uniquely identifiable by the UNC path returned by the Port property.

Michel
0
 

Author Comment

by:rsuzi
Comment Utility
If I would use printer driver name:
What if on printer server that I'm cheching for example for a HP Laserjet 1100 printer there are two such printers installed and none of them is actually the one I am looking for (since I've installed my HP Laserjet 1100 printer from a different printer server), although the printer driver name corresponds to the one I'm looking for?

0
 

Author Comment

by:rsuzi
Comment Utility
Michel,
What version of VB are you using? I get a totally different result when running your code from VB6. I've run it on two different machines that both have the same printer installed from the same printer server I got:

on one of them
\\PS1\HP LaserJet 1100   winspool      Ne00:
\\PS2\Star LC-20      winspool      Ne01:

and on the other
\\PS2\Star LC-20      winspool      Ne00:

So I guess the same physical printer installed from the same printer server on two different client machines might return differrent values for port number, depending if there are other printers installed on those client machines and the order in which they were installed.
0
 
LVL 2

Expert Comment

by:WalterM
Comment Utility
I believe that only the printer port UNC is truly unique.

In the example you state above, if you would only be matching printers against the drivername, then you would indeed find all printers that use that specific driver. It might even be so that all printers on the LAN are the same type, then each printer in the collection could use the same driver.
However, each printer must be connected to it's own unique network port, so the OS knows how to identify and access it.

In your original question, you ask if there is a unique identifier per printer: well, there is, it's the aforementioned Port property.

So DON'T use the driver name to uniquely identify a printer; use the Port property instead.

Michel
0
 
LVL 2

Accepted Solution

by:
WalterM earned 100 total points
Comment Utility
rsuzi,

I'm using VB6 too, so that shouldn't be the problem. It is more likely that any output differences are caused by differences in our network environment.

It seems te me that the first entry in your list is the UNC Port, which identifies the shared printer as such.
The last entry in the list seems to be the local device name, which is assigned on each client computer independently.

In what order did you print these properties?

Remember, a network printer port UNC will be of the form:

   \\Servername\Printername

while a local printer port will be identified by something non-UNC like:

   LPT1:


Michel
0
 
LVL 9

Expert Comment

by:Valliappan AN
Comment Utility
rsuzi,

You need to print to the same printer type, or to the exact same printer, wherever the machine is located in a network. Anyhow, the port checking will not be the same, and it actually depends on what name you have given to the port and/or the system. If you want a unique locator, then you could use both drivername and port. I feel, port, identification, not to be a viable method.

Could you tell, what sort of application, for which you need to locate the printer, like this?

Further details please...

cheers.
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 

Author Comment

by:rsuzi
Comment Utility
Michel,
What seems strange to me is that we have both run exactly the same piece of code and we get different results. From my tests, I would say printer.devicename is the unique UNC that I need, not the port, so I'a m little confused right now.
Are you sure this form of identification goes for Win98 or WinMe, not only for WinNT, because I've read that is has been introduced since WinNT 3.51?
One more question for you: I've read somewhere in the MSDN that there are some devices (not computers) that you can connect to a network and if connect printers to those devices, you can share those printers on the network. Does the rule \\Servername\printername apply in this case too?
0
 

Author Comment

by:rsuzi
Comment Utility
valli_an,
To answer your question: I have an app on a machine A that has several printers installed, some of them may ne shared with the network by printer servers, some may be printers connected directly to the client machine. The user is free to choose one of the printers to print a document. There is a component that runs on another machine B that must attempt to print the document on B if the chosen printer is installed on it, otherwise the document should be printed on A, so I need to something to uniquely identify exactly the chosen printer on the LAN.
Greetings
0
 
LVL 9

Expert Comment

by:Valliappan AN
Comment Utility
check this link: HOWTO: Retrieve Settings From a Printer Driver

http://support.microsoft.com/support/kb/articles/Q190/2/18.ASP?LN=EN-US&SD=msdn&FR=0

You could retrieve the devicename, device driver version (if need be), and try a combination of devicename, specversion, driver version.
          dmDeviceName(1 To CCHDEVICENAME) As Byte
          dmSpecVersion As Integer
          dmDriverVersion As Integer

So, as you find the printer, then, you could choose between local or network, by identifying the port.

hope this helps,
Cheers.          
0
 
LVL 2

Expert Comment

by:WalterM
Comment Utility
rsuzi,

for the sake of clarity: I'm  using VB6 on a Win98 machine, connected to a hybrid Netware 3.12/WinNT 4 network.

I can't explain the apparent output differences, especially since in your case the DeviceName property seems to return a UNC network port string.

The 'winspool' string returned by the DriverName property indicates that you testing machine uses a printer spooler.
I could not find a machine at our company with a similar driver configuration, but here's my guess: because the spooler driver must intercept and handle any printer requests, it may appear as if the printers are attached to local ports (Ne00: and Ne01:) while in reality, the spooler may pass the data onto a external printer connected to a network port (\PS1\HP LaserJet 1100 and \\PS2\Star LC-20), using the DeviceName property to indicate the underlying network connection.

Concerning your question about printer sharing: this does not affect network addressing, but only queue concurrent access to a shared resource i.e. printer. The printer would still have a uniquely identifiable UNC. You can compare this to the local printer spooler service, which handles printer requests of multiple applications running on one machine.

Because your output raised my curiosity, I wrote a small module for displaying printer & printer port information. It may be useful for obtaining some information about your machine's printer configuration.

*** code starts here ***

' Module MPrinters
' ----------------
' Target OS: Win95/Win98

Option Explicit

' Declarations for EnumPrinters
' -----------------------------

Public Enum PrinterInfoLevelEnum
    PrinterInfoLevel2 = 2
    PrinterInfoLevel5 = 5
End Enum

Private Enum EnumPrintersFlagsEnum

    PRINTER_ENUM_DEFAULT = &H1&
    PRINTER_ENUM_LOCAL = &H2&
    PRINTER_ENUM_CONNECTIONS = &H4&
    PRINTER_ENUM_FAVORITE = &H4&
    PRINTER_ENUM_NAME = &H8&
    PRINTER_ENUM_REMOTE = &H10&
    PRINTER_ENUM_SHARED = &H20&
    PRINTER_ENUM_NETWORK = &H40&
   
    PRINTER_ENUM_EXPAND = &H4000&
    PRINTER_ENUM_CONTAINER = &H8000&
   
    PRINTER_ENUM_ICONMASK = &HFF0000
    PRINTER_ENUM_ICON1 = &H10000
    PRINTER_ENUM_ICON2 = &H20000
    PRINTER_ENUM_ICON3 = &H40000
    PRINTER_ENUM_ICON4 = &H80000
    PRINTER_ENUM_ICON5 = &H100000
    PRINTER_ENUM_ICON6 = &H200000
    PRINTER_ENUM_ICON7 = &H400000
    PRINTER_ENUM_ICON8 = &H800000
    PRINTER_ENUM_HIDE = &H1000000
   
End Enum

Private Enum PrinterAttributeFlagsEnum
    PRINTER_ATTRIBUTE_QUEUED = &H1&
    PRINTER_ATTRIBUTE_DIRECT = &H2&
    PRINTER_ATTRIBUTE_DEFAULT = &H4&
    PRINTER_ATTRIBUTE_SHARED = &H8&
    PRINTER_ATTRIBUTE_NETWORK = &H10&
    PRINTER_ATTRIBUTE_HIDDEN = &H20&
    PRINTER_ATTRIBUTE_LOCAL = &H40&
   
    PRINTER_ATTRIBUTE_ENABLE_DEVQ = &H80&
    PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS = &H100&
    PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST = &H200&
   
    PRINTER_ATTRIBUTE_WORK_OFFLINE = &H400&
    PRINTER_ATTRIBUTE_ENABLE_BIDI = &H800&
    PRINTER_ATTRIBUTE_RAW_ONLY = &H1000&
    PRINTER_ATTRIBUTE_PUBLISHED = &H2000&
End Enum

Private Type PRINTER_INFO_2
        pServerName As Long             ' String
        pPrinterName As Long            ' String
        pShareName As Long              ' String
        pPortName As Long               ' String
        pDriverName As Long             ' String
        pComment As Long                ' String
        pLocation As Long               ' String
        pDevMode As Long                ' DEVMODE               API-Viewer Bug - actually a pointer!
        pSepFile As Long                ' String
        pPrintProcessor As Long         ' String
        pDatatype As Long               ' String
        pParameters As Long             ' String
        pSecurityDescriptor As Long     ' SECURITY_DESCRIPTOR   API-Viewer Bug - actually a pointer!
        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_5
        pPrinterName As Long            ' String
        pPortName As Long               ' String
        Attributes As Long              ' String
        DeviceNotSelectedTimeout As Long
        TransmissionRetryTimeout As Long
End Type

Private Declare Function EnumPrinters Lib "winspool.drv" Alias "EnumPrintersA" _
                       (ByVal flags As EnumPrintersFlagsEnum, _
                        ByVal name As String, _
                        ByVal Level As Long, _
                        pPrinterEnum As Any, _
                        ByVal cdBuf As Long, _
                        pcbNeeded As Long, _
                        pcReturned As Long) As Long
                       
' Declarations for EnumPorts
' -----------------------------

Public Enum PortInfoLevelEnum
    PortInfoLevel1 = 1
    PortInfoLevel2 = 2
End Enum

Private Type PORT_INFO_1
        pName As Long               ' String
End Type

Private Enum PortTypeEnum
    PORT_TYPE_WRITE = &H1
    PORT_TYPE_READ = &H2
    PORT_TYPE_REDIRECTED = &H4
    PORT_TYPE_NET_ATTACHED = &H8
End Enum

Private Type PORT_INFO_2
        pPortName As Long           ' String
        pMonitorName As Long        ' String
        pDescription As Long        ' String
        fPortType As PortTypeEnum   ' Long
        Reserved As Long
End Type

' Declarations for FormatMessage
' ------------------------------

Private Declare Function EnumPorts Lib "winspool.drv" Alias "EnumPortsA" _
                        (ByVal pName As String, _
                         ByVal Level As Long, _
                         lpbPorts As Any, _
                         ByVal cbBuf As Long, _
                         pcbNeeded As Long, _
                         pcReturned As Long) As Long

Private Enum FormatMsgFlagsEnum
    FORMAT_MESSAGE_ALLOCATE_BUFFER = &H100&
    FORMAT_MESSAGE_IGNORE_INSERTS = &H200&
    FORMAT_MESSAGE_FROM_STRING = &H400&
    FORMAT_MESSAGE_FROM_HMODULE = &H800&
    FORMAT_MESSAGE_FROM_SYSTEM = &H1000&
    FORMAT_MESSAGE_ARGUMENT_ARRAY = &H2000&
    FORMAT_MESSAGE_MAX_WIDTH_MASK = &HFF&
End Enum

Private Enum FormatMsgErrorsEnum
    ERROR_INVALID_PARAMETER = 87&
    ERROR_INSUFFICIENT_BUFFER = 122&
    ERROR_MR_MID_NOT_FOUND = 317&
    ERROR_INVALID_FLAGS = 1004&
End Enum

Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" _
                        (ByVal dwFlags As FormatMsgFlagsEnum, _
                         ByVal lpSource As Long, _
                         ByVal dwMessageId As Long, _
                         ByVal dwLanguageId As Long, _
                         ByVal lpBuffer As String, _
                         ByVal nSize As Long, _
                         Arguments As Any) As Long
                         
' Utility declarations from kernel32.dll
' --------------------------------------

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

Public Function GetSystemErrMsg(ByVal ErrorID As Long, Optional ByVal MaxWidth As Byte = 0) As String
    'Note that the MaxWidth default of 0& causes the message to be returned as is.
    'Any other value reformats the message according to the specified maximum line length.
    'Since the maximum allowed width is 255 (&HFF), the parameter is implemented as a Byte.
   
    Dim BufLen As Long
    Dim MsgLen As Long
    Dim DllErr As Long
    Dim flags As FormatMsgFlagsEnum
   
    BufLen = 256
   
    'If necessary, keep enlarging the buffer until it is large enough
    Do While True
        GetSystemErrMsg = Space$(BufLen)
        flags = FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS
        If MaxWidth <> 0 Then flags = flags Or (MaxWidth And FORMAT_MESSAGE_MAX_WIDTH_MASK)
        MsgLen = FormatMessage(flags, 0&, ErrorID, 0&, GetSystemErrMsg, Len(GetSystemErrMsg), ByVal 0&)
        If MsgLen > 0 Then
            GetSystemErrMsg = Left$(GetSystemErrMsg, MsgLen)
            Exit Function
        Else
            'If the function returns 0, we can get the error code using GetLastError
            DllErr = Err.LastDllError
            Select Case DllErr
            Case ERROR_INSUFFICIENT_BUFFER
                'Enlarge buffer and try again
                BufLen = BufLen * 2
            Case ERROR_MR_MID_NOT_FOUND
                GetSystemErrMsg = "Unknown error &H" & Hex$(ErrorID)
                Exit Function
                'GetSystemErrMsg = ReplaceInserts(GetSystemErrMsg(ERROR_MR_MID_NOT_FOUND), MaxWidth, Hex$(ErrorID), "windows")
            Case Else
                Err.Raise DllErr, "GetSystemErrMsg", GetSystemErrMsg(DllErr)
            End Select
        End If
    Loop
   
End Function

Private Sub RaiseAPIError(Optional ByVal Source As String)
    Dim ErrID As Long
    ErrID = Err.LastDllError
    If ErrID <> 0 Then Err.Raise ErrID, Source, GetSystemErrMsg(ErrID)
End Sub

Private Function StringFromPtr(lpStrZ As Long) As String
    ' Returns a VB string from an LPSTRZ (zero-terminated ANSI string pointer)
    Dim L As Long
    If (lpStrZ <> 0) Then
        L = lstrlenA(lpStrZ)
        If L > 0 Then
            StringFromPtr = String$(L, 32)
            ' Let VB do the intrinsic ANSI-to-Unicode translation
            CopyMemory ByVal StringFromPtr, ByVal lpStrZ, L
        End If
    End If
End Function

Private Function AppendStr(Msg As String, ByVal Value As String)
    If Len(Msg) Then Msg = Msg & ", "
    Msg = Msg & Value
End Function

Private Function GetAttrDesc(ByVal Attr As PrinterAttributeFlagsEnum) As String
    If (Attr And PRINTER_ATTRIBUTE_QUEUED) Then AppendStr GetAttrDesc, "Queued"
    If (Attr And PRINTER_ATTRIBUTE_DIRECT) Then AppendStr GetAttrDesc, "Direct"
    If (Attr And PRINTER_ATTRIBUTE_DEFAULT) Then AppendStr GetAttrDesc, "Default"
    If (Attr And PRINTER_ATTRIBUTE_SHARED) Then AppendStr GetAttrDesc, "Shared"
    If (Attr And PRINTER_ATTRIBUTE_NETWORK) Then AppendStr GetAttrDesc, "Network"
    If (Attr And PRINTER_ATTRIBUTE_HIDDEN) Then AppendStr GetAttrDesc, "Hidden"
    If (Attr And PRINTER_ATTRIBUTE_LOCAL) Then AppendStr GetAttrDesc, "Local"

    If (Attr And PRINTER_ATTRIBUTE_ENABLE_DEVQ) Then AppendStr GetAttrDesc, "DevQueryPrint"
    If (Attr And PRINTER_ATTRIBUTE_KEEPPRINTEDJOBS) Then AppendStr GetAttrDesc, "Keep Printed Jobs"
    If (Attr And PRINTER_ATTRIBUTE_DO_COMPLETE_FIRST) Then AppendStr GetAttrDesc, "Do Complete First"

    If (Attr And PRINTER_ATTRIBUTE_WORK_OFFLINE) Then AppendStr GetAttrDesc, "Work Offline"
    If (Attr And PRINTER_ATTRIBUTE_ENABLE_BIDI) Then AppendStr GetAttrDesc, "Bidirectional"
    If (Attr And PRINTER_ATTRIBUTE_RAW_ONLY) Then AppendStr GetAttrDesc, "Raw Only"
    If (Attr And PRINTER_ATTRIBUTE_PUBLISHED) Then AppendStr GetAttrDesc, "Published"
End Function

Private Function GetPortTypeDesc(ByVal PortType As PortTypeEnum) As String
    If (PortType And PORT_TYPE_READ) Then AppendStr GetPortTypeDesc, "Read"
    If (PortType And PORT_TYPE_WRITE) Then AppendStr GetPortTypeDesc, "Write"
    If (PortType And PORT_TYPE_REDIRECTED) Then AppendStr GetPortTypeDesc, "Redirected"
    If (PortType And PORT_TYPE_NET_ATTACHED) Then AppendStr GetPortTypeDesc, "Network"
End Function

Public Sub ShowPrinterInfo2(Buffer() As Byte, ByVal ElemCnt As Long)
    ' Display the contents of a buffer containing an array of PRINTER_INFO_2 structures
    Dim Info() As PRINTER_INFO_2
    Dim ElemSize As Long
    Dim InfoSize As Long
    Dim I As Long

    ElemSize = LenB(Info(0))
    Debug.Assert (ElemCnt * ElemSize) <= (UBound(Buffer) - LBound(Buffer) + 1)
    ReDim Info(0 To (ElemCnt - 1))
    InfoSize = ElemCnt * ElemSize
    CopyMemory Info(0), Buffer(0), InfoSize
    For I = 0 To (ElemCnt - 1)
        Debug.Print "Information about printer #"; I
        With Info(I)
            Debug.Print , "Server"; Tab(30); " : ";
            If .pServerName <> 0 Then
                StringFromPtr (.pServerName)
            Else
                Debug.Print "(Controlled locally)"
            End If
            Debug.Print , "Printername"; Tab(30); " : "; StringFromPtr(.pPrinterName)
            Debug.Print , "Share"; Tab(30); " : "; StringFromPtr(.pShareName)
            Debug.Print , "Port"; Tab(30); " : "; StringFromPtr(.pPortName)
            Debug.Print , "Driver"; Tab(30); " : "; StringFromPtr(.pDriverName)
            Debug.Print , "Comment"; Tab(30); " : "; StringFromPtr(.pComment)
            Debug.Print , "Location"; Tab(30); " : "; StringFromPtr(.pLocation)
           
            Debug.Print , "SepFile"; Tab(30); " : "; StringFromPtr(.pSepFile)
            Debug.Print , "PrintProcessor"; Tab(30); " : "; StringFromPtr(.pPrintProcessor)
            Debug.Print , "DataType"; Tab(30); " : "; StringFromPtr(.pDatatype)
            Debug.Print , "Parameters"; Tab(30); " : "; StringFromPtr(.pParameters)
           
            Debug.Print , "Attributes"; Tab(30); " : "; GetAttrDesc(.Attributes)
            Debug.Print , "Priority"; Tab(30); " : "; .Priority
            Debug.Print , "DefaultPriority"; Tab(30); " : "; .DefaultPriority
            Debug.Print , "StartTime"; Tab(30); " : "; .StartTime
            Debug.Print , "UntilTime"; Tab(30); " : "; .UntilTime
            Debug.Print , "Status"; Tab(30); " : "; .Status
            Debug.Print , "Jobs"; Tab(30); " : "; .cJobs
            Debug.Print , "AveragePPM"; Tab(30); " : "; .AveragePPM
        End With
    Next I
   
End Sub

Public Sub ShowPrinterInfo5(Buffer() As Byte, ByVal ElemCnt As Long)
    ' Display the contents of a buffer containing an array of PRINTER_INFO_5 structures
    Dim Info() As PRINTER_INFO_5
    Dim ElemSize As Long
    Dim InfoSize As Long
    Dim I As Long

    ElemSize = LenB(Info(0))
    ReDim Info(0 To (ElemCnt - 1))
    InfoSize = ElemCnt * ElemSize
    Debug.Assert InfoSize <= (UBound(Buffer) - LBound(Buffer) + 1)
    CopyMemory Info(0), Buffer(0), InfoSize
    For I = 0 To (ElemCnt - 1)
        Debug.Print "Information about printer #"; I
        With Info(I)
            Debug.Print , "Name"; Tab(30); " : "; StringFromPtr(.pPrinterName)
            Debug.Print , "Port"; Tab(30); " : "; StringFromPtr(.pPortName)
            Debug.Print , "Attributes"; Tab(30); " : "; GetAttrDesc(.Attributes)
        End With
    Next I
   
End Sub

Public Sub ShowPortInfo1(Buffer() As Byte, ByVal ElemCnt As Long)
    ' Display the contents of a buffer containing an array of PORT_INFO_1 structures
    Dim Info() As PORT_INFO_1
    Dim ElemSize As Long
    Dim InfoSize As Long
    Dim I As Long

    ElemSize = LenB(Info(0))
    ReDim Info(0 To (ElemCnt - 1))
    InfoSize = ElemCnt * ElemSize
    Debug.Assert InfoSize <= (UBound(Buffer) - LBound(Buffer) + 1)
    CopyMemory Info(0), Buffer(0), InfoSize
    For I = 0 To (ElemCnt - 1)
        Debug.Print "Information about port #"; I
        With Info(I)
            Debug.Print , "Name"; Tab(30); " : "; StringFromPtr(.pName)
        End With
    Next I
   
End Sub

Public Sub ShowPortInfo2(Buffer() As Byte, ByVal ElemCnt As Long)
    ' Display the contents of a buffer containing an array of PORT_INFO_2 structures
    Dim Info() As PORT_INFO_2
    Dim ElemSize As Long
    Dim InfoSize As Long
    Dim I As Long

    ElemSize = LenB(Info(0))
    ReDim Info(0 To (ElemCnt - 1))
    InfoSize = ElemCnt * ElemSize
    Debug.Assert InfoSize <= (UBound(Buffer) - LBound(Buffer) + 1)
    CopyMemory Info(0), Buffer(0), InfoSize
    For I = 0 To (ElemCnt - 1)
        Debug.Print "Information about port #"; I
        With Info(I)
            Debug.Print , "Name"; Tab(30); " : "; StringFromPtr(.pPortName)
            Debug.Print , "Monitor"; Tab(30); " : "; StringFromPtr(.pMonitorName)
            Debug.Print , "Monitor"; Tab(30); " : "; StringFromPtr(.pDescription)
            Debug.Print , "Type"; Tab(30); " : "; GetPortTypeDesc(.fPortType)
            Debug.Print , "Reserved"; Tab(30); " : "; GetPortTypeDesc(.Reserved)
        End With
    Next I
   
End Sub

Public Sub ShowPrinters(Optional ByVal Level As PrinterInfoLevelEnum = PrinterInfoLevel2)
    Dim Buffer() As Byte
    Dim BufSize As Long
    Dim ElemCnt As Long
    Dim Result As Long
    Dim ErrID As Long
   
    On Error GoTo ErrHandler
   
    Select Case Level
    Case 2, 5
    Case Else
        Err.Raise 5, , "Invalid information level. Valid levels are 2 and 5."
    End Select
   
    ' Determine the required buffer space
    Result = EnumPrinters(PRINTER_ENUM_LOCAL, vbNullString, Level, ByVal 0, 0, BufSize, ElemCnt)
    ' Expecting error 122: buffer too small
    ErrID = Err.LastDllError
    If (ErrID <> 122) Then
        Err.Raise ErrID, "EnumPrinters", GetSystemErrMsg(ErrID)
    Else
        ReDim Buffer(0 To BufSize)
        Result = EnumPrinters(PRINTER_ENUM_LOCAL, vbNullString, Level, Buffer(0), BufSize, BufSize, ElemCnt)
        If Result <> 0 Then RaiseAPIError ("EnumPrinters")
        If ElemCnt = 0 Then
            Debug.Print "No printers installed."
        Else
            Select Case Level
            Case 2
                ShowPrinterInfo2 Buffer, ElemCnt
            Case 5
                ShowPrinterInfo5 Buffer, ElemCnt
            End Select
        End If
    End If
    Exit Sub
   
ErrHandler:
   
    Debug.Print Err.Source & " caused error "; Err.Number; " : "; Err.Description
   
End Sub

Public Sub ShowPorts(Optional ByVal Level As PortInfoLevelEnum = PortInfoLevel2)
    Dim Buffer() As Byte
    Dim BufSize As Long
    Dim ElemCnt As Long
    Dim Result As Long
    Dim ErrID As Long
   
    On Error GoTo ErrHandler
   
    Select Case Level
    Case 1, 2
    Case Else
        Err.Raise 5, , "Invalid information level. Valid levels are 1 and 2."
    End Select
   
    ' Determine the required buffer space
    Result = EnumPorts(vbNullString, Level, ByVal 0, 0, BufSize, ElemCnt)
    ' Expecting error 122: buffer too small
    ErrID = Err.LastDllError
    If (ErrID <> 122) Then
        Err.Raise ErrID, "EnumPorts", GetSystemErrMsg(ErrID)
    Else
        ReDim Buffer(0 To BufSize)
        Result = EnumPorts(vbNullString, Level, Buffer(0), BufSize, BufSize, ElemCnt)
        If Result <> 0 Then RaiseAPIError ("EnumPorts")
        If ElemCnt = 0 Then
            Debug.Print "No printer ports installed."
        Else
            Select Case Level
            Case 1
                ShowPortInfo1 Buffer, ElemCnt
            Case 2
                ShowPortInfo2 Buffer, ElemCnt
            End Select
        End If
    End If
    Exit Sub
   
ErrHandler:
   
    Debug.Print Err.Source & " caused error "; Err.Number; " : "; Err.Description
   
End Sub

*** code ends here ***


You can try it by pasting the above code into a new module, and executing the public subroutines from within the immediate window.

These are my results of running "ShowPrinters 5":


Information about printer # 0
              Name            : Lexmark Optra S 1855 PS2
              Port            : \\Printserver\918_4
              Attributes      : Default, Network, Local
Information about printer # 1
              Name            : Lexmark Optra SC 1275 PS2
              Port            : \\PRINTSERVER\C903e_4
              Attributes      : Network, Local
Information about printer # 2
              Name            : Xerox DocuColor 5750
              Port            : \\Netserver\xerox
              Attributes      : Network, Local, Bidirectional
Information about printer # 3
              Name            : Acrobat PDFWriter
              Port            : LPT1:
              Attributes      : Local
Information about printer # 4
              Name            : Acrobat Distiller
              Port            : C:\Program Files\Adobe\Acrobat 4.0\PDF Output\*.pdf
              Attributes      : Local


As you can see, the printer ports returned are the same as those returned by enumerating VB's Printers collection. I don't know why Network printers also have the Local flag set.

hope this helps a bit in solving the puzzle,

Michel
0
 
LVL 2

Expert Comment

by:WalterM
Comment Utility
rsuzi,

here's a remark i found hidden away in an old knowledge base article:

"When a Windows-based application (such as Word) queries the operating system for the name of the currently active printer driver, Windows NT always replies with "winspool" instead of the actual name of the printer driver."

(I presume your test was conducted on a WinNT machine?)

Michel
0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
Hi rsuzi@devx,
It appears that you have forgotten this question. I will ask Community Support to close it unless you finalize it within 7 days. I will suggest to:

    Accept WalterM's comment(s) as an answer.

rsuzi@devx, if you think your question was not answered at all or if you need help, you can simply post a new comment here.  Community Support moderators will follow up.

EXPERTS: If you disagree with that recommendation, please post an explanatory comment.
==========
DanRollins -- EE database cleanup volunteer
0
 
LVL 1

Expert Comment

by:Computer101
Comment Utility
Comment from expert accepted as answer

Computer101
E-E Moderator
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

There are many ways to remove duplicate entries in an SQL or Access database. Most make you temporarily insert an ID field, make a temp table and copy data back and forth, and/or are slow. Here is an easy way in VB6 using ADO to remove duplicate row…
Most everyone who has done any programming in VB6 knows that you can do something in code like Debug.Print MyVar and that when the program runs from the IDE, the value of MyVar will be displayed in the Immediate Window. Less well known is Debug.Asse…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
Get people started with the utilization of class modules. Class modules can be a powerful tool in Microsoft Access. They allow you to create self-contained objects that encapsulate functionality. They can easily hide the complexity of a process from…

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now