Как избежать исключения Win32 при доступе к Process.MainModule.FileName в C #?

Обновить

December 2018

Просмотры

30.4k раз

35

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

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

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

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

5 ответы

0

Также можно отключить следующий вариант ...

[Свойства проекта] 1

21

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

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

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

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

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

Даже работает как 64 битного процесса вы никогда не будет разрешен доступ MainModuleпроцесса 4 (System) или процесс 0 (Process System Idle). Это будет бросать Win32Exceptionс сообщением:

Невозможно перечислить технологические модули.

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

9

Если вы хотите, чтобы избавиться от 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) создания. Добавить константный ItemLifetime и установлено на 60 секунд. Создать словарь, где ключ - процесс PID и значение является объектом экземпляра ProcessCacheItem. Если вы хотите, чтобы получить имя процесса, первый чек в кэше. Если элемент в кэше истек, удалите его и добавьте обновленные один.

2

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

46

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

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

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;
}