0
votes

I'm updating some routines that help identify and store the printers available for a user when they access our application over Citrix. When the printers are created they are flagged with a name like;

In citrix;

PrinterName (from MyTerminalID) in session 209

In RDP;

PrinterName (redirected 209)

run locally we just get the PrinterName

That's all fine but we've switched from using the "system.drawing" method of identifying printers which is somewhat dangerous and identifies every single printer on the citrix node to using the System.Management query SELECT * FROM Win32_Printer

I've modified this with a condition e.g., DeviceID Like '%(from MyTerminalID)%' AND DeviceID LIKE '%in session 123' but the first time I execute the query it can take a number of seconds (15+) before it comes back with the results. One I have the results it's very quick.

Now the question is, how can I make it go faster ?

I'm only interesting in getting the subset of printers that either relate to myTerminalID AND my session or my session (if RDP, but the citrix version is the important bit).

Is the query running like a proper SQL query so will use the condition to reduce the time to run or is it more of a pseudo query where it's going to pull back the whole data set and then apply the filtering.

Is there a way to reduce the amount of data that it needs to go and get (e.g., if I do a SELECT DeviceID rather than a SELECT *).

re-running the query is much faster <1 second. In some ways it doesn't matter if it takes 15+ seconds to pull back the list of printers as that's currently used for set up but there is also another routine that checks if the previously selected printers are still available to the user on the same terminal (even if a different user was the one that set them up). I would also like to try and potentially filter out "non-printers" like the fax etc., and to do that I need to look at some of the detail of the properties.

1

1 Answers

0
votes

About your current scenario, you could try to set two policies:
- Use Universal printing only
- Do not auto-create client printers

then force the update of the group policy (client side).

Maybe you want that client and server printers mirror each other, but just for testing.
(Note, however, that I've seen much worse than 15 seconds in WMI enumerations in different situations. Note also that I'm no expert in Citrix Sessions/UPS.)



Querying with WQLWQL (SQL for WMI) Keywords





EnumerationOptions.Rewindable Property
Use the Like Operator to Simplify Your WQL Queries

_Query.Condition = "NOT DeviceID LIKE '%fax%' AND NOT DeviceID LIKE '%xps%' AND NOT DeviceID LIKE '%PDF%'"



QUERY             MIN     MAX 
One Printer:       96ms - 181ms
All Printers: (7) 107ms - 190ms
Filtered:     (4) 108ms - 229ms

50 iterations measured using a StopWatch

  Dim _SW As Stopwatch = Stopwatch.StartNew()

  Dim _Printers As List(Of Printer) = GetSystemPrinters()
  Console.WriteLine(_SW.ElapsedMilliseconds.ToString())

This is the code and object used:

Public Function GetSystemPrinters() As List(Of Printer)

    Dim _Printers As New List(Of Printer)()

    Dim _ConnOptions As New ConnectionOptions()
    _ConnOptions.Authentication = AuthenticationLevel.Connect
    _ConnOptions.Impersonation = ImpersonationLevel.Impersonate
    'If needed => .UserName, .Password, .Authority
    _ConnOptions.Timeout = EnumerationOptions.InfiniteTimeout
    Dim _Scope As New ManagementScope("\\" + Environment.MachineName + "\root\CIMV2", _ConnOptions)
    _Scope.Connect()

    Dim _Query As New SelectQuery("SELECT * FROM Win32_Printer") 'Or "SELECT * FROM CIM_Printer"

    'Create a filter to rule out some "non-printers"
    _Query.Condition = "NOT DeviceID LIKE '%fax%' AND NOT DeviceID LIKE '%xps%' AND NOT DeviceID LIKE '%PDF%'"

    Dim _Options As New EnumerationOptions()
    _Options.Timeout = EnumerationOptions.InfiniteTimeout
    'Forward only query => no caching
    _Options.Rewindable = False
    'Pseudo-async result
    _Options.ReturnImmediately = True

    Dim _searcher As New ManagementObjectSearcher(_Scope, _Query, _Options)

    For Each _objPrinter As ManagementObject In _searcher.Get()
        Dim _Printer As New Printer()

        _Printer.PrinterName = _objPrinter.Properties("Name").Value.ToString()
        _Printer.PrinterPort = _objPrinter.Properties("PortName").Value.ToString()
        _Printer.PrinterDriver = _objPrinter.Properties("DriverName").Value.ToString()
        _Printer.PrintProcessor = _objPrinter.Properties("PrintProcessor").Value.ToString()
        _Printer.DeviceID = _objPrinter.Properties("DeviceID").Value.ToString()
        _Printer.Status = CType(_objPrinter.Properties("PrinterStatus").Value, PrinterStatus)
        _Printer.IsLocalPrinter = If(_objPrinter.Properties("Local").Value.ToString() = "True", True, False)
        _Printer.IsNetworkPrinter = If(_objPrinter.Properties("Network").Value.ToString() = "True", True, False)
        _Printer.IsDefaultPrinter = If(_objPrinter.Properties("Default").Value.ToString() = "True", True, False)

        Dim _PrinterProps As New List(Of PrinterProperties)()
        For Each _oBJData As PropertyData In _objPrinter.Properties
            _PrinterProps.Add(New PrinterProperties() With {
                .PropertyName = _oBJData.Name,
                .PropertyValue = If(_oBJData.Value IsNot Nothing, _oBJData.Value.ToString(), ""),
                .PropertyValueType = _oBJData.Type.ToString(),
                .PropertyIsArray = _oBJData.IsArray
            })
        Next

        _Printer.PrinterProperties = _PrinterProps
        _Printers.Add(_Printer)
    Next

    Return _Printers

End Function

Relative Objects:

Public Enum PrinterStatus As Integer
    Other = 1
    Unknown = 2
    Ready = 3
    Printing = 4
    Warmup = 5
    StoppedPrinting = 6
    Offline = 7
End Enum

Public Class Printer
    Public Property PrinterName() As String
    Public Property PrinterPort() As String
    Public Property PrinterDriver() As String
    Public Property PrintProcessor() As String
    Public Property DeviceID() As String
    Public Property Status() As PrinterStatus
    Public Property IsDefaultPrinter() As Boolean
    Public Property IsLocalPrinter() As Boolean
    Public Property IsNetworkPrinter() As Boolean
    Public Property PrinterProperties() As List(Of PrinterProperties)
End Class

Public Class PrinterProperties
    Public Property PropertyName() As String
    Public Property PropertyValue() As String
    Public Property PropertyValueType() As String
    Public Property PropertyIsArray() As Boolean
End Class