[an error occurred while processing this directive]

This page describes a simple and accurate way of determining (getting) the name of the application (process) of a given window handle (hwnd). My example shows the method using C# (c sharp, csharp) but any .NET Framework programming language will do.

I'll use this specific example, which is motivated by my own experience and situation. I'm logging all of the open, visible windows every few minutes to get an idea of what applications are frequently used, which windows are visible, and things like that. Windows APIs provide the EnumWindows and EnumDesktopWindows functions to list all of the open windows in sorted z-order. More specifically, EnumWindows provides a list of window handles (hwnds) from which information can ge gained through additional Windows API functions like GetWindowRect and GetWindowRgnBox.

The problem is that the API function GetWindowModuleFileName oddly does not return the module file name for the given hwnd. Most of the time it either returns the foreground module name or a blank string. Drat!

Prevailing wisdom is to use enumerations of process modules and do some matching between the module handles (hModules) and window handles (hwnds) and then call GetModuleFileNameEx, but this is slow and potentially error-prone in my opinion. Slow because you're now looking at a potential O(n2) algorithm for what should be an O(n) algorithm. Error-prone because my personal experience indicates that GetModuleFileNameEx additionally returns incorrect or strange results that are hard to replicate.

Fortunately, the .NET Framework provides the Process class in System.Diagnostics, which does almost all of the work for you. Of course, I'm not certain that obtaining process information avoids an enumeration "under the hood" but it seems to be much faster to use the Process class than to use the "usual method" described above. Furthermore it has not yet failed to work for me.

The basic solution is one line of code instead of 10s of lines:

(Process.GetProcessById(win32.GetWindowProcessID(hwnd))).ProcessName

I realize that some people will balk at such a long line of code with a bunch of nested function calls, so here's the same thing in a 3-line version:

Int32 pid = win32.GetWindowProcessID(hwnd);
Process p = Process.GetProcessById(pid);
string appName = p.ProcessName;

My experience is that this works flawlessly. Note that this method does not give you the full path name, but only the application name. So if you are trying (for example) to differentiate between two different applications named myapp.exe located in two different folders, you'll have to try something else.

Here's the full version with everything you should need.

using System;
using System.Diagnostics;		// Debug, Process classes
using System.Runtime.InteropServices;	// DllImport function

class GetAppName
{
  public GetAppName(Int32 hwnd)
  {
    Int32 pid = win32.GetWindowProcessID(hwnd);
    Process p = Process.GetProcessById(pid);
    string appName = p.ProcessName;
    Debug.WriteLine(appName);
  }
}

class win32
{
  [DllImport("user32")]
  private static extern UInt32 GetWindowThreadProcessId(
    Int32 hWnd,
    out Int32 lpdwProcessId
  );
  
  public static Int32 GetWindowProcessID(Int32 hwnd)
  {
    Int32 pid = 1;
    GetWindowThreadProcessId(hwnd, out pid);
    return pid;
  }

  public win32()
  {
    
  }
}

Update: Erik Ylvisaker wrote me to indicate a potentially easier way to do this that also provides a full path name. Details of what he wrote are below. Note that I have not tried this code myself.

The Process class contains a ProcessModuleCollection listing all the modules loaded by the process. As far as I can tell, the first module loaded is always the application file. So the simplest approach to get a complete path is:

(Process.GetProcessById(win32.GetWindowProcessID(hwnd))).Modules[0].FileName

I noticed it seems to be slow.... It seems that making lots of calls to Process.Modules in this process seems to be very slow. And sometimes, for reasons I don't understand, Win32exceptions are thrown by the call. These only seem to happen if it's called a lot, and when the code breaks in the debugger with one of these exceptions, I can step right past it and the exception isn't thrown again. I don't seem to run into this if I cache what's returned from Process.Modules, although I haven't gone beyond just looking at the filename for the very first one.

[an error occurred while processing this directive]