Как избежать исключения Win32 при доступе к Process.MainModule.FileName в C #? - PullRequest
36 голосов
/ 29 февраля 2012

Я запустил новый проект со списком полных путей ко всем запущенным процессам. При доступе к некоторым процессам программа вылетает и выдает Win32Exception . В описании говорится, что произошла ошибка при перечислении модулей процесса. Первоначально я думал, что эта проблема может возникнуть, потому что я запускаю ее на 64-битной платформе, поэтому я перекомпилировал ее для типов процессора x86 и AnyCPU . Но я получаю ту же ошибку.

Process p = Process.GetProcessById(2011);
string s = proc_by_id.MainModule.FileName;

Ошибка возникает в строке # 2. Пустые поля показывают процессы, где произошла ошибка: Screenshot

Есть ли способ обойти это сообщение об ошибке?

Ответы [ 5 ]

47 голосов
/ 16 марта 2012

Пожалуйста, посмотрите ответ Джеффа Меркадо здесь .

Я немного адаптировал его код, чтобы просто получить путь к файлу определенного процесса:

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;
}
22 голосов
/ 29 февраля 2012

Исключение выдается при попытке доступа к свойству MainModule. Документация для этого свойства не перечисляет Win32Exception как возможное исключение, но, глядя на IL для свойства, становится очевидно, что доступ к нему может вызвать это исключение. В целом, это исключение будет выдано, если вы пытаетесь сделать что-то, что невозможно или не разрешено в ОС.

Win32Exception имеет свойство NativeErrorCode, а также Message, которое объяснит, в чем проблема. Вы должны использовать эту информацию для устранения вашей проблемы. NativeErrorCode - код ошибки Win32. Мы можем весь день угадывать, в чем проблема, но единственный способ понять это - проверить код ошибки.

Но чтобы продолжить догадываться, одним из источников этих исключений является доступ к 64-битным процессам из 32-битного процесса. В результате вы получите Win32Exception со следующим сообщением:

32-битные процессы не могут получить доступ к модулям 64-битного процесса.

Вы можете получить число бит вашего процесса, оценив Environment.Is64BitProcess.

Даже при работе в качестве 64-битного процесса вам никогда не будет разрешен доступ к MainModule процесса 4 (Система) или процесса 0 (Процесс ожидания системы). Это выдаст Win32Exception с сообщением:

Невозможно перечислить модули процесса.

Если ваша проблема заключается в том, что вы хотите создать список процессов, похожий на тот, что в диспетчере задач, вам придется обрабатывать процессы 0 и 4 особым образом и присваивать им конкретные имена (так же, как это делает диспетчер задач). Обратите внимание, что в более старых версиях Windows системный процесс имеет идентификатор 8.

10 голосов
/ 25 января 2016

Если вы хотите избавиться от Win32Exception и получить лучшую производительность, давайте сделаем это:

  1. Мы будем использовать Win32 API для получения имени файла процесса
  2. Мы реализуемкеш (только объяснение)

Во-первых, вам нужно импортировать 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;
}

Наконец, давайте реализуем кеш, поэтому нам не нужно вызывать эту функцию слишком часто.Создайте класс ProcessCacheItem со свойствами (1), именем процесса (2), временем создания.Добавьте const ItemLifetime и установите на 60 секунд.Создайте словарь, в котором ключ - PID процесса, а значение - это экземпляр объекта ProcessCacheItem.Когда вы хотите получить имя процесса, сначала проверьте в кеше.Если срок действия элемента в кэше истек, удалите его и добавьте обновленный.

2 голосов
/ 29 февраля 2012

Возможно, потому что вы пытаетесь получить доступ к свойству MainModule для некоторых процессов (наиболее вероятно, тех, которые выполняются с учетными данными SYSTEM ), для которых у вас нет разрешения ...

0 голосов
/ 13 сентября 2018

Также можно отключить следующую опцию ...

[Project Properties] 1

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...