How to avoid a Win32 exception when accessing Process.MainModule.FileName in C#? How to avoid a Win32 exception when accessing Process.MainModule.FileName in C#? windows windows

How to avoid a Win32 exception when accessing Process.MainModule.FileName in C#?


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;}


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.


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.