locked
WMI query with powershell: how to use forward only enumerators? RRS feed

  • Question

  • How can I translate the following vbscript to powershell:

    strComputer = "."
    Set objWMIService = GetObject("winmgmts:" _
    	& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
    Set colObj = objWMIService.ExecQuery _
    	("Select username From win32_computersystem", , _
    	wbemFlagReturnImmediately + wbemFlagForwardOnly)
    
    For Each wmiObj in colObj
    	wscript.echo wmiObj.username
    Next


    The following might be a bad example because a forward only enumerator won't really make this any faster, but it many cases it can. I googled for a while and I can't find out how to use this with powershell.

     

     

     

     

    Friday, July 29, 2011 8:40 PM

Answers

  • The hint given by jrv, plus another hint from Kazun in the PowerShell forum, allowed me to experiment until I came up with this solution. I assign $False to the Rewindable property of the EnumerationOptions object.

     

    $Computer = "Server5"

    $Options = New-Object System.Management.EnumerationOptions
    $Options.Rewindable = $False

    $Scope = new-Object System.Management.ManagementScope("\\$Computer\Root\cimv2")
    $Query = New-Object System.Management.ObjectQuery "SELECT * FROM Win32_ComputerSystem"
    $Results = New-Object System.Management.ManagementObjectSearcher $Scope, $Query, $Options
    $Items = $Results.Get()

    ForEach ($Item In $Items)
    {
        "Computer Name: " + $Item.Name
        "Bootup State: " + $Item.BootupState
        "TimeZone offset from UTC (in minutes): " + $Item.CurrentTimeZone
        "Description: " + $Item.description
        "Domain: " + $Item.Domain
        "Manufacturer: " + $Item.Manufacturer
        "Model: " + $Item.Model
        "Number Of Processors: " + $Item.NumberOfProcessors
        "Total Physical Memory: " + [Math]::Round($Item.TotalPhysicalMemory/(1024*1024)) + " MB"
        "Logged on user (if any): " + $Item.UserName
    }

    -----

     


    Richard Mueller - MVP Directory Services
    • Proposed as answer by jrv Monday, August 1, 2011 5:15 PM
    • Marked as answer by red888 Tuesday, August 2, 2011 1:12 PM
    Monday, August 1, 2011 2:29 PM

All replies

  • Go here:

    http://www.autoitscript.com/forum/topic/40749-powershell-to-get-computer-properties/

    Search on the page for wbemFlagForwardOnly

    Does that help?

    Karl


    My Blog: http://unlockpowershell.wordpress.com
    My Book: Windows PowerShell 2.0 Bible
    My E-mail: -join ("6B61726C6D69747363686B65406D742E6E6574"-split"(?<=\G.{2})",19|%{[char][int]"0x$_"})
    Friday, July 29, 2011 8:59 PM
  • $strComputer="."

    @(gwmi win32_computersystem -ComputerName $strComputer)|%{$_.username}


    my blog: http://shserg.ru/

    Sunday, July 31, 2011 2:37 PM
  • I've done a lot of searching, and tried a lot of variations, but I have found no way to specify wbemForwardOnly with Get-WMIObject. I thought the -Query parameter might allow me to specify this, but it failed. It may not be possible.

     


    Richard Mueller - MVP Directory Services
    Sunday, July 31, 2011 7:03 PM
  • In NEt SManagement classes it has morphed into 'Rewindable' whoich is the negative of ForwardOnly.

    ForwardOnly implies NOT Rewindable.

    http://msdn.microsoft.com/en-us/library/system.management.enumerationoptions.rewindable.aspx

    PS>new-object System.Management.EnumerationOptions(
      $context,
      [TimeSpan]10,
      [int]100,
      $rewindable,
      $returnImmediatley,
      $useAmendedQualifiers,
      $ensureLocatable,
      $prototypeOnly,
      $directRead,
      $enumerateDeep
    )
    
    ReturnImmediately  : False
    BlockSize      : 100
    Rewindable      : False
    UseAmendedQualifiers : False
    EnsureLocatable   : False
    PrototypeOnly    : False
    DirectRead      : False
    EnumerateDeep    : False
    Context       : {}
    Timeout       : 00:00:00.0000010
    
    
    

     

    This is how we set up options for a custom WMI connection.  Yu can use  class get or a query object.  They take an  options object which will already be included.  Just go in and set these values as needed before foing your GET,

     

     


    jv
    Sunday, July 31, 2011 8:35 PM
  • The hint given by jrv, plus another hint from Kazun in the PowerShell forum, allowed me to experiment until I came up with this solution. I assign $False to the Rewindable property of the EnumerationOptions object.

     

    $Computer = "Server5"

    $Options = New-Object System.Management.EnumerationOptions
    $Options.Rewindable = $False

    $Scope = new-Object System.Management.ManagementScope("\\$Computer\Root\cimv2")
    $Query = New-Object System.Management.ObjectQuery "SELECT * FROM Win32_ComputerSystem"
    $Results = New-Object System.Management.ManagementObjectSearcher $Scope, $Query, $Options
    $Items = $Results.Get()

    ForEach ($Item In $Items)
    {
        "Computer Name: " + $Item.Name
        "Bootup State: " + $Item.BootupState
        "TimeZone offset from UTC (in minutes): " + $Item.CurrentTimeZone
        "Description: " + $Item.description
        "Domain: " + $Item.Domain
        "Manufacturer: " + $Item.Manufacturer
        "Model: " + $Item.Model
        "Number Of Processors: " + $Item.NumberOfProcessors
        "Total Physical Memory: " + [Math]::Round($Item.TotalPhysicalMemory/(1024*1024)) + " MB"
        "Logged on user (if any): " + $Item.UserName
    }

    -----

     


    Richard Mueller - MVP Directory Services
    • Proposed as answer by jrv Monday, August 1, 2011 5:15 PM
    • Marked as answer by red888 Tuesday, August 2, 2011 1:12 PM
    Monday, August 1, 2011 2:29 PM
  • Richard - not sure but isn't $false the default. Async and semi-async queries cannnot be rewindable by default.  Think about it.  They are just spooling. Sync queries are slower but are there for when you need to reprocess the results repeatedly without having to requery for them again.  This save the network - or so the lofic goes - and allows us to hold onto the results.  I think under NET calsses the semi sync may be rewindable but I have never tried it.  It was not in W2K WMI.

     

     

     


    jv
    Monday, August 1, 2011 2:51 PM
  • The link you provided says the default is True. My documentation from Windows 2000 days says that wbemFlagReturnImmediately is enabled by default, but wbemFlagForwardOnly is not. Queries are said to be faster if you enable wbemFlagForwardOnly, so it must be disabled by default. Having wbemFlagReturnImmediately enabled means the query runs in "semisynchronous" mode, which is probably what you mean. The script can work with the swbemObjectSet that returns while other instances are being processed.

     


    Richard Mueller - MVP Directory Services
    Monday, August 1, 2011 3:10 PM
  • The link you provided says the default is True. My documentation from Windows 2000 days says that wbemFlagReturnImmediately is enabled by default, but wbemFlagForwardOnly is not. Queries are said to be faster if you enable wbemFlagForwardOnly, so it must be disabled by default. Having wbemFlagReturnImmediately enabled means the query runs in "semisynchronous" mode, which is probably what you mean. The script can work with the swbemObjectSet that returns while other instances are being processed.

     


    Richard Mueller - MVP Directory Services


    Richard,

    I guess memory is not always reliable.

    Then the defaults are still the same.  Now allwe need is atest of performance. I will try setting that the next time i need a big query.

    Thanks for the info.

     


    jv
    Monday, August 1, 2011 5:15 PM
  • Karl - good but it is the old VBScript method.  Those flags are always available With GetObject.  AutoIt Uses The WMI scriping controls so the information shuold be the same.

    Good find.

    Const wbemFlagForwardOnly=&H20
    
    objWbemObjectSet = SWbemServices.ExecQuery( _
     ByVal strQuery, _
     [ ByVal strQueryLanguage ], _
     [ ByVal iFlags ], _
     [ ByVal objWbemNamedValueSet ] _
    )
    
    ' iFlags get the flag
    
    

     

    Richard:

    Full documentation here: http://msdn.microsoft.com/en-us/library/aa393866(VS.85).aspx

    wbemFlagForwardOnly
    32 (0x20)

    Causes a forward-only enumerator to be returned. Forward-only enumerators are generally much faster and use less memory than conventional enumerators, but they do not allow calls to SWbemObject.Clone_.


    The docs are what I remembered in tehat only SeniSynchronous queries support this call.  In fact this method call is how to do a SemiSync query.

    The reference to 'Clone' method means that the objects are not cloned but deliverd one at a time for inspection. Rewindable. which is the same setting but opposite truth state - or the bit is a zero, causes the records to be copied into memory and held until you release the query.  Searching is faster but the need to repeatedly enumerate the same query will cause more network traffic and may be actually slower for susequent queries.

    For most purposes the forward only option would be better as suggested.

     


    jv
    Monday, August 1, 2011 5:31 PM
  • Thanks for the reply, but I'm a little confused as to how to use this. Could you give me a quick example?


    Richard Molinar posted an example of how to set the flag above.

     


    jv
    Tuesday, August 2, 2011 12:57 PM
  • Thanks a lot for all the help. I played with this a little bit and it seems faster, but I haven't really bench-marked it or anything. I wrote this cheesy little function though:

    Function New-WmiCustomConnection {
    param(
    	[string]$Computer = ".",
        [string]$Query = $(throw "Enter wmi query"),
        [string]$Scope = "\\$Computer\Root\cimv2",
    	[bool]$Rewindable = $False
      )
    	
    $Options = New-Object System.Management.EnumerationOptions
    #All Options:
    #	$context,
    #	[TimeSpan]10,
    #	[int]100,
    #	$rewindable,
    #	$returnImmediatley,
    #	$useAmendedQualifiers,
    #	$ensureLocatable,
    #	$prototypeOnly,
    #	$directRead,
    #	$enumerateDeep
    $Options.Rewindable = $Rewindable
    
    $wmiScope = new-Object System.Management.ManagementScope($Scope)
    $wmiQuery = New-Object System.Management.ObjectQuery $Query
    $Results = New-Object System.Management.ManagementObjectSearcher $wmiScope, $wmiQuery, $Options
    $Items = $Results.Get()
    Return $Items
    }


    I have a question, are any of the other options, apart from rewindable, particularly useful in querying WMI or is there no reason to set them to anything other than their defaults?

    Tuesday, August 2, 2011 4:31 PM
  • I've seen the Timeout property used. See this link on available settings.

    http://msdn.microsoft.com/en-us/library/system.management.enumerationoptions_members(v=VS.80).aspx

    However, the article seems to say the default value is Int64.MaxValue, which essentially is no timeout.

     


    Richard Mueller - MVP Directory Services
    Tuesday, August 2, 2011 4:47 PM
  • I have a question, are any of the other options, apart from rewindable, particularly useful in querying WMI or is there no reason to set them to anything other than their defaults?


    For performance - no.

    If you are trying to test you need to overome the screen IO issues.  Assign output to Out-Null as screen output can skew the results.

    Measure-Command {New-WmiCustomConnection | Out-Null} 

    This will give you an accurate measurement but only if run against teh llocal machine.  REmoting can introduce many big delays that are varibale per execution.

    Try querying a large domain for AD accounts with both methods and measure.  You should see a big difference.

    Here I altered you code a bit to get it to be mor like the defaults.  The default is Rewindable.  We want to set 'ForwardOnly"  make it a switch an flip teh boolean sense with a -not and teh switch works as required.

    Measure-command {New-WmiCustomConnection -forwardonly -query 'select * from cim_datafile'|Out-Null}

    Function New-WmiCustomConnection {
       param(
         [string]$Computer = ".",
         [string]$Query = $(throw "Enter wmi query"),
         #[string]$Scope = "\\$Computer\Root\cimv2",
         [switch]$ForwardOnly
       )
    	
       $Options = New-Object System.Management.EnumerationOptions
       <# All Options:
         $context,
         [TimeSpan]10,
         [int]100,
         $rewindable,
         $returnImmediatley,
         $useAmendedQualifiers,
         $ensureLocatable,
         $prototypeOnly,
         $directRead,
         $enumerateDeep
       #>
       [void]$Options.Rewindable = -not $ForwardOnly
       
       $wmiScope = new-Object System.Management.ManagementScope($Scope)
       $wmiQuery = New-Object System.Management.ObjectQuery($Query)
       $Results = New-Object System.Management.ManagementObjectSearcher($wmiScope, $wmiQuery, $Options)
       $Results.Get()
    }
    
    
    Wednesday, August 3, 2011 12:49 AM
  • Thanks tons for this example code!  It helped me immensely when Get-WMIObject was causing issues for large query results.

    Just a quick addition about the 'System.Management.ManagementObjectSearcher' object.  It is very tolerant about allowing you to redefine Options, Scope, and Query at run time.  See below for example.

    $Computers = @("localhost", "Computer1", "Computer2")
    $Classes = @("Win32_ComputerSystem", "Win32_OperatingSystem")
    
    $Results = New-Object System.Management.ManagementObjectSearcher
    $Results.Options = (New-Object System.Management.EnumerationOptions)
    $Results.Scope = (New-Object System.Management.ManagementScope("root\cimv2"))
    $Results.Query = (New-Object System.Management.ObjectQuery)
    
    $Results.Options.Rewindable = $false
    
    $Computers | ForEach-Object {
    
        $Results.Scope.Path.Server = $_
        
        $Classes | ForEach-Object {
        
            $Results.Query = "SELECT * FROM " + $_
            
            $Results.Get()
        }
    }
    

    Monday, April 22, 2013 7:03 PM
  • All of which can be changed with Get-WmiObject except for timeout.

    The type accelerator for the seaarcher is: [wmisearcher]

    Also note that teh full search creation can add the options and scope to teh creqation line.

    $results is not a good name for an active object.

    $searcher=[wmisearcher]

    OR

    $searcher=New-Object System.Management.ManagementObjectSearcher('\\server\root\CimV2','select * from Win32_BIOS')
    $results=$searcher.Get()

    Now you can do results AND:

    $searcher.Get()|select caption, serialnumber, Biosversion

    Now WMI work just like PowerSHell intends it to work.  No mixing of old VBScritp methods - just use Net methods.

    Stick with dotNet as it can really simplify things.


    ¯\_(ツ)_/¯

    Monday, April 22, 2013 8:46 PM