在C#中访问Process.MainModule.FileName时如何避免Win32exception?
我开始了一个新的项目,列出所有正在运行的进程的完整path。 当访问一些进程时,程序崩溃并抛出一个Win32Exception 。 说明中列出了列出stream程模块时发生的错误。 最初我以为这个问题可能会发生,因为我在64位平台上运行它,所以我重新编译了CPUtypesx86和AnyCPU 。 不过,我也遇到了同样的错误。
Process p = Process.GetProcessById(2011); string s = proc_by_id.MainModule.FileName;
错误发生在第2行。 空白字段显示出现错误的进程:
有没有办法解决这个错误信息?
当您尝试访问MainModule
属性时引发exception。 该属性的文档没有列出Win32Exception
作为可能的exception,但是查看IL的属性显然访问它可能会引发此exception。 一般来说,如果你正在尝试做一些在操作系统中是不可能的或者不允许的事情,它会抛出这个exception。
Win32Exception
具有NativeErrorCode
属性,也是一个Message
,将解释什么是问题。 您应该使用该信息来解决您的问题。 NativeErrorCode
是Win32错误代码。 我们可以整天猜测问题是什么,但实际解决这个问题的唯一方法是检查错误代码。
但是要继续猜测,这些exception的一个来源是从32位进程访问64位进程。 这样做会抛出一个Win32Exception
以下消息:
32位进程无法访问64位进程的模块。
您可以通过评估Environment.Is64BitProcess
来获取进程的位数。
即使作为64位进程运行,也绝不允许访问进程4(系统)或进程0(系统空闲进程)的MainModule
。 这将抛出一个Win32Exception
与消息:
无法枚举进程模块。
如果你的问题是你想要一个类似于任务pipe理器的进程列表,你将不得不以特殊的方式处理进程0和4,并给他们特定的名字(就像任务pipe理器一样)。 请注意,在旧版本的Windows上,系统进程ID为8。
请看这里的杰夫·梅尔卡多的答案。
我稍微调整了自己的代码以获取特定进程的文件path:
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; }
如果你想摆脱Win32Exception,并获得最佳性能,让我们这样做:
- 我们将使用Win32 API来获取进程文件名
- 我们将实现一个caching(只有解释)
首先,您需要导入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; }
最后,让我们实现一个caching,所以我们不需要经常调用这个函数。 用属性创build一个类ProcessCacheItem(1)进程名(2)创build时间。 添加一个const ItemLifetime并设置为60秒。 创build一个字典,其中键 – 进程PID和值是ProcessCacheItem的对象实例。 当你想获取进程名时,首先检查caching。 如果caching中的项目过期,请将其删除并添加刷新的项目。
也许是因为您正在尝试访问您没有权限的某些进程(很可能是在SYSTEM凭据下运行的那些进程)的MainModule属性…