-1

I'm creating a program that should monitor all the child processes after a initial one.

I'm doing this with ProcessExtensions.GetProcess function from process extensions, such as:

        Dim counter
        counter = 0

        Dim pr2 = ProcessExtensions.GetProcesses.ToList()
        Console.WriteLine(pr2.Count)
        For Each pr3 As Process In pr2
            counter = counter + 1
            Dim parentpr As Process = ProcessExtensions.Parent(pr3)  '' <- problem right here
               If parentpr IsNot Nothing Then
                   Console.WriteLine(counter & "  " & pr3.Id & "   " & pr3.ProcessName & "  " & pr3.StartTime & "   " & parentpr.Id)
               End If
        Next
        Console.ReadKey()

The problem is, this is too slow, it takes around half a second to check each process on windows 10, and with over 200 processes on average it doesn't allow me to check if a child process was started effectively.

I've considered creating a filter with a start date, but that filter will loop trough all the processes as well, and will not provide a result fast enough.

Meanwhile tasklist, or wmic process geton a command line provide instant results, but will force me to re-write all the code.

Is it possible to monitor when a process start on windows 10, instantly?

Other ideas are also welcomed.

I'm new to VB, thanks in advance for any help.

Edit: The problem is getting the ParentID. I'm using a custom ProcessExtensions module that gets it, but its a bit slow.

Public NotInheritable Class ProcessExtensions
    Inherits Process
    Public Sub New()
    End Sub
    Private Shared Function FindIndexedProcessName(ByVal pid As Integer) As String
        Try
            Dim processName = Process.GetProcessById(pid).ProcessName
            Dim processesByName = Process.GetProcessesByName(processName)
            Dim processIndexdName As String = Nothing

            For index As Integer = 0 To processesByName.Length - 1
                processIndexdName = If(index = 0, processName, Convert.ToString(processName) & "#" & index)
                Dim processId = New PerformanceCounter("Process", "ID Process", processIndexdName)
                If CInt(processId.NextValue()).Equals(pid) Then
                    Return processIndexdName
                End If
            Next

            Return processIndexdName
        Catch ex As ArgumentException
            Return Nothing
        End Try

    End Function

    Private Shared Function FindPidFromIndexedProcessName(ByVal indexedProcessName As String) As Process
        Dim parentId = New PerformanceCounter("Process", "Creating Process ID", indexedProcessName)
        Try
            Return Process.GetProcessById(CInt(parentId.NextValue()))
        Catch ex As Exception
            Return Nothing
        End Try

    End Function

    Public Function Parent() As Process
        Return FindPidFromIndexedProcessName(FindIndexedProcessName(Me.Id))
    End Function

    Public Shared Function Parent(ByVal pr As Process) As Process
        Return FindPidFromIndexedProcessName(FindIndexedProcessName(pr.Id))
    End Function

End Class
César Amorim
  • 367
  • 3
  • 16
  • I think you are using a custom `ProcessExtensions` module. you should add it too. – AliReza Sabouri Feb 09 '20 at 17:09
  • how long does it take if you just write this code `Dim pr = ProcessExtensions.GetProcesses.ToList()` ? – AliReza Sabouri Feb 09 '20 at 17:44
  • I asked because I think your problem is not about getting list of processes just want to make sure – AliReza Sabouri Feb 09 '20 at 17:49
  • I can get ToString(), but not ToList(), I don't have that function. – César Amorim Feb 09 '20 at 18:03
  • just add `import System.Linq` on top of your file – AliReza Sabouri Feb 09 '20 at 18:06
  • 1
    Maybe you want to test WMI's [Win32_Process](https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-process). The queries are cached. – Jimi Feb 09 '20 at 18:06
  • what dotnet framework you are using? 2.0? – AliReza Sabouri Feb 09 '20 at 18:07
  • 1
    sry check this instead: `Dim x = Process.GetProcesses.ToList()` or simply this should work `dim x = Process.GetProcesses` – AliReza Sabouri Feb 09 '20 at 18:12
  • Thanks AliReza, I apologize as you can see I'm very fresh with programming. It was on .NET 2.0 in fact. Still the ToList is a major improvement as it is almost instant. The problem is getting the ParentID. I'll update the answer – César Amorim Feb 09 '20 at 18:28
  • @CésarAmorim I added a multi-thread sample. your code does not have big issues it's fine but the problem is the main for each you used ... it takes time to get child processes for every process in the list. so making this multi-thread can solve this. feel free to ask if you had any questions. good luck – AliReza Sabouri Feb 09 '20 at 19:40
  • @CésarAmorim I provided a faster solution for you. please consider approving the answer if it solved your problem, if not, feel free to ask any questions. good luck – AliReza Sabouri Feb 10 '20 at 12:51

1 Answers1

1

I refactored some parts of your code and created a multi-thread sample for you. in my system I had

0.100ms execution time before refactoring

0.086ms after refactoring

0.020ms using multiple threads.


Sub Main

    Dim sw = New Stopwatch()
    sw.Start()
    ' you should await this task -> await StartMonitoring
    StartMonitoring().GetAwaiter().GetResult()
    sw.Stop()
    Console.WriteLine($"Done in {sw.ElapsedMilliseconds} ms.")
End Sub

Public Async Function StartMonitoring() As Task
    Dim pr2 = ProcessExtensions.GetProcesses.ToList()
    Console.WriteLine(pr2.Count)
    Dim counter = 0
    Dim taskList = New List(Of Task)
    For Each pr3 As Process In pr2
        taskList.Add(CreateTask(pr3, counter))
        counter = counter + 1
    Next
    Await Task.WhenAll(taskList)
End Function

Public  Function CreateTask(pr As Process ,counter As Integer) As Task 
    Return Task.Run(
        Sub()
            Dim parentpr As Process = ProcessExtensions.Parent(pr)
            If parentpr IsNot Nothing Then
                Console.WriteLine(counter & "  " & pr.Id & "   " & pr.ProcessName & "  " & parentpr.Id & " "  & Thread.CurrentThread.ManagedThreadId)
            End If
        End Sub)
End Function


Public NotInheritable Class ProcessExtensions
    Inherits Process

    Public Sub New()
    End Sub

    Private Shared Function FindIndexedProcessName(pr As Process) As String
        Try
            Dim processName = pr.ProcessName
            Dim childProcesses = Process.GetProcessesByName(processName) 
            Dim processIndexdName As String = Nothing

            For index As Integer = 0 To childProcesses.Length - 1
                processIndexdName = If(index = 0, processName,  processName & "#" & index)
                Dim processId = New PerformanceCounter("Process", "ID Process", processIndexdName)
                If CInt(processId.NextValue()).Equals(pr.Id) Then
                    Return processIndexdName
                End If
            Next
            Return processIndexdName
        Catch ex As Exception
            Return Nothing
        End Try

    End Function

    Private Shared Function FindPidFromIndexedProcessName(ByVal indexedProcessName As String) As Process
        Dim parentId = New PerformanceCounter("Process", "Creating Process ID", indexedProcessName)
        Try
            Return Process.GetProcessById(CInt(parentId.NextValue()))
        Catch ex As Exception
            Return Nothing
        End Try

    End Function

    Public Function Parent() As Process
        Return FindPidFromIndexedProcessName(FindIndexedProcessName(Me))
    End Function

    Public Shared Function Parent(ByVal pr As Process) As Process
        Return FindPidFromIndexedProcessName(FindIndexedProcessName(pr))
    End Function

End Class

hope it gives you some idea how you can improve this functionality.

AliReza Sabouri
  • 4,355
  • 2
  • 25
  • 37
  • Hi Ali, just a small fix, on the function `CheckParent`, it should be `Dim tsk = Task.Factory.StartNew(` for vb.net. Also, could you point how and where do you measure the time that it takes for the code to run? I mean how did you found the miliseconds values? – César Amorim Feb 10 '20 at 13:39
  • you can use StopWatch to check your execution time. https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.stopwatch?view=netframework-4.8 – AliReza Sabouri Feb 10 '20 at 13:58
  • @CésarAmorim I double-checked my answer and noticed I had a mistake about waiting for results. sorry about that. check this I fixed that issue. – AliReza Sabouri Feb 10 '20 at 15:33
  • also, I added stopWatch to measuring execution time. – AliReza Sabouri Feb 10 '20 at 15:37
  • and you can check this link if you want to know why I am using `Task.Run` https://stackoverflow.com/questions/38423472/what-is-the-difference-between-task-run-and-task-factory-startnew – AliReza Sabouri Feb 10 '20 at 16:00