Как избежать исключения Win32 при доступе к Process.MainModule.FileName в С#?
Я начал новый проект, перечисляя полные пути для всех запущенных процессов. При доступе к некоторым процессам программа вылетает и бросает Win32Exception. В описании указано, что во время перечисления модулей процесса произошла ошибка. Первоначально я думал, что эта проблема может возникнуть, потому что я запускаю ее на платформе 64-разрядной, поэтому я перекомпилировал ее для типов процессоров x86 и AnyCPU strong > . Однако я получаю ту же ошибку.
Process p = Process.GetProcessById(2011);
string s = proc_by_id.MainModule.FileName;
Ошибка в строке # 2. Пустые поля показывают процессы, в которых произошла ошибка:
![Screenshot]()
Есть ли способ обойти это сообщение об ошибке?
Ответы
Ответ 1
Исключение возникает при попытке получить доступ к свойству MainModule
. Документация для этого свойства не отображает Win32Exception
как возможное исключение, но, глядя на IL для свойства, очевидно, что доступ к нему может вызвать это исключение. В общем случае это будет исключение, если вы пытаетесь сделать что-то, что невозможно или не разрешено в ОС.
Win32Exception
имеет свойство NativeErrorCode
, а также a Message
, которое объяснит, в чем проблема. Вы должны использовать эту информацию для устранения неполадок. NativeErrorCode
- код ошибки Win32. Мы можем угадать весь день, что проблема, но единственный способ понять это - проверить код ошибки.
Но для продолжения угадывания одним из источников этих исключений является доступ к 64-битным процессам из 32-битного процесса. Выполнение этого вызовет Win32Exception
со следующим сообщением:
32-битные процессы не могут получить доступ к модулям 64-битного процесса.
Вы можете получить количество бит вашего процесса, оценив Environment.Is64BitProcess
.
Даже работая как 64-битный процесс, вам никогда не будет разрешен доступ к MainModule
процесса 4 (система) или процесса 0 (процесс ожидания системы). Это вызовет сообщение Win32Exception
с сообщением:
Невозможно перечислить модули процесса.
Если проблема заключается в том, что вы хотите сделать список процессов, аналогичный такому в Диспетчере задач, вам придется обрабатывать процессы 0 и 4 особым образом и давать им конкретные имена (как это делает диспетчер задач). Обратите внимание, что в старых версиях Windows системный процесс имеет ID 8.
Ответ 2
Пожалуйста, см. Jeff Mercado ответ здесь.
Я слегка адаптировал его код, чтобы просто получить путь к файлу определенного процесса:
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;
}
Ответ 3
Если вы хотите избавиться от Win32Exception и получить лучшую производительность, сделайте следующее:
- Мы будем использовать Win32 API для получения имени файла процесса
- Мы реализуем кеш (только объяснение)
Сначала вам нужно импортировать API Win32
[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;
}
Наконец, позвольте реализовать кеш, поэтому нам не нужно слишком часто вызывать эту функцию. Создайте класс ProcessCacheItem со свойствами (1) имя процесса (2) время создания. Добавьте константу ItemLifetime и установите значение 60 секунд. Создайте словарь, где ключ - процесс PID и значение - это экземпляр объекта ProcessCacheItem. Когда вы хотите получить имя процесса, сначала проверьте кеш. Если элемент в кеше истек, удалите его и добавьте обновленный.
Ответ 4
Возможно, потому, что вы пытаетесь получить доступ к свойству MainModule для некоторых процессов (скорее всего, те, которые работают под SYSTEM), на которых у вас нет разрешения...