it-swarm.com.de

Wie vermeide ich eine Win32-Ausnahme beim Zugriff auf Process.MainModule.FileName in C #?

Ich habe ein neues Projekt gestartet, in dem die vollständigen Pfade zu allen laufenden Prozessen aufgelistet sind. Beim Zugriff auf einige der Prozesse stürzt das Programm ab und löst eine Win32Exception aus. Die Beschreibung besagt, dass beim Auflisten der Prozessmodule ein Fehler aufgetreten ist. Anfangs dachte ich, dieses Problem könnte auftreten, weil ich es auf einer 64-Bit -Plattform laufe, also habe ich es für die CPU-Typen x86 und AnyCPU neu kompiliert. Ich bekomme den gleichen Fehler.

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

Der Fehler tritt in Zeile 2 auf. Die leeren Felder zeigen Prozesse an, an denen der Fehler aufgetreten ist: Screenshot

Gibt es eine Möglichkeit, diese Fehlermeldung zu umgehen?

36
beta

Die Ausnahme wird ausgelöst, wenn Sie versuchen, auf die MainModule-Eigenschaft zuzugreifen. In der Dokumentation für diese Eigenschaft wird Win32Exception nicht als mögliche Ausnahme aufgeführt. Wenn Sie jedoch die IL der Eigenschaft betrachten, ist es offensichtlich, dass der Zugriff auf diese Eigenschaft diese Ausnahme auslösen kann. Im Allgemeinen wird diese Ausnahme ausgelöst, wenn Sie versuchen, etwas zu tun, das im Betriebssystem nicht möglich oder nicht zulässig ist.

Win32Exception hat die Eigenschaft NativeErrorCode und außerdem eine Message, die das Problem beschreibt. Sie sollten diese Informationen verwenden, um das Problem zu beheben. NativeErrorCode ist der Win32-Fehlercode. Wir können den ganzen Tag raten, was das Problem ist, aber die einzige Möglichkeit, dies herauszufinden, besteht darin, den Fehlercode zu überprüfen.

Um jedoch weiter zu raten, ist eine Quelle dieser Ausnahmen der Zugriff auf 64-Bit-Prozesse aus einem 32-Bit-Prozess. Dadurch wird ein Win32Exception mit der folgenden Nachricht ausgegeben:

Ein 32-Bit-Prozess kann nicht auf Module eines 64-Bit-Prozesses zugreifen. 

Sie können die Anzahl der Bits Ihres Prozesses ermitteln, indem Sie Environment.Is64BitProcess auswerten.

Selbst als 64-Bit-Prozess können Sie niemals auf MainModule von Prozess 4 (System) oder 0 (System Idle Process) zugreifen. Dadurch wird ein Win32Exception mit der Nachricht ausgegeben:

Die Prozessmodule können nicht aufgelistet werden.

Wenn Sie das Problem haben, dass Sie eine Prozessliste erstellen möchten, die der im Task-Manager ähnlich ist, müssen Sie die Prozesse 0 und 4 auf besondere Weise behandeln und ihnen bestimmte Namen geben (genau wie Task-Manager). Beachten Sie, dass der Systemprozess in älteren Windows-Versionen die ID 8 hat.

22

Siehe Jeff Mercados Antwort hier .

Ich habe seinen Code leicht angepasst, um nur den Dateipfad eines bestimmten Prozesses zu erhalten:

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;
}
46
Mike Fuchs

Wenn Sie Win32Exception loswerden und die beste Leistung erzielen möchten, können Sie Folgendes tun:

  1. Wir werden die Win32-API verwenden, um den Namen der Prozessdatei abzurufen
  2. Wir werden einen Cache implementieren (nur Erklärung)

Zunächst müssen Sie die Win32-API importieren

[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);

Zweitens schreiben wir die Funktion, die den Namen der Prozessdatei zurückgibt.

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

Schließlich implementieren wir einen Cache, damit wir diese Funktion nicht zu oft aufrufen müssen. Erstellen Sie eine Klasse ProcessCacheItem mit Erstellungszeit von Properties (1), Prozessname (2). Fügen Sie eine const ItemLifetime hinzu und setzen Sie sie auf 60 Sekunden. Erstellen Sie ein Wörterbuch, in dem die Schlüssel-Prozess-PID und der Wert die Objektinstanz von ProcessCacheItem ist. Wenn Sie den Prozessnamen abrufen möchten, checken Sie zuerst den Cache ein. Wenn ein Element im Cache abgelaufen ist, entfernen Sie es und fügen Sie ein aktualisiertes hinzu.

10
Evgeny Sobolev

Vielleicht, weil Sie versuchen, auf die MainModule-Eigenschaft für einige Prozesse zuzugreifen (höchstwahrscheinlich diejenigen, die unterSYSTEMcredentials laufen), für die Sie keine Berechtigung haben ...

2
aleroot

Kann auch die nächste Option deaktivieren ...

[Project Properties] 1

0
JxDarkAngel