Как повысить отзывчивость логики ManagementEventWatcher?
Меня подозревают Win32_ProcessStartTrace
события класса для уведомления о запуске процесса на ПК:
Me.processStartWatcher =
New ManagementEventWatcher(New WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"))
Тем не менее, это кажется неэффективным, потому что для запуска события после запуска исполняемого файла требуется 1 или 2 секунды:
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
Это означает, что если процесс запущен и завершился быстро, я не буду уведомлен.
Есть ли что-то, что я мог бы сделать, чтобы повысить отзывчивость на ManagementEventWatcher
объект?.
Я пытался установить Timeout
свойство, но, как говорится в описании члена, кажется, это не имеет ничего общего с этой целью.
1 ответ
Вы используете WITHIN
в вашем WMI-запросе укажите интервал опроса. В статье содержится множество предупреждений о том, что использование небольших интервалов не рекомендуется. Но продвиньтесь вперед и попробуйте это в любом случае:
Me.processStartWatcher =
New ManagementEventWatcher(
New WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace WITHIN 0.1"))
И вы поймете, что это просто не имеет никакого значения, по умолчанию уже так быстро, как может быть. Это архитектурно в Windows, ядро не имеет "крючка" для генерации уведомления при создании процесса. Как бы это ни было важно, скажем, для антивирусного сканера. Они вынуждены исправлять ОС, чтобы иметь возможность подключиться. Поставщик WMI, который реализует этот запрос (C:\Windows\System32\wbem\KrnlProv.dll), не делает этого.
Еще один способ убедиться в этом - просто реализовать это самостоятельно с помощью класса Process. Например:
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
С примером программы, которая использует его:
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
Нет разницы.