我开始了一个新项目,列出了所有正在运行的进程的完整路径。当访问某些进程时,程序崩溃并抛出 Win32Exception 。描述说明列出流程模块时发生错误。最初我认为这个问题可能会发生,因为我在 64位平台上运行它,所以我重新编译了它的CPU类型 x86 和 AnyCPU 。不过,我遇到了同样的错误。
Process p = Process.GetProcessById(2011);
string s = proc_by_id.MainModule.FileName;
错误发生在第2行。空白字段显示发生错误的进程:
有没有办法解决此错误消息?
答案 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并获得最佳性能,那就让我们这样做:
首先,您需要导入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 凭据下运行的进程)没有您的权限...