在C#中访问Process.MainModule.FileName时如何避免Win32异常?

时间:2012-02-29 15:20:36

标签: c# windows exception process

我开始了一个新项目,列出了所有正在运行的进程的完整路径。当访问某些进程时,程序崩溃并抛出 Win32Exception 。描述说明列出流程模块时发生错误。最初我认为这个问题可能会发生,因为我在 64位平台上运行它,所以我重新编译了它的CPU类型 x86 AnyCPU 。不过,我遇到了同样的错误。

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

错误发生在第2行。空白字段显示发生错误的进程: Screenshot

有没有办法解决此错误消息?

4 个答案:

答案 0 :(得分:47)

请参阅Jeff Mercado的回答here

我稍微修改了他的代码以获取特定进程的文件路径:

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

答案 1 :(得分:22)

尝试访问MainModule属性时抛出异常。此属性的文档未列出Win32Exception作为可能的异常,但查看属性的IL,很明显访问它可能会抛出此异常。通常,如果您尝试执行操作系统中不可能或不允许的操作,则会抛出此异常。

Win32Exception拥有属性NativeErrorCode以及Message,可以解释问题所在。您应该使用该信息来解决您的问题。 NativeErrorCode是Win32错误代码。我们可以整天猜测问题是什么,但实际解决这个问题的唯一方法是检查错误代码。

但是要继续猜测,这些异常的一个来源是从32位进程访问64位进程。这样做会抛出Win32Exception,并显示以下消息:

  

32位进程无法访问64位进程的模块。

您可以通过评估Environment.Is64BitProcess来获取流程的位数。

即使作为64位进程运行,也永远不会允许您访问进程4(系统)或进程0(系统空闲进程)的MainModule。这将抛出Win32Exception消息:

  

无法枚举过程模块。

如果您想要创建类似于任务管理器中的进程列表的进程列表,则必须以特殊方式处理进程0和4并为其指定特定名称(就像任务管理器一样)。请注意,在旧版本的Windows上,系统进程具有ID 8。

答案 2 :(得分:10)

如果你想摆脱Win32Exception并获得最佳性能,那就让我们这样做:

  1. 我们将使用Win32 API获取进程文件名
  2. 我们将实施缓存(仅解释)
  3. 首先,您需要导入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);
    

    其次,让我们编写返回进程文件名的函数。

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

    最后,让我们实现一个缓存,这样我们就不需要经常调用这个函数了。使用属性(1)进程名称(2)创建时创建一个ProcessCacheItem类。添加const ItemLifetime并设置为60秒。创建一个字典,其中key - process PID和value是ProcessCacheItem的对象实例。如果要获取进程名称,请先检入缓存。如果缓存中的项目已过期,请将其删除并添加刷新的项目。

答案 3 :(得分:2)

也许是因为您试图访问MainModule属性,因为某些进程(很可能是那些在 SYSTEM 凭据下运行的进程)没有您的权限...