Page 1 of 1
WMI Event Handlers
Posted: Mon Sep 19, 2011 12:59 pm
by mikejs
I'm trying to use PB to create a process monitor utility - something that would be able to list what processes are running, but also detect when new processes are launched or quit. I have the first bit working:
Code: Select all
Define.COMateObject wmiobj, process
Define.COMateEnumObject query
Define.l c, result
wmiobj = COMate_GetObject("winmgmts://./root/cimV2")
query = wmiobj\CreateEnumeration("ExecQuery('Select * from Win32_Process')")
c=1
If query
Repeat
process=query\GetNextObject()
result=COMate_GetLastErrorCode()
If result=#S_OK
Debug "Process "+Str(c)+" Executable path: "+process\GetStringProperty("ExecutablePath")
EndIf
c+1
Until result=#S_FALSE Or result=#E_FAIL
query\release()
EndIf
wmiobj\release()
However, I'm getting a bit stuck with the second bit - detecting when a process is launched or quit. I don't want to just poll the list, as one of the things I'm interested in is picking up things that run transiently (i.e. launch, execute, and then quit, within a very short period of time).
I'm trying to make sense of this .Net code as a basis for this:
http://weblogs.asp.net/whaggard/archive ... 38006.aspx. Trying the query string in that .net code as a 'Notification query' using WBEMTEST (a built-in WMI browser), seems to work.
So, I assume I need to do something with SetEventHandler and that query string, but I can't see how to do this. Can anyone with more (any!) COM / WMI experience point me in the right direction here?
Alternatively, does anyone know of a way of doing this without involving COM / WMI in the first place?
Re: WMI Event Handlers
Posted: Mon Sep 19, 2011 2:39 pm
by mikejs
This is my current trial and error approach that gets the furthest:
Code: Select all
Define.COMateObject wmiobj, event
wmiobj = COMate_GetObject("winmgmts://./root/cimV2")
Debug "Result: "+COMate_GetLastErrorDescription()
event = wmiobj\GetObjectProperty("ExecQuery('SELECT * FROM __InstanceOperationEvent WITHIN 1 WHERE TargetInstance ISA Win32_Process')")
Debug "Result: "+COMate_GetLastErrorDescription()
event\SetEventHandler(#COMate_CatchAllEvents, @ProcCallback())
Debug "Result: "+COMate_GetLastErrorDescription()
This results in a "Method is not implemented" error at the sethandler stage.
Some of what's wrong is that I suspect the result of ExecQuery to return an enumerable list, and therefore event should be a COMateEnumObject, not a COMateObject. However, you can only do SetEventHandler on COMateObjects, not on COMateEnumObjects.
Also, a query isn't the same thing as a notification query, as far as I can tell, but I can't find any info on how to issue a notification query using COMate.
Re: WMI Event Handlers
Posted: Tue Sep 20, 2011 11:33 am
by srod
Well, it seems that you really need to use the 'ExecNotificationQueryAsync()' method of WMI which requires that you provide your own sink object to receive events. This would be quite a lot of work - though it doesn't look too difficult.
I've had a quick tinker with 'ExecNotificationQuery()' however, which might be worth exploring further. With this we can avoid any kind of sink object, though that does mean that we do not receive nice notifications etc, instead we have to poll for them (though we do not have to continually enumerate a collection of objects).
The following is a quick hack :
Code: Select all
IncludePath "..\..\"
XIncludeFile "COMatePLUS.pbi"
DisableExplicit
Define.COMateObject objWMIService, objProcess, colProcessList
strComputer.s = "."
objWMIService = COMate_GetObject("winmgmts://./root/cimV2")
If objWMIService
colProcessList = objWMIService\GetObjectProperty("ExecNotificationQuery('SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA $0027Win32_Process$0027')")
If colProcessList
OpenWindow(0, 100, 200, 195, 260, "PureBasic Window", #PB_Window_SystemMenu)
Repeat
Event = WaitWindowEvent(1)
If Event = #PB_Event_CloseWindow ; If the user has pressed on the close button
Break
EndIf
objProcess = colProcessList\GetObjectProperty("NextEvent(1)")
If objProcess
Debug "Closed process : " + objProcess\GetStringProperty("TargetInstance\Name")
objProcess\Release()
EndIf
ForEver
colProcessList\Release()
EndIf
objWMIService\Release()
Else
MessageRequester("COMate - WMI demo", "Couldn't create the WMI scripting object!")
EndIf
I hope it helps.
Re: WMI Event Handlers
Posted: Tue Sep 20, 2011 12:20 pm
by mikejs
That helps a great deal, thanks.
However, it only reports process that close while it's running, whereas I'm interested in processes starting as well.
If I change it to __InstanceOperationEvent, rather than __InstanceDeletionEvent, then I get many more events, but I'm not sure how I would then tell which ones are creations, which are deletions, and which are other things that I'm not interested in.
One approach which seems to work is to set up two queries, one for __InstanceCreationEvent, and one for __InstanceDeletionEvent, but that seems a bit messy.
Thanks again.
Re: WMI Event Handlers
Posted: Tue Sep 20, 2011 12:52 pm
by srod
No you only need the one query because if you use __InstanceOperationEvent then the objProcess\GetStringProperty("Path_\Class") will tell you what kind of event you are dealing with.
The following demonstrates. Run the following and then run notepad.
Code: Select all
IncludePath "..\..\"
XIncludeFile "COMatePLUS.pbi"
DisableExplicit
Define.COMateObject objWMIService, objProcess, colProcessList
strComputer.s = "."
objWMIService = COMate_GetObject("winmgmts://./root/cimV2")
If objWMIService
colProcessList = objWMIService\GetObjectProperty("ExecNotificationQuery('SELECT * FROM __InstanceOperationEvent WITHIN 1 WHERE TargetInstance ISA $0027Win32_Process$0027')")
If colProcessList
OpenWindow(0, 100, 200, 195, 260, "PureBasic Window", #PB_Window_SystemMenu)
Repeat
Event = WaitWindowEvent(1)
If Event = #PB_Event_CloseWindow ; If the user has pressed on the close button
Break
EndIf
objProcess = colProcessList\GetObjectProperty("NextEvent(1)")
If objProcess
If objProcess\GetStringProperty("TargetInstance\Name") = "notepad.exe"
Debug objProcess\GetStringProperty("TargetInstance\Name") + " : " + objProcess\GetStringProperty("Path_\Class")
EndIf
objProcess\Release()
EndIf
ForEver
colProcessList\Release()
EndIf
objWMIService\Release()
Else
MessageRequester("COMate - WMI demo", "Couldn't create the WMI scripting object!")
EndIf
Re: WMI Event Handlers
Posted: Tue Sep 20, 2011 1:47 pm
by mikejs
Works perfectly.
Thanks again.
Re: WMI Event Handlers
Posted: Tue Sep 20, 2011 2:05 pm
by Little John
mikejs wrote:Works perfectly.

Agreed!

Thank you, srod!
Regards, Little John
Re: WMI Event Handlers
Posted: Tue Sep 20, 2011 4:08 pm
by srod
An alternative if wishing for event notifications without the hassle of setting up a sink object, is to use COMatePLUS to run a VBS script etc. VBS can set up an appropriate sink object very easily.