43

I started a new project listing the full paths to all running processes. When accessing some of the processes the program crashes and throws a Win32Exception. The description says an error occured while listing the process modules. Initially I thought this problem might occur because I'm running it on a 64-bit platform, so I recompiled it for the CPU types x86 and AnyCPU. I'm getting the same error, though.

Process p = Process.GetProcessById(2011);
string s = proc_by_id.MainModule.FileName;

The error occurs in line #2. The blank fields show processes where the error occured: Screenshot

Is there any way to get around this error message?

Martin Liversage
  • 104,481
  • 22
  • 209
  • 256
beta
  • 2,583
  • 15
  • 34
  • 46
  • Without seeing code it would be hard to tell for sure but off the cuff I'd guess it's a permissions problem. – M.Babcock Feb 29 '12 at 15:22
  • Sorry about that. I just added the relevant code. The weird thing is it's working fine for about 70% of the processes but it doesn't for a few. – beta Feb 29 '12 at 15:24
  • Not sure, do you need SeDebugPrivilege? – user541686 Feb 29 '12 at 15:25
  • Does it always occur when trying to access a specific process or does it happen regardless of which process you try to get the name for? – M.Babcock Feb 29 '12 at 15:27
  • I'm not sure about requiring SeDebugPrivilege but I don't think so. @M.Babcock: It does happen when trying to access some specific processes only. – beta Feb 29 '12 at 15:34

5 Answers5

56

Please see Jeff Mercado's answer here.

I adapted his code slightly to just get the filepath of a specific process:

string s = GetMainModuleFilepath(2011);

.

private string GetMainModuleFilepath(int processId)
{
    string wmiQueryString = "SELECT ProcessId, ExecutablePath FROM Win32_Process WHERE ProcessId = " + processId;
    using (var searcher = new ManagementObjectSearcher(wmiQueryString))
    {
        using (var results = searcher.Get())
        {
            ManagementObject mo = results.Cast<ManagementObject>().FirstOrDefault();
            if (mo != null)
            {
                return (string)mo["ExecutablePath"];
            }
        }
    }
    return null;
}
Community
  • 1
  • 1
Mike Fuchs
  • 12,081
  • 6
  • 58
  • 71
  • This looks like a great solution as well, although I already got it solved some time ago. Thanks anyways! – beta Mar 16 '12 at 13:58
  • 1
    This is a good solution to avoid the Win32Exception of 32/64 bit bogus can not access messages. – Mike de Klerk Apr 25 '13 at 07:44
  • In my experiences, in order for this to return SYSTEM processes, I had to be running the executable as an Administrator. – Westy92 Jul 28 '14 at 17:52
  • 2
    Works great to avoid the issue, but is slow as hell. –  Oct 12 '14 at 23:26
  • What I did was throw this method into a utility class, then wrapped the attempt to read process.MainModule.FileName in a try/catch, resulting to using this method inside the catch. Works great. But trying to use this method all by its lonesome in something as demanding as a firewall application for example (in my case), impossible. Drags down user experience way too much. –  Oct 13 '14 at 00:51
  • I'm sure it depends on some environment conditions, like how many processes are running, but what's your measurement of how long the method takes? I just tested it and got a pretty steady 56-82 milliseconds for my setup. – Mike Fuchs Oct 13 '14 at 11:11
  • I would add a explicit `Dispose` call or `using` statement for `ManagementObject` even if it is not necessary because it has a finalizer. – Andre Kampling Dec 08 '21 at 19:06
30

The exception is thrown when you try to access the MainModule property. The documentation for this property does not list Win32Exception as a possible exception, but looking at the IL for the property it is evident that accessing it may throw this exception. In general it will throw this exception if you are trying to do something that is impossible or not allowed in the OS.

Win32Exception has the property NativeErrorCode and also a Message that will explain what the problem is. You should use that information to troubleshoot your problem. NativeErrorCode is the Win32 error code. We can guess all day long what the problem is but the only way to actually figure this out is to inspect the error code.

But to continue guessing, one source of these exceptions is accessing 64 bit processes from a 32 bit process. Doing that will throw a Win32Exception with the following message:

A 32 bit processes cannot access modules of a 64 bit process.

You can get the number of bits of your process by evaluating Environment.Is64BitProcess.

Even running as a 64 bit process you will never be allowed to access MainModule of process 4 (System) or process 0 (System Idle Process). This will throw a Win32Exception with the message:

Unable to enumerate the process modules.

If you problem is that you want to make a process listing similar to the one in Task Manager you will have to handle process 0 and 4 in a special way and give them specific names (just as Task Manager does). Note that on older versions of Windows the system process has ID 8.

Martin Liversage
  • 104,481
  • 22
  • 209
  • 256
  • Many thanks for clarifying this behaviour. It were mainly processes by 'System' and 'Idle' and I thus failed to list its main modules. The few other exceptions were some services, but I managed to find the process names by reading the output of the shell command 'tasklist.exe'. Might be a workaround solution for now but it's working fine. – beta Mar 01 '12 at 09:02
  • I'm a bit late to this party but I recently got this issue when locking my computer while my app was running. My prog watches an active app and im guessing for security reasons when you lock your computer, that call is unable to complete. Not 100% sure on that but figured id comment on it – Mungoid Sep 02 '15 at 14:54
  • Possibly another question in the making, but from the perspective of future proofing, would there be a cleaner way of handling processes 0 and 4, rather than hard coding these specific PIDs? I could always catch the exception, but it seems better to avoid the exception in the first place if possible. – Bryan Aug 23 '21 at 12:56
18

If you want to get rid off Win32Exception and get the best performance, let's do this:

  1. We will use Win32 API to get process file name
  2. We will implement a cache (only explanation)

First, you need to import Win32 API

[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);

[DllImport("psapi.dll")]
static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, [Out] StringBuilder lpBaseName, [In] [MarshalAs(UnmanagedType.U4)] int nSize);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);

Second, let's write the function that returns process file name.

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string GetProcessName(int pid)
{
      var processHandle = OpenProcess(0x0400 | 0x0010, false, pid);

      if (processHandle == IntPtr.Zero)
      {
          return null;
      }

      const int lengthSb = 4000;

      var sb = new StringBuilder(lengthSb);

      string result = null;

      if (GetModuleFileNameEx(processHandle, IntPtr.Zero, sb, lengthSb) > 0)
      {
          result = Path.GetFileName(sb.ToString());
      }

      CloseHandle(processHandle);

      return result;
}

Finally, let's implement a cache so we do not need to call this function too often. Create a class ProcessCacheItem with properties (1) process name (2) creation time. Add a const ItemLifetime and set to 60 seconds. Create a dictionary where key - process PID and value is object instance of ProcessCacheItem. When you want to get process name, first check in cache. If item in cache expired, remove it and add refreshed one.

Evgeny Sobolev
  • 515
  • 4
  • 13
  • 2
    This will still fail if the other process is running with elevated privileges. Instead of passing 0x0400 | 0x0010 to OpenProcess, pass PROCESS_QUERY_LIMITED_INFORMATION (0x1000) – Dmitry Streblechenko Feb 11 '19 at 18:01
3

Also can deactivate the next option...

[Project Properties]1

Paul Roub
  • 36,322
  • 27
  • 84
  • 93
JxDarkAngel
  • 911
  • 11
  • 4
2

Maybe because you are trying to access the MainModule property for some processes (most likely those running under SYSTEM credentials) on which you don't have the permission ...

aleroot
  • 71,077
  • 30
  • 176
  • 213