2010-09-13 5 views
0

J'ai des problèmes avec la gestion des exceptions. Plus précisément, je crée un objet System.Diagnostic.Process à partir de l'identificateur de processus (PID), puis je l'utilise pour exécuter mon algorithme. J'ai remarqué que cette classe lance une exception InvalidOperation et ArgumentException lors de l'accès à différentes propriétés, car le processus s'est déjà arrêté pendant que j'accède à l'instance de processus.Comment gérer correctement les exceptions système?

Cependant, l'algorithme utilise d'autres fonctions qui lancent les mêmes exceptions. Le code suivant est celui qui a soulevé le problème:

XmlWindow mWindow = new XmlWindow(new IntPtr(args.Message.GetContent<Int64>())); 
Process mWindowProcess = mWindow.GetProcess(); 
XmlProcessInstance mWindowProcessInstance = null; 
XmlProcessLayout pLayout = null; 

Log(mWindow); 

lock (mCoreData.ProcessList) { 
    try { 
     // Ensure matching XmlProcess 
     mCoreData.ProcessList.EnsureProcessManagement(mWindowProcess); 
    } catch (System.ComponentModel.Win32Exception eWin32Exception) { 
     sLog.WarnFormat("Unable to manage window creation ({0}, error code {1}).", eWin32Exception.Message, eWin32Exception.NativeErrorCode); 
     break; 
    } 
} 

lock (mCoreData.LayoutList) { 
    // Unmanaged process? 
    if (mCoreData.LayoutList.IsManagedProcessInstance(mWindowProcess) == false) { 
     lock (mCoreData.UnmanagedLayout) { 
      // Force process management 
      if ((mWindowProcessInstance = mCoreData.UnmanagedLayout.GetProcessInstance((uint)mWindowProcess.Id)) == null) { 
       mWindowProcessInstance = mCoreData.UnmanagedLayout.ManageProcessInstance((uint)mWindowProcess.Id, mCoreData.ProcessList); 
       sLog.DebugFormat("Layout \"{0}\" will manage explictly the process \"{1}\" ({2}).", mCoreData.UnmanagedLayout.Name, mWindowProcessInstance.ApplicationName, mWindowProcessInstance.InstanceId); 
      } 
     } 
    } else { 
     // Find the (managed) process instance 
     mWindowProcessInstance = mCoreData.LayoutList.GetProcessInstance((uint)mWindowProcess.Id); 
    } 
} 

Log(mWindowProcessInstance); 

// Ensure window match 
mWindowProcessInstance.ProcessAssociation.AssociatedItem.LearnWindowMatching(mWindow); 
// Register process instance window 
mWindowProcessInstance.LearnWindowTemplating(mWindow); 
mWindowProcessInstance.Windows.Add(mWindow); 
// Apply window template (if any) 
mWindowProcessInstance.ApplyTemplateWindow(mWindow); 

Le problème est de savoir comment gérer l'exception InvalidOperationException. Le code ci-dessus ne fonctionne pas, car l'exception peut être renvoyée par SomeFunction, en accédant à la instance de processus; Je dois gérer seulement les exceptions lancées par le mWindowProcess.

Bien sûr, je besoin d'un grand instruction try/catch, parce que l'utilisation de la variable mWindowProcess est très intense

Comment cela pourrait être résolu correctement?

Répondre

1

Utilisez deux blocs try/catch distincts. Chaque bloc gère différemment la même exception.

2

Vous pouvez utiliser deux blocs try-catch.

Process p = Process.GetProcessById(pid); 
try { 
    SomeFunction(); // could throw InvalidOperationException 
} catch (InvalidOperationException) { } catch { throw; } 
try { 
    if (p.Id == 1234) { ... } // could throw InvalidOPerationException! 
} catch (InvalidOperationException) { } catch { throw; } 
+0

Quel est le point de l'extra 'catch {throw; } '? AFAIK en omettant cela ne change pas le comportement du code. –

+1

Le point ne corrige pas le code. Il s'agit de montrer qu'il peut y avoir deux déclarations d'essai. – Zafer

1

Vous pouvez vérifier Process.HasExited avant chaque appel et déterminer ce qu'il faut faire si le processus est sorti à ce moment-là. On ne sait pas s'il existe une façon systématique de gérer cela pour votre application. Malheureusement, vous devez toujours vérifier l'exception, car le processus peut se terminer entre l'appel de requête et l'utilisation de la classe Process. Il est regrettable que InvalidOperationException ait été utilisé, car il est souvent utilisé pour indiquer un état corrompu irrécupérable.

La meilleure façon de procéder est malheureusement d'essayer de détecter tous les appels spécifiques pour lesquels vous souhaitez traiter l'erreur. Si vous voulez quitter le bloc d'utilisation plus grande, vous pouvez alors lancer votre propre exception personnalisée qui est plus représentatif de la véritable échec Une option pour nettoyer cette place (ProcessTerminatedException, par exemple.):

public static int SafeGetId(this Process process) 
    { 
     if (process == null) throw new ArgumentNullException("process"); 

     try 
     { 
      return process.Id; 
     } 
     catch (InvalidOperationException ex) 
     { 
      //Do special logic, such as wrap in a custom ProcessTerminatedException 
      throw; 
     } 
    } 

Maintenant, vous pouvez appeler SafeGetId() partout où vous accédiez précédemment à l'ID. Vous pouvez créer des wrappers supplémentaires pour d'autres méthodes/propriétés susceptibles d'échouer.

0

J'ai trouvé une réponse possible. En effet cette solution était inattendue autant évidente ...

Ceci est citation de la documentation Exception:

Exception comprend un certain nombre de propriétés qui aident à identifier l'emplacement de code, le type, le fichier d'aide, et la raison de l'exception: StackTrace, InnerException, Message, HelpLink, HResult, Source, TargetSite et Data.

Les propriétés Exception répertoriées contribuent réellement à la détection des exceptions. Dans mon cas, il est acceptable d'attraper uniquement les exceptions lancées par la classe Process.Donc, je suppose que ce code est la bonne façon de filtrer les exceptions:

try { 
    .... 
} catch (InvalidOperationException eInvalidOperationException) { 
    if (eInvalidOperationException.TargetSite.DeclaringType == typeof(System.Diagnostics.Process)) { 
     // Exception when accessing mWindowProcess 
    } else 
     throw; 
} catch (ArgumentException eArgumentException) { 
    if (eArgumentException.TargetSite.DeclaringType == typeof(System.Diagnostics.Process)) { 
     // Exception when accessing mWindowProcess 
    } else 
     throw; 
} 

Ce travail pour mon code jusqu'à ce que l'accès au code seulement une instance de processus (mWindowProcess); dans le cas où plusieurs variables de processus (non directement associées à mWindowProcess) lèvent ces exceptions, elles doivent être interceptées et utilisent le dictionnaire Exception.Data pour notifier la situation différente.

La classe Exception dispose d'un contrôle très efficace sur la reconnaissance d'exception.

+0

Cela ne fonctionnerait pas si l'exception était lancée par une classe autre que Project, telle qu'une classe d'assistance interne utilisée dans son implémentation. C'est une mauvaise idée de s'appuyer sur TargetSite pour le filtrage, car le site cible exact d'une exception est un détail d'implémentation qui peut facilement changer. –

+0

A droite, donc cela "fonctionne" parce que l'implémentation du processus est correcte. Est-ce raisonnable de supposer cela? Si c'est le cas, c'est un bon modèle try-catch-all ... – Luca