Come evitare un’eccezione Win32 quando si accede a Process.MainModule.FileName in C #?

Ho iniziato un nuovo progetto elencando i percorsi completi per tutti i processi in esecuzione. Quando si accede ad alcuni dei processi, il programma si blocca e genera una Win32Exception . La descrizione dice che si è verificato un errore durante l’elencazione dei moduli di processo. Inizialmente pensavo che questo problema potesse verificarsi perché lo sto eseguendo su una piattaforma a 64 bit , quindi l’ho ricompilato per i tipi di CPU x86 e AnyCPU . Sto ricevendo lo stesso errore, però.

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

L’errore si verifica nella riga n. I campi vuoti mostrano i processi in cui si è verificato l’errore: Immagine dello schermo

C’è un modo per aggirare questo messaggio di errore?

L’eccezione viene generata quando si tenta di accedere alla proprietà MainModule . La documentazione per questa proprietà non elenca Win32Exception come una ansible eccezione, ma guardando l’IL per la proprietà è evidente che l’accesso potrebbe generare questa eccezione. In generale, questa eccezione verrà generata se si sta tentando di eseguire qualcosa che è imansible o non consentito nel sistema operativo.

Win32Exception ha la proprietà NativeErrorCode e anche un Message che spiegherà qual è il problema. Dovresti usare queste informazioni per risolvere il tuo problema. NativeErrorCode è il codice di errore di Win32. Possiamo indovinare tutto il giorno qual è il problema, ma l’unico modo per capirlo è ispezionare il codice di errore.

Ma per continuare a indovinare, una fonte di queste eccezioni è l’accesso ai processi a 64 bit da un processo a 32 bit. Farlo genererà una Win32Exception con il seguente messaggio:

Un processo a 32 bit non può accedere ai moduli di un processo a 64 bit.

È ansible ottenere il numero di bit del processo valutando Environment.Is64BitProcess .

Anche in esecuzione come processo a 64 bit non sarà mai ansible accedere a MainModule del processo 4 (sistema) o processo 0 (processo inattivo del sistema). Questo genererà una Win32Exception con il messaggio:

Imansible enumerare i moduli del processo.

Se il problema è che si desidera creare un elenco di processi simile a quello in Task Manager, sarà necessario gestire i processi 0 e 4 in un modo speciale e assegnare loro nomi specifici (proprio come fa Task Manager). Si noti che nelle versioni precedenti di Windows il processo di sistema ha l’ID 8.

Per favore vedi la risposta di Jeff Mercado qui .

Ho adattato leggermente il suo codice per ottenere il percorso file di un processo specifico:

 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().FirstOrDefault(); if (mo != null) { return (string)mo["ExecutablePath"]; } } } return null; } 

Se vuoi eliminare Win32Exception e ottenere le migliori prestazioni, facciamolo:

  1. Useremo l’API Win32 per ottenere il nome del file di processo
  2. Implementeremo una cache (solo spiegazione)

Innanzitutto, è necessario importare 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); 

Secondo, scriviamo la funzione che restituisce il nome del file di processo.

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

Infine, implementiamo una cache, quindi non è necessario chiamare questa funzione troppo spesso. Creare una class ProcessCacheItem con il tempo di creazione delle proprietà (1) nome processo (2). Aggiungi un const ItemLifetime e imposta su 60 secondi. Creare un dizionario dove chiave – process PID e value è un’istanza di object ProcessCacheItem. Quando vuoi ottenere il nome del processo, prima controlla la cache. Se l’elemento nella cache è scaduto, rimuoverlo e aggiungerne uno aggiornato.

Forse perché stai provando ad accedere alla proprietà MainModule per alcuni processi (molto probabilmente quelli in esecuzione sotto le credenziali SYSTEM ) su cui non hai il permesso …