I'm suscribed to Win32_ProcessStartTrace
class events to notify when a process is ran on the PC:
Me.processStartWatcher =
New ManagementEventWatcher(New WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"))
However, this seems not efficient because it takes 1 or 2 seconds to fire the event after I ran an executable file:
Private Sub ProcessStartWatcher_EventArrived(sender As Object, e As EventArrivedEventArgs) _
Handles processStartWatcher.EventArrived
If (Me.ProcessStartedEvent IsNot Nothing) Then
RaiseEvent ProcessStarted(Me, e)
End If
End Sub
This means that if a process is ran and has exited quickly then I will not be notified.
Is there something I could do to increase responsiveness on a ManagementEventWatcher
object?.
I tried to set the Timeout
property but as the description of the member says, it seems does have nothing to do with this purpose.
You use the WITHIN
clause in your WMI query to specify the polling interval. The article gives plenty of warning that using small intervals is not recommended. But forge ahead and try it anyway:
Me.processStartWatcher =
New ManagementEventWatcher(
New WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace WITHIN 0.1"))
And you'll find out that it simply doesn't make any difference, the default is already as fast as it can be. This is architectural in Windows, the kernel has no 'hook' to generate a notification when a process is created. Important as it might be to, say, a virus scanner. They are forced to patch the OS to be able to jump in. The WMI provider that implements this query (C:\Windows\System32\wbem\KrnlProv.dll) does not do this.
Another way to see this is to just implement it yourself with the Process class. For example:
Public Class ProcessMonitor
Public Event Started As Action(Of Integer)
Public Event Stopped As Action(Of Integer)
Public Sub New(interval As Integer)
Me.interval = interval
running = Scan()
timer = New Threading.Timer(AddressOf callback, Nothing, interval, 0)
End Sub
Private Sub callback(state As Object)
Dim active As HashSet(Of Integer) = Scan()
Dim started As IEnumerable(Of Integer) = active.Except(running)
Dim stopped As IEnumerable(Of Integer) = running.Except(active)
running = active
For Each pid As Integer In started
RaiseEvent started(pid)
Next
For Each pid As Integer In stopped
RaiseEvent stopped(pid)
Next
timer.Change(interval, 0)
End Sub
Private Function Scan() As HashSet(Of Integer)
Dim ret As New HashSet(Of Integer)
For Each proc As Process In Process.GetProcesses()
ret.Add(proc.Id)
Next
Return ret
End Function
Private running As HashSet(Of Integer)
Private timer As System.Threading.Timer
Private interval As Integer
End Class
With a sample program that uses it:
Module Module1
Sub Main()
Dim monitor As New ProcessMonitor(100)
AddHandler monitor.Started, AddressOf Started
AddHandler monitor.Stopped, AddressOf Stopped
Console.ReadLine()
End Sub
Sub Started(pid As Integer)
Console.WriteLine("Started: {0}", Process.GetProcessById(pid).ProcessName)
End Sub
Sub Stopped(pid As Integer)
Console.WriteLine("Stopped: {0}", pid)
End Sub
End Module
No difference.