Как повысить отзывчивость логики 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

Нет разницы.

Другие вопросы по тегам