Huntland Services Ltd

Tel: +44 (0)1392-490518
Fax: +44 (0)1392-428003
Enquiries@huntland.co.uk

Using Synchronous, Asynchronous And Semi-synchronous  Operations in WMI Scripts

 

Back

Download Sample Scripts

 

WMI supports the possibility of using synchronous semi-synchronous and asynchronous operations.  Each type of operation has its own advantages.

Synchronous

This merely means that the operation being performed must be complete before data is handed back to the script.  In other words if your scripts performs a lengthy query of WMI, it will appear to hang while the data is being fetched.  This is ok for minor work and Ad Hoc queries but may cause difficulties for longer running scripts.  A typical query of the Application log on your local system would look like this:

Set objSvc = GetObject("winmgmts:")
Set objSet = objSvc.ExecQuery("Select * From Win32_NtLogEvent Where LogFile = 'Application'")
For Each obj in objSet
     count = count + 1
Next

The line 'For each obj in objSet' will not begin to execute until all the data has been returned from the previous line, the query.

Asynchronous

This time control is returned to your script as soon as the operation has started.  This is multi threading in VbScript!   Your script can now carry on with other tasks while WMI  manages how the asynchronous operation runs and feeds data back to the calling script.  WMI does this through via a SWBEMSINK object which acts as an intermediary.  You create this in your script and pass its name as a parameter when calling the operation.

Set objSvc = GetObject("winmgmts:")
Set sink = wscript.CreateObject("WbemScripting.SWbemSink","SINK_")
retVal = objSvc.ExecQueryAsync( sink,"Select * From Win32_ntLogEvent Where LogFile = 'Application'")
'get on with other tasks in the meanwhile, here.

When WMI has the  data to return it fires the OnObjectReady event which will execute the code in your script inside a subroutine called "SINK_OnObjectReady".  Notice the prefix for this subroutine must use the same name you specified when creating the sink above - in this case "SINK_".  If you want to specify "whoCares_" in the sink definition, then the subroutine must be called "whoCares_OnObjectReady".  For example

Sub SINK_OnObjectReady(objObject, objAsyncContext)
      count = count + 1
End Sub

Likewise you can create code that will be executed when the asynchronous operation has finished as in:

Sub SINK_OnCompleted(iHResult, objErrorObject, objAsyncContext)
      wscript.echo "The Asynchronous operation has finished"
End Sub

The iHResult , objErrorObject, and objAsyncContext you can ignore for the purposes of our simple script or read up about in the WMI SDK downloadable from Microsoft's MSDN web site.

Ok, so you think you've cracked the problem.  Then we learn that Asynchronous is not quite what you'd expect.  It turns out the mechanism used by WMI is to go and fetch the data you requested and collect the whole lot into the memory space of WMI.  Then when it's finally grabbed the whole lot, it dumps it out to the SWbemSink object and feeds it back to the script.  It's still faster if your script is doing other tasks but if the data being fetched is very large (maybe 100,000 records from the event log) it can cause WMI to stop the operation with an out of memory error..

Semi-Synchronous

This is the best of both worlds.  Easy to code and fast performance for multiple tasks.  WMI fetches the data, but immediately feeds it one record at a time back to the calling script so it can get on with processing it.  Notice, this is not multi-threading anymore.  You can't carry on with other tasks while waiting for the data, but at least you will finish processing that data faster so you can move on to the next sequential task.  The way to signal WMI that you want to execute an operation semi-synchronously is to set two special flags when you call it:

 

WbemFlagReturnImmediately

16

Returns control immediately to the calling script

WbemFlagForwardOnly

32

Returns each result as and when it arrives

The values for these two flags are ANDed to arrive at the appropriate value - 48.  Not all operations support these two flags and you will have to check in the WMI SDK documentation whether it's possible for whatever operation you want to perform.  Fortunately ExecQuery does.  For example:

Set objSvc = GetObject("winmgmts:")
Set objSet = objSvc.ExecQuery("Select * From Win32_NtLogEvent Where LogFile = 'Application'",,48)
For Each obj in objSet
     count = count + 1
Next

Notice the extra syntax in the ExecQuery line - ", , 48".  Now when this runs, unlike the very similar looking script in the synchronous example, the For Next loop executes almost immediately and keeps looping while WMI feeds in the data as its required.  It no longer matters if there are 100,000 events to process.

Download the three samples and try them on your system.  You may need a large number of events in the event log to see a significant difference in times.  The scripts are deliberately simple so the message does not become cluttered with surplus syntax.  For example, it is assumed the user of the script has sufficient access privileges to WMI locally, e.g. administrator.

Please accept these scripts as is.  Feedback will be gratefully accepted at Debug@huntland.co.uk