web-dev-qa-db-fra.com

Comment éviter une exception Win32 lors de l'accès à Process.MainModule.FileName en C #?

J'ai démarré un nouveau projet avec la liste complète des chemins d'accès à tous les processus en cours. Lors de l'accès à certains processus, le programme se bloque et lance une Win32Exception . La description indique qu'une erreur s'est produite lors de la liste des modules de processus. Initialement, je pensais que ce problème pouvait se produire car je l'exécutais sur une plate-forme 64 bits . Je l'ai donc recompilé pour les types de CPU x86 et AnyCPU . Je reçois la même erreur, cependant.

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

L'erreur se produit dans la ligne # 2. Les champs vides indiquent les processus où l'erreur s'est produite: Screenshot

Existe-t-il un moyen de contourner ce message d'erreur?

36
beta

L'exception est levée lorsque vous essayez d'accéder à la propriété MainModule. La documentation de cette propriété ne répertorie pas Win32Exception en tant qu'exception possible, mais si vous examinez l'IL pour la propriété, il est évident que son accès peut générer cette exception. En général, cette exception sera levée si vous essayez de faire quelque chose d’impossible ou d’être interdit dans le système d’exploitation.

Win32Exception a la propriété NativeErrorCode et aussi une Message qui expliquera le problème. Vous devriez utiliser cette information pour résoudre votre problème. NativeErrorCode est le code d'erreur Win32. Nous pouvons deviner toute la journée quel est le problème, mais le seul moyen de le savoir est de vérifier le code d'erreur.

Mais pour continuer à deviner, l'une des sources de ces exceptions est l'accès aux processus 64 bits à partir d'un processus 32 bits. Faire cela jettera un Win32Exception avec le message suivant:

Un processus 32 bits ne peut pas accéder aux modules d'un processus 64 bits. 

Vous pouvez obtenir le nombre de bits de votre processus en évaluant Environment.Is64BitProcess.

Même en tant que processus 64 bits, vous ne serez jamais autorisé à accéder à MainModule du processus 4 (système) ou au processus 0 (processus inactif du système). Cela jettera un Win32Exception avec le message:

Impossible d'énumérer les modules de processus.

Si votre problème est que vous souhaitiez créer une liste de processus similaire à celle de Task Manager, vous devrez gérer les processus 0 et 4 de manière spéciale et leur attribuer des noms spécifiques (comme le fait le gestionnaire de tâches). Notez que sur les anciennes versions de Windows, le processus système porte l’ID 8.

22
Martin Liversage

S'il vous plaît voir la réponse de Jeff Mercado ici .

J'ai légèrement adapté son code pour obtenir le chemin du fichier d'un processus spécifique:

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

Si vous voulez vous débarrasser de Win32Exception et obtenir les meilleures performances, faisons ceci:

  1. Nous allons utiliser l'API Win32 pour obtenir le nom du fichier de processus
  2. Nous allons implémenter un cache (uniquement une explication)

Tout d'abord, vous devez importer l'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);

Deuxièmement, écrivons la fonction qui retourne le nom du fichier de processus.

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

Enfin, implémentons un cache pour ne pas avoir à appeler cette fonction trop souvent. Créez une classe ProcessCacheItem avec propriétés (1) heure de création du nom de processus (2). Ajoutez un élément ItemLifetime et définissez-le sur 60 secondes. Créez un dictionnaire où clé - processus PID et valeur constituent une instance d'objet de ProcessCacheItem. Lorsque vous voulez obtenir le nom du processus, commencez par vérifier le cache. Si l'élément en cache a expiré, supprimez-le et ajoutez-en un actualisé.

10
Evgeny Sobolev

Peut-être parce que vous essayez d'accéder à la propriété MainModule pour certains processus (très probablement ceux qui s'exécutent sousSYSTEMcredentials) sur lesquels vous n'avez pas la permission ...

2
aleroot

Peut également désactiver l'option suivante ...

[Project Properties] 1

0
JxDarkAngel