April 2nd, 2007

It’s Elementary: Using VB To Get Process Information (Matt Gertz)

I’ve had a wide variety of jobs over my career, beyond working at Microsoft.  I’ve been a burger-flipper and an administrative assistant.  I’ve worked with the DOE and NASA on robotics projects, and back in my NROTC days I spent a month at the helm & planes of a nuclear submarine.  I spent a summer doing really tedious data entry, another summer writing an accounts receivable program for local businesses in my home town, and a couple of summers writing CAD/CAM models for IC chips.  But I think one of the most interesting things I ever did, for one brief moment, was when I got to be a detective.

At the time it happened (1993), I was in the graduate program at Carnegie Mellon.  I was a confirmed “Macintoid” at the time, and the only other platforms I knew well were Unix and VAX/VMS (yep, no DOS or Windows for me).  So, one evening I got a call from a friend who had graduated the previous year and was now working at a well-known government agency (which shall remain nameless for the purposes of this article).  He had a problem:  his management suspected that one of their employees was loading & using truly inappropriate software on one of their taxpayer-purchased Macs, but they couldn’t prove it. I can’t actually remember why the suspicion existed, but anyway their working assumption was that he was running it from floppy disk, so as to not leave any footprints behind on the hard drive.  My friend wondered if I had any ideas how they might prove this. 

Well, I happened to know that, if the software had registered a particular file type on the Mac (as specified by a four-character code), there were tools to see that file code unless the user rebuilt the desktop after the program was removed – but even in the latter case, there would be a log of the rebuild which would itself be circumstantial evidence.  And, to make a long story short, it indeed turned out that the employee was running a truly heinous piece of software on the Mac, and ended up looking for another job.

The point of all of this is that there’s a lot happening on a computer that isn’t always terribly obvious, and a lot of information there which might not be immediately apparent but that can be got at.  This came back to me a few months ago when I was confronted with a particular problem on my home PC.  It was a new PC, and what was happening was that every fifteen minutes, any application that I was running would lose focus.  This was extremely annoying whenever I was playing a full-screen video game (for instance, when fighting a battle) – suddenly the game would minimize and disappear, and by the time I would maximize, my character would be dead.  If I watched carefully, I could see the interfering app briefly showing up in the task bar, but too fast for me to do anything with it.  Obviously, there was some agent on my machine which was stealing focus on a regular basis, but what was it?  My startup folder was empty, so that wasn’t the problem.  The problem must be with an application which got started from the “Run” key in the registry, but there were about twenty of those – which was the culprit?  Task manager was no help; there were too many tasks running.

Well, I knew that there was a focus issue involved, and that windows get an event when they lose focus – perhaps I could use Visual Basic to track the problem down!  I’d need an application with a window, so I launched Visual Studio and created a windows app.  Right-clicking on the form in the project treeview, I chose “View Code,” and in the resulting code editor I changed the left-hand dropdown to “(Form1 events)” and the right-hand dropdown to “Lost Focus” to create the appropriate event handler.  Without adding any code, I put a breakpoint in that event handler, hit F5, and waited. 

Sure enough, a few minutes later, the breakpoint got hit – I could indeed catch the loss of focus.  Excellent.  Now, the question was: could I get the name of the process that was gaining focus?

It turns out that this is pretty easy to do.  First, I started by adding a rich text box to the form, and then in the Form_Load event handler, I printed out the start time so I’d have an idea as to how long I’d been waiting for something to happen:

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Me.RichTextBox1.AppendText(“Starting up at “ & Now & vbCrLf)

    End Sub

 

Next thing was to figure out who was gaining focus – that would be my culprit.  The way to do this is to get the foreground window, then get its process id, then get the path of the EXE that owned that id.  I happened to know about a couple of native APIs that could help me out here, and I was able leverage the VB “Declare” syntax to get access to them:

Declare Auto Function GetForegroundWindow Lib “user32.dll” () As Integer

Declare Auto Function GetWindowThreadProcessId Lib “user32.dll” (ByVal hwnd As Integer, _

         ByRef procid As Integer) As UInteger

Now, I just call those APIs to get the process ID, and given that, I can use the .NET Process class to get information about that process, such as its path.  I call the resulting code when the form deactivates or loses focus:

    Private Sub GoingAway(ByVal sender As Object, ByVal e As System.EventArgs) _

      Handles Me.Deactivate, Me.LostFocus

        Dim hwnd As Integer = GetForegroundWindow()

        ‘ Note that process_id will be used as a ByRef argument

        ‘ and will be changed by GetWindowThreadProcessId

        Dim process_id As Integer = 1

        GetWindowThreadProcessId(hwnd, process_id)

 

        If (process_id <> 1) Then

            Dim appExePath As String = Process.GetProcessById(process_id).MainModule.FileName()

 

            Me.RichTextBox1.AppendText(“Lost focus at “ & Now & ” due to “ & appExePath & vbCrLf)

        Else

            Me.RichTextBox1.AppendText(“Lost focus due to unknown cause.”)

        End If

    End Sub

 

I got that up & running, and that was enough to finger the culprit, which was some software “phoning home” every 15 minutes (!) to see if there were any updates that could be installed.  A quick check on the Internet showed me that others had been having similar problems with the same EXE (created by a major industry player who Should Have Known Better – fortunately, not us J), and that the EXE could be safely removed with no impact to the printer.  Problem solved, crisis over – another case closed thanks to VB!

 

–Matt–*

0 comments