【问题标题】:Poor performance on SELECT * FROM Win32_PrinterSELECT * FROM Win32_Printer 性能不佳
【发布时间】:2018-01-16 19:33:51
【问题描述】:

我正在更新一些例程,当用户通过 Citrix 访问我们的应用程序时,这些例程有助于识别和存储可供用户使用的打印机。创建打印机时,它们会被标记为类似的名称;

在思杰;

会话 209 中的打印机名称(来自 MyTerminalID)

在 RDP 中;

打印机名称(重定向 209)

在本地运行,我们只得到 PrinterName

这一切都很好,但是我们已经从使用“system.drawing”识别打印机的方法切换到使用 System.Management 查询SELECT * FROM Win32_Printer

我已经修改了这个条件,例如DeviceID Like '%(from MyTerminalID)%' AND DeviceID LIKE '%in session 123',但是当我第一次执行查询时,它可能需要几秒钟 (15+) 才能返回结果。一个我有结果,很快。

现在的问题是,我怎样才能让它跑得更快?

我只对获取与 myTerminalID 和我的会话或我的会话相关的打印机子集感兴趣(如果是 RDP,但 citrix 版本是重要的部分)。

查询是否像正常的 SQL 查询一样运行,因此将使用条件来减少运行时间,还是更像是一个伪查询,它将拉回整个数据集然后应用过滤。

有没有办法减少它需要去和获取的数据量(例如,如果我执行 SELECT DeviceID 而不是 SELECT *)。

重新运行查询要快得多

【问题讨论】:

    标签: vb.net printing wmi citrix .net-4.6.1


    【解决方案1】:

    关于您当前的情况,您可以尝试设置两个策略:
    - 仅使用通用打印
    - 不要自动创建客户端打印机

    然后强制更新组策略(客户端)。

    也许您希望客户端和服务器打印机相互镜像,但只是为了测试。
    (但是请注意,在不同情况下,我在 WMI 枚举中看到的时间比 15 秒差得多。另请注意,我m 不是 Citrix Sessions/UPS 方面的专家。)


    WMI SQL (WQL) 是 ANSI SQL 标准的子集。请参阅:
    Querying with WQL - WQL (SQL for WMI) Keywords
    当然,它会返回一组与查询中定义的约束相匹配的记录(对象)。
    它是一个高效的系统吗?在这件事上你可能会听到非常不同的意见。

    第一个查询速度较慢,因为此时会创建新对象。
    如果没有其他指示,WMI 会缓存这些对象以供后续查询。
    请参阅 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%'"
    


    以下测试是在一台有 7 台打印机的机器上进行的,其中 3 台是“非打印机”:Fax、XPS Driver 和 PDF Driver。其他 4 台是“真正的”打印机:2 台是本地打印机,2 台是网络打印机。
    QUERY             MIN     MAX 
    One Printer:       96ms - 181ms
    All Printers: (7) 107ms - 190ms
    Filtered:     (4) 108ms - 229ms
    

    使用 StopWatch 测量 50 次迭代

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

    这是使用的代码和对象:

    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
    

    相对对象:

    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
    

    【讨论】:

    • 是否需要 Dim _ConnOptions 作为新的 ConnectionOptions() _ConnOptions.Authentication = AuthenticationLevel.Connect _ConnOptions.Impersonation = ImpersonationLevel.Impersonate?这些对性能有帮助吗?
    • @Andy Macnaughton-Jones 否。在网络操作(COM 身份验证)中,可能需要通过 Kerberos 或 NTLMDomain 权限对请求进行身份验证。请注意,我使用了AuthenticationLevel.Connect。本地机器没问题。在域中,必须使用AuthenticationLevel.PacketPrivacy
    猜你喜欢
    • 2014-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-20
    • 2014-01-03
    • 1970-01-01
    • 2013-10-16
    • 1970-01-01
    相关资源
    最近更新 更多