简体   繁体   English

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

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

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 .当访问某些进程时,程序崩溃并抛出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 .最初我认为可能会出现这个问题,因为我在64 位平台上运行它,所以我为 CPU 类型x86AnyCPU重新编译它。 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.错误发生在第 2 行。 The blank fields show processes where the error occured:空白字段显示发生错误的进程:截屏

Is there any way to get around this error message?有没有办法绕过这个错误信息?

Please see Jeff Mercado's answer here . 在此处查看Jeff Mercado的回答。

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. 尝试访问MainModule属性时抛出异常。 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. 此属性的文档不会将Win32Exception列为可能的异常,但是查看IL的属性很明显,访问它可能会抛出此异常。 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. Win32Exception具有属性NativeErrorCode ,还有一个Message ,可以解释问题所在。 You should use that information to troubleshoot your problem. 您应该使用该信息来解决您的问题。 NativeErrorCode is the Win32 error code. NativeErrorCode是Win32错误代码。 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. 但是要继续猜测,这些异常的一个来源是从32位进程访问64位进程。 Doing that will throw a Win32Exception with the following message: 这样做会抛出Win32Exception并带有以下消息:

A 32 bit processes cannot access modules of a 64 bit process. 32位进程无法访问64位进程的模块。

You can get the number of bits of your process by evaluating Environment.Is64BitProcess . 您可以通过评估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). 即使作为64位进程运行,也永远不会允许您访问进程4(系统)或进程0(系统空闲进程)的MainModule This will throw a Win32Exception with the message: 这将抛出Win32Exception并显示以下消息:

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). 如果您想要创建类似于任务管理器中的进程列表的进程列表,则必须以特殊方式处理进程0和4并为其指定特定名称(就像任务管理器一样)。 Note that on older versions of Windows the system process has ID 8. 请注意,在旧版本的Windows上,系统进程的ID为8。

If you want to get rid off Win32Exception and get the best performance, let's do this: 如果你想摆脱Win32Exception并获得最佳性能,让我们这样做:

  1. We will use Win32 API to get process file name 我们将使用Win32 API来获取进程文件名
  2. We will implement a cache (only explanation) 我们将实现缓存(仅解释)

First, you need to import Win32 API 首先,您需要导入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. 使用属性(1)进程名称(2)创建时创建一个ProcessCacheItem类。 Add a const ItemLifetime and set to 60 seconds. 添加const ItemLifetime并设置为60秒。 Create a dictionary where key - process PID and value is object instance of ProcessCacheItem. 创建一个字典,其中key - process PID和value是ProcessCacheItem的对象实例。 When you want to get process name, first check in cache. 如果要获取进程名称,请先检入缓存。 If item in cache expired, remove it and add refreshed one. 如果缓存中的项目已过期,请将其删除并添加刷新的项目。

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

Also can deactivate the next option... 也可以停用下一个选项...

[项目属性 ] 1 ] 1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM