在 C# 中访问 Process.MainModule.FileName 时如何避免 Win32 异常?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9501771/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to avoid a Win32 exception when accessing Process.MainModule.FileName in C#?
提问by beta
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-bitplatform, so I recompiled it for the CPU types x86and AnyCPU. I'm getting the same error, though.
我开始了一个新项目,列出了所有正在运行的进程的完整路径。当访问某些进程时,程序崩溃并抛出Win32Exception。描述说列出流程模块时发生错误。最初我认为可能会出现这个问题,因为我在64 位平台上运行它,所以我为 CPU 类型x86和AnyCPU重新编译了它。不过,我遇到了同样的错误。
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:

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

Is there any way to get around this error message?
有什么办法可以绕过这个错误信息吗?
采纳答案by Martin Liversage
The exception is thrown when you try to access the MainModuleproperty. The documentation for this property does not list Win32Exceptionas 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.
当您尝试访问该MainModule属性时会引发异常。此属性的文档未列出Win32Exception可能的异常,但查看该属性的 IL,很明显访问它可能会引发此异常。通常,如果您尝试执行操作系统中不可能或不允许的操作,它会抛出此异常。
Win32Exceptionhas the property NativeErrorCodeand also a Messagethat will explain what the problem is. You should use that information to troubleshoot your problem. NativeErrorCodeis 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.
Win32Exception有属性NativeErrorCode,还有一个Message可以解释问题所在的属性。您应该使用该信息来解决您的问题。NativeErrorCode是 Win32 错误代码。我们可以整天猜测问题是什么,但真正解决这个问题的唯一方法是检查错误代码。
But to continue guessing, one source of these exceptions is accessing 64 bit processes from a 32 bit process. Doing that will throw a Win32Exceptionwith the following message:
但是继续猜测,这些异常的一个来源是从 32 位进程访问 64 位进程。这样做会抛出一个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 MainModuleof process 4 (System) or process 0 (System Idle Process). This will throw a Win32Exceptionwith the message:
即使作为 64 位进程运行,您也永远不会被允许访问MainModule进程 4(系统)或进程 0(系统空闲进程)。这将抛出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). Note that on older versions of Windows the system process has ID 8.
如果你的问题是你想创建一个类似于任务管理器中的进程列表,你将不得不以一种特殊的方式处理进程 0 和 4 并给它们指定特定的名称(就像任务管理器一样)。请注意,在旧版本的 Windows 上,系统进程的 ID 为 8。
回答by aleroot
Maybe because you are trying to access the MainModule property for some processes (most likely those running under SYSTEMcredentials) on which you don't have the permission ...
也许是因为您正在尝试访问某些您没有权限的进程(很可能是那些在SYSTEM凭据下运行的进程)的 MainModule 属性...
回答by Mike Fuchs
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;
}
回答by Evgeny Sobolev
If you want to get rid off Win32Exception and get the best performance, let's do this:
如果你想摆脱 Win32Exception 并获得最佳性能,让我们这样做:
- We will use Win32 API to get process file name
- We will implement a cache (only explanation)
- 我们将使用 Win32 API 来获取进程文件名
- 我们将实现一个缓存(仅解释)
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. 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.
最后,让我们实现一个缓存,这样我们就不需要太频繁地调用这个函数了。创建一个具有属性 (1) 进程名称 (2) 创建时间的类 ProcessCacheItem。添加 const ItemLifetime 并设置为 60 秒。创建一个字典,其中键 - 进程 PID 和值是 ProcessCacheItem 的对象实例。当您想获取进程名称时,首先检查缓存。如果缓存中的项目已过期,请将其删除并添加刷新的项目。

