$wmi -destroyat the end when you no longer need WMI.Also, the Tcl event loop must be running, either through Tk or through a vwait command.[Maiki] - 2011-05-06 09:08:40Assumes twapi 3.0 - will need to be modified for earlier versions
proc start_process_tracker {} {
# Get local WMI root provider
set ::wmi [twapi::_wmi]
# Create an WMI event sink
set ::event_sink [twapi::comobj wbemscripting.swbemsink]
# Attach our handler to it
set ::event_sink_id [$::event_sink -bind process_start_handler]
# Associate the sink with a query that polls every 1 sec for process
# starts.
$::wmi ExecNotificationQueryAsync [$::event_sink -interface] "select * from __InstanceCreationEvent within 1 where TargetInstance ISA 'Win32_Process'"
}
proc process_start_handler {wmi_event args} {
if {$wmi_event eq "OnObjectReady"} {
# First arg is a IDispatch interface of the event object
# Create a TWAPI COM object out of it
set ifc [lindex $args 0]
IUnknown_AddRef $ifc; # Must hold ref before creating comobj
set event_obj [twapi::comobj_idispatch $ifc]
# Get and print the Name property
puts "Process [$event_obj ProcessID] [$event_obj ProcessName] started at [clock format [large_system_time_to_secs [$event_obj TIME_CREATED]] -format {%x %X}]"
# Get rid of the event object
$event_obj -destroy
}
}Avoiding Hangs in Queries neb 2011-07-19 I've been using a set of wmi queries on a large number of machines. Occasionally, they will hang for no apparent reason. Everything works, connects, etc, but the query just never returns. This is an oft-asked-about issue, online; and Microsoft never saw fit to offer any way to cancel or timeout a query. You can, however, use an Asynchronous (as opposed to a 'Semisynchronous') query using close to the same process as above.The following code is a knock-off of this MSDN example: http://msdn.microsoft.com/en-us/library/aa392295%28v=vs.85%29.aspx

package require twapi
set ::bdone 0
set count 0
proc change_handler {wmi_event args} {
switch $wmi_event {
OnObjectReady {
# Create a TWAPI COM object out of the IDispatch interface (first arg)
set ifc [lindex $args 0]
::twapi::IUnknown_AddRef $ifc; # Must hold ref before creating comobj
set event_obj [twapi::comobj_idispatch $ifc]
puts "Name: [$event_obj Name]"
incr ::count
$event_obj destroy
}
OnCompleted {
set ::bdone 1
}
}
}
set eventsink [twapi::comobj wbemscripting.swbemsink]
set eventsink_id [$eventsink -bind change_handler]
set wmi [twapi::_wmi]
after 12 {if {!$::bdone} {set ::bdone 2}}
set wql {SELECT Name FROM Win32_Process}
$wmi ExecQueryAsync [$eventsink -interface] $wql
vwait bdone
$eventsink -unbind $eventsink_id
$eventsink -destroy
$wmi -destroy
after 3000
puts "$count objects"
if {$::bdone==2} {puts timedout}'after 12' corresponds to roughly how long it takes the machine I am on to return the first record. Adding one ms results in the rest of the list all burped out at once.A more annotated version follows. Note it also uses '$eventsink Cancel' for the timeout action, rather than relying on the variable. Canceling triggers the 'OnCompleted' event, with a result value of 0x80041032 (user cancel). It also seems to work faster: Cancel will interrupt the list in around 6ms on my machine, vs 12 for the version above.There should be a way to turn the objErrorObject parameter into a COM or SWbemLastError object (which would have a GetObjectText_ method), but I haven't worked it out.package require twapi
set ::bdone 0
set count 0
proc change_handler {wmi_event args} {
switch $wmi_event { # http://msdn.microsoft.com/en-us/library/gg196623%28v=VS.85%29.aspx
OnObjectReady { # objObject, objAsyncContext
# Create a TWAPI COM object out of the IDispatch interface (first arg)
set ifc [lindex $args 0]
::twapi::IUnknown_AddRef $ifc; # Must hold ref before creating comobj
set event_obj [twapi::comobj_idispatch $ifc]
puts "Name: [$event_obj Name]"
incr ::count
$event_obj -destroy
}
OnCompleted { # iHResult, objErrorObject, objAsyncContext
set ::bdone 1
if {[lindex $args 0]} {
puts "Completed with error of 0x[format %08X [lindex $args 0]] (0x80041032 = user cancel)"
}
}
OnProgress { # iUpperBound, iCurrent, strMessage, objWbemAsyncContext
# Needs the '128' in the ExecQueryAsync call (4th param). Not all queries supply progress info.
puts Progress
}
OnObjectPut { # objWbemObjectPath, objWbemAsyncContext
puts Put
}
default {
puts "Unhandled event: $wmi_event"
}
}
}
set eventsink [twapi::comobj wbemscripting.swbemsink]
set eventsink_id [$eventsink -bind change_handler]
set wmi [twapi::_wmi]
after 6 {$eventsink Cancel; puts "query timed out."}
set wql {SELECT Name FROM Win32_Process}
# params: eventsink, query string, must be 'WQL', 128 (0x80) = wbemFlagSendStatus
$wmi ExecQueryAsync [$eventsink -interface] $wql WQL 128
vwait bdone
puts "unmaking objects"
$eventsink -unbind $eventsink_id
$eventsink -destroy
$wmi -destroy
puts "Pausing to make sure everything is canceled."
after 3000
puts "$count objects"One thing I am worried about with these is that we set event_obj on each record returned, but I can't destroy it. Uncommenting the destroy line stops the query results at that point. In VBS examples online, they don't worry about destroying the objects--but they also don't have to create them. I'm not certain if the result of comobj_idispatch gets re-used, and then dies when it goes out of scope, or if it's creating a new object at every iteration (and how to delete it), and therefore these scripts have a memory leak.APN neb, I've modified your examples above to do an IUnknown_AddRef on the event object interface before converting it to a comobj. This is required in the general case. You can then call $event_obj destroy. and I believe you will not see the query results stop. Otherwise, yes, you will have a memory leak of comobj objects. Let me know if that does not work for you.IMHO, WMI is a completely unreliable piece of crap. I spent long hours debugging what I thought were problems in TWAPI's event handling when it turned out VB code and MS own SDK tools exhibited the same behaviours with hangs and timeouts. I don't know if the WMI implementations have improved in the last few years.neb: Thank you, APN. I had tried putting in an addref, but mucked it up somewhere. I've made the references you added explicit, so they run as-is on my machine.And no, WMI has not improved, in my experience. Aside from the intermittent and inexplicable hangs, I find it easy to corrupt the repository. Eg, we had a range of subnets that had originally been on Novell. When it was uninstalled, WMI corrupted on approximately 50% of the machines, requiring a stop/delete/restart type of refresh. It's not immediately obvious *why* things don't work, in that situation. WMI just sort of stops giving back results.

