How to determine if another application is active?

  • 14 years ago

    I am working in VB.Net VS2005 (.NET 2.0).
    I need a way to determine if another (third party) application on the PC is running and is active.
    being active means, the user is actually using it(clicking on it, typing in it, scrolling ...etc).

    Background:
    I have a project to monitor certain third party applications on users' PCs and determine if these applications are activley being used or if they are idle and not being used.
    These are third party applications and the idea is to close down this application after a period of 10 minutes of being inactive so as to reclaim the license.

    I am working with the process class which exposes certain (basic) information about the process, however, i still can not figure out how to tell if there is a way to monitor if an application is being used (active/inactive).

    Your help would be appreciated.

    Thanks!

  • 14 years ago

    Well I'm not sure I'll be much help, but this is my idea.  How you implement it I haven't a clue but maybe it will help get you started.  You obviosly need to know which application are "running".  By running I don't mean being used.  Then you could create a timer for each application.  The timer should be reset if a key is pressed or mouse is moved and that application is the active window.  Whenever a timer hits 10 minutes you shut the program down.  This of course assumes the types of programs you are monitoring aren't meant to just run in the background like encoding a movie or something.  To monitor mouse and keyboard activity you'll need a system hook.  I have know idea how it works but you should look up 'mouse hooks' or ask about them.  I've done a little research in that area but never got anything working.  Sorry, that's all I've got, just ideas and nothing of real great help but maybe what I said will help give you and idea about how it could be done.

  • 14 years ago

    I had an inspiration today.  I found myself playing around with the 'process' object.  I was looking at the methods and properties and noticed it had a wealth of information there.  The most intriguing and I believe applicable to your problem is CPU usage.  Applications that are being used need the CPU.  Applications that aren't being used don't need it, or to be more accurate need very little of it.  I figured you could monitor CPU usage over a period of time and if it's very low (a result of an idle application) you can kill it.  So I set to work on a class that will monitor an applications processes using the principal already mentioned.  It probably could use a bit of fine tuning but it works well as far as I can tell.  I'd explain it but I think the comments should do most of the talking.  If you have any questions, comments, or suggestions please post them.  I kind of went overboard (I do that occasionally on these forums) but the topic intrigued me and I thought my solution would work.  I really hope you can use it or at least the principal. 

    Here is the class code along with a single helper class.

    '---Purpose: Monitor processes and terminate them if not in use---
    'A process is deemed idle (not in use) if it's CPU usage does not meet a certain mininum average
    Public Class ProcessMonitor
        '---DECLARATIONS---
        Private WithEvents tmr As New Timer                 'Timer to do the checking at set intervals
    
        Private m_NamesToMonitor As New List(Of String)     'Names of processes to monitor ie. iexplore (Internet Explorer)
        Private m_Processes As New Hashtable                'Data for every running process being monitored
        '                                                   'Key = Process.ID & ProcessName; Value = MonitorData
    
        Private m_Interval As Integer                       'How often processes should be checked/updated (Milliseconds)
        Private m_KillTime As Integer                       'When should a process be killed (Minutes)
        Private m_Threshold As Integer                      'Minimum AVG. Milliseconds/minute of CPU time we consider to be active
    
    
        '---PROPERTIES---
    
        '---Get or Set timers interval---
        'This interval determines how often new processes are checked for and how often we
        'check if they should be killed.
        Public Property Interval() As Integer
            Get
                Return tmr.Interval
            End Get
            Set(ByVal value As Integer)
                tmr.Interval = value
            End Set
        End Property
    
        '---Get or Set the threshold time---
        Public Property Threshold() As Integer
            Get
                Return m_Threshold
            End Get
            Set(ByVal value As Integer)
                m_Threshold = value
            End Set
        End Property
    
        '---Get or Set the time to Kill
        Public Property KillTime() As Integer
            Get
                Return m_KillTime
            End Get
            Set(ByVal value As Integer)
                If value > 0 Then m_KillTime = value
            End Set
        End Property
    
        '---Use default values---
        Public Sub New()
            Interval = 5000     '5 second intervals
            KillTime = 1       'Kill after 1 minute (For testing I wanted quick)
            Threshold = 100     'Minimum AVG of 100 milliseconds/min of CPU time to consider active
        End Sub
    
        '---Adds a single process name to monitor---
        Public Sub Add(ByVal processName As String)
            AddRange(New String() {processName})
        End Sub
    
        '---Adds multiple process names to monitor---
        Public Sub AddRange(ByVal processNames As String())
            For Each name As String In processNames
                '---If we are monitoring the name already just move on---
                If m_NamesToMonitor.Contains(name) Then Continue For
                '---Add name to list of names to be monitored---
                m_NamesToMonitor.Add(name)
                AddProcesses(name)
            Next
            CheckMonitoring()
        End Sub
    
        '---Add running processes for monitoring that match the given name---
        Private Sub AddProcesses(ByVal name As String)
            '---Loop through each process that has the given name---
            For Each proc As Process In Process.GetProcessesByName(name)
                Dim key As String = ProcessKey(proc)
                '---If process is not already being monitored then add it---
                If Not m_Processes.ContainsKey(key) Then
                    m_Processes.Add(key, New MonitorData(proc))
                    '---Allow the process object to raise events---
                    proc.EnableRaisingEvents = True
                    '---Add a handler for the Exited event so we know when a process is killed---
                    AddHandler proc.Exited, AddressOf ProcessExited
                End If
            Next
        End Sub
    
        '---Checks to see if there is a need to continue monitoring---
        Public Sub CheckMonitoring()
            '---Stop if there are no names to monitor---
            If m_NamesToMonitor.Count = 0 Then
                tmr.Stop()
                Form1.Text = "Stopped"
            ElseIf Not tmr.Enabled Then
                'Start monitoring if we aren't
                tmr.Start()
                Form1.Text = "Running"
            End If
        End Sub
    
        '---Add any new processes that may have started---
        Private Sub Refresh()
            For Each name As String In m_NamesToMonitor
                AddProcesses(name)
            Next
        End Sub
    
        '---A process was killed---
        Private Sub ProcessExited(ByVal sender As Object, ByVal e As System.EventArgs)
            Dim proc As Process = CType(sender, Process)        'Process that was killed
            RemoveHandler proc.Exited, AddressOf ProcessExited  'Remove handler
            m_Processes.Remove(ProcessKey(proc))                'Stop monitoring
        End Sub
    
        '---Returns a unique key that represents the process---
        'Format: ProcessID & ProcessName
        'ID may have been sufficient but I wasn't sure
        Public Function ProcessKey(ByVal proc As Process) As String
            Return proc.Id.ToString & proc.ProcessName
        End Function
    
        '---The actual monitoring---
        Private Sub tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles tmr.Tick
            'DEBUG PURPOSES ONLY
            Form1.ListBox1.Items.Clear()
    
            '---Check each process being monitored---
            For Each data As MonitorData In m_Processes.Values
                '---Time elapsed since we last checked it (seconds)
                Dim elapsed As Integer = DateDiff(DateInterval.Second, data.LastChange, Now)
    
                'DEBUG PURPOSES ONLY
                Form1.ListBox1.Items.Add(data.Proc.ProcessName & " (PID=" & data.Proc.Id & "): " & _
                   elapsed & " sec (AVG usage " & _
                   CInt(data.Proc.UserProcessorTime.Subtract(data.CPUTime).Milliseconds / elapsed * 60) & _
                   " ms)")
    
                '---If time elapsed has exceded our killtime then check the process for activity---
                If elapsed >= KillTime * 60 Then
                    Dim CPUTime As TimeSpan = data.Proc.UserProcessorTime
                    Dim diff As TimeSpan = CPUTime.Subtract(data.CPUTime)
    
                    '---Is the average CPU usage abover our threshold---
                    If CInt(diff.Milliseconds / elapsed * 60) > Threshold Then
                        'YES - Reset our times; the process appears active
                        data.CPUTime = CPUTime
                        data.LastChange = Now
                    Else
                        'NO - Kill the process it does not appear to be active
                        If Not data.Proc.HasExited Then data.Proc.Kill()
                    End If
                End If
            Next
    
            Refresh()
            CheckMonitoring()
        End Sub
    End Class
    Here is the code for the helper class which is required.
    '---Helper class---
    'Stores the the process being monitored as well as time a stamp and performance
    'information so it can be monitored
    Public Class MonitorData
        Private m_lastChange As Date        'When this process was last checked
        Private m_CPUTime As TimeSpan       'The CPU usage the last time checked
        Private m_Proc As Process           'The actual process being monitored
    
        '---Constructor---
        'Accepts a single process object
        Public Sub New(ByVal proc As Process)
            m_Proc = proc
            Me.CPUTime = proc.UserProcessorTime
            LastChange = Now
        End Sub
    
        Public ReadOnly Property Proc() As Process
            Get
                Return m_Proc
            End Get
        End Property
    
        Public Property LastChange() As Date
            Get
                Return m_lastChange
            End Get
            Set(ByVal value As Date)
                m_lastChange = value
            End Set
        End Property
    
        Public Property CPUTime() As TimeSpan
            Get
                Return m_CPUTime
            End Get
            Set(ByVal value As TimeSpan)
                m_CPUTime = value
            End Set
        End Property
    End Class
    
    Here is the code for a simple form I used to test my class
    'The form requires a listbox, two textboxes, and two buttons
    Public Class Form1
        Dim monitor As New ProcessMonitor   'ProcessMonitor object
    
        'Add name in Textbox1 for monitoring
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            'Add name for monitoring
            If TextBox1.Text.Trim <> String.Empty Then
                monitor.Add(TextBox1.Text.Trim)
                TextBox1.Clear()
            End If
        End Sub
    
        'Get the ProcessName of the ID entered into TextBox2
        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
            Dim id As Integer
            If Integer.TryParse(TextBox2.Text, id) Then
                Try
                    MsgBox(Process.GetProcessById(id).ProcessName)
                Catch ex As Exception
                    MsgBox("ID not found")
                End Try
            End If
        End Sub
    
        Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
            '
            'Button1
            '
            Me.Button1.Location = New System.Drawing.Point(160, 11)
            Me.Button1.Name = "Button1"
            Me.Button1.Size = New System.Drawing.Size(122, 20)
            Me.Button1.Text = "Monitor Name"
            '
            'TextBox1
            '
            Me.TextBox1.Location = New System.Drawing.Point(12, 12)
            Me.TextBox1.Name = "TextBox1"
            Me.TextBox1.Size = New System.Drawing.Size(142, 20)
            '
            'ListBox1
            '
            Me.ListBox1.Location = New System.Drawing.Point(12, 82)
            Me.ListBox1.Name = "ListBox1"
            Me.ListBox1.Size = New System.Drawing.Size(370, 121)
            '
            'Button2
            '
            Me.Button2.Location = New System.Drawing.Point(160, 55)
            Me.Button2.Name = "Button2"
            Me.Button2.Size = New System.Drawing.Size(122, 21)
            Me.Button2.Text = "Get ProcessName"
            '
            'TextBox2
            '
            Me.TextBox2.Location = New System.Drawing.Point(12, 55)
            Me.TextBox2.Name = "TextBox2"
            Me.TextBox2.Size = New System.Drawing.Size(142, 20)
        End Sub
    End Class

Post a reply

Enter your message below

Sign in or Join us (it's free).

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law.”