views:

557

answers:

2

I would like to develop an application (that runs at all times in the background I guess) to track the usage of applications (msword, excel, powerpoint, *.exe, etc.).

I can handle posting the data to a database but don't exactly know where to start with monitoring running applications (when they start, when they stop). Anyone have any clues?

+1  A: 

You could poll all top-level windows periodically and record the Window title text; and based on that do some conclusions as to which program are running.

Or you could do it via process name, or a combination.

For enumerating windows, see http://msdn.microsoft.com/en-us/library/ms633497(VS.85).aspx, and here is how to use it in managed code. For enumerating processes, use the Process.GetProcesses method or see http://www.codeproject.com/KB/threads/enumprocess.aspx

driis
+3  A: 

You can periodically poll a list of the running processes using System.Diagnostics.Process.GetProcesses().

This following code outputs information about starting and exiting processes. Exiting system processes won't be recognized.

class Program
{
    struct ProcessStartTimePair
    {
        public Process Process { get; set; }
        public DateTime StartTime { get; set; }
        public DateTime ExitTime
        {
            get
            {
                return DateTime.Now; // approximate value
            }
        }

        public ProcessStartTimePair(Process p) : this()
        {
            Process = p;
            try
            {
                StartTime = p.StartTime;
            }
            catch (System.ComponentModel.Win32Exception)
            {
                StartTime = DateTime.Now; // approximate value
            }
        }
    }

    static void Main(string[] args)
    {
        List<ProcessStartTimePair> knownProcesses = new List<ProcessStartTimePair>();
        while (true)
        {
            foreach (Process p in Process.GetProcesses())
            {
                if (!knownProcesses.Select(x => x.Process.Id).Contains(p.Id))
                {
                    knownProcesses.Add(new ProcessStartTimePair(p));
                    Console.WriteLine("Detected new process: " + p.ProcessName);
                }
            }

            for (int i = 0; i < knownProcesses.Count; i++) 
            {
                ProcessStartTimePair pair = knownProcesses[i];
                try
                {
                    if (pair.Process.HasExited)
                    {
                        Console.WriteLine(pair.Process.ProcessName + " has exited (alive from {0} to {1}).", pair.StartTime.ToString(), pair.ExitTime.ToString());
                        knownProcesses.Remove(pair);
                        i--; // List was modified, 1 item less
                        // TODO: Store in the info in the database
                    }
                }
                catch (System.ComponentModel.Win32Exception)
                {
                    // Would have to check whether the process still exists in Process.GetProcesses().
                    // The process probably is a system process.
                }
            }
            Console.WriteLine();
            System.Threading.Thread.Sleep(1000);
        }
    }
}

You probably can simply ignore system processes that don't let you read the HasExited property.

Edit

Here's a .net 2.0 version:

    static void Main(string[] args)
    {
        List<ProcessStartTimePair> knownProcesses = new List<ProcessStartTimePair>();
        while (true)
        {
            foreach (Process p in Process.GetProcesses())
            {
                if (!ProcessIsKnown(knownProcesses, p.Id))
                {
                    knownProcesses.Add(new ProcessStartTimePair(p));
                    Console.WriteLine("Detected new process: " + p.ProcessName);
                }
            }

            for (int i = 0; i < knownProcesses.Count; i++) 
            [...]
        }
    }

    static bool ProcessIsKnown(List<ProcessStartTimePair> knownProcesses, int ID)
    {
        foreach (ProcessStartTimePair pstp in knownProcesses)
        {
            if (pstp.Process.Id == ID)
            {
                return true;
            }
        }
        return false;
    }

Note that the code could be improved and only shows the concept.

What happens when the process disappears? Do you have to poll every minute to get 1 minute resolution?
Robert Harvey
Yes, you would have to pull every minute for that. I would store all the existing processes in a list and check if there are new ones or if some of them have exited.
Yeah I would think that once the application closes then ExitTime would not be accisble (as it is not running). I want to be able to say that user 'jdoe' used excel for a total of 44 minutes in July
Jason
I also found that a few minutes ago, some processes also don't let the app read their HasExited property. I'm preparing a code sample now.
Great code example...just what I was looking for.
Jason
I am forced to work with .Net 2.0. I assume the ".Select" is a method in the linq namespace...any work around for:if (!knownProcesses.Select(x => x.Process.Id).Contains(p.Id)) {knownProcesses.Add(new ProcessStartTimePair(p)); Console.WriteLine("Detected new process: " + p.ProcessName);}
Jason