2009-05-08 10 views
15

Quelles que soient les autres options pouvant aboutir au même résultat (ajout de points d'arrêt manuellement), est-il possible d'ajouter un point d'arrêt dans le code source d'un projet Visual Studio?Appliquer/désactiver des points d'arrêt par programme dans Visual Studio

Tels que:

try 
{ 
    FunctionThatThrowsErrors(obj InscrutableParameters); 
} 
catch(Exception ex) 
{ 
    Log.LogTheError(ex); 
    AddBreakPointToCallingFunction(); 
} 

De cette façon, lorsque vous exécutez à déboguer la prochaine fois, il aura automatiquement des points d'arrêt à tous les points qui ont causé des problèmes au cours de la dernière course. Je ne dis pas que c'est un moyen particulièrement utile de déboguer. Je me demande simplement si la capacité est là.

+0

Il doit être, je le fais en C++ tout le temps via 'if (IsDebuggerPresent()) DebugBreak();', je l'ai dans le constructeur de plusieurs de mes classes d'exception. –

Répondre

39

Vous m'avez inspiré pour fouiner autour de cela - merci de m'avoir éveillé toute la nuit. :) Voici une façon de le faire.

Visual Studio offre un très bon support de points d'arrêt. L'une des fonctionnalités les plus intéressantes est que vous pouvez lui indiquer d'exécuter une macro Visual Studio lorsque le point d'arrêt est atteint. Ces macros ont un accès complet à l'environnement de développement, c'est-à-dire qu'elles peuvent faire tout ce que vous pouvez faire manuellement au clavier, y compris la définition d'autres points d'arrêt. Cette solution consiste à: 1) placer un try/catch de niveau supérieur dans votre programme pour intercepter toutes les exceptions, 2) placer un point d'arrêt dans le bloc catch qui exécute votre macro et 3) faire en sorte que la macro regarde l'exception pour comprendre d'où il vient, et mettre un point d'arrêt là. Lorsque vous l'exécutez dans le débogueur et qu'une exception se produit, vous avez un nouveau point d'arrêt sur la ligne de code incriminée.

Prenez cet exemple de programme:

using System; 

namespace ExceptionCallstack 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      try 
      { 
       func1(); 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine("Oops"); 
       Console.ReadKey(); 
      } 
     } 

     static void func1() 
     { 
      func2(); 
     } 

     static void func2() 
     { 
      func3(); 
     } 

     static void func3() 
     { 
      throw new Exception("Boom!"); 
     } 
    } 
} 

L'objectif est de définir un programme sur ce point d'arrêt dans throw fonc3 lorsque vous l'exécutez dans le débogueur et obtenez l'erreur. Pour ce faire, créez d'abord une nouvelle macro Visual Studio (j'ai appelé la mienne SetBreakpointOnException). Coller dans un nouveau module MyDebuggerMacros ou autre:

Imports System 
Imports EnvDTE 
Imports EnvDTE80 
Imports EnvDTE90 
Imports System.Diagnostics 
Imports System.Text.RegularExpressions 

Public Module DebuggerMacros 

    Sub SetBreakpointOnException() 

     Dim output As String = "" 

     Dim stackTrace As String = DTE.Debugger.GetExpression("e.StackTrace").Value 
     stackTrace = stackTrace.Trim(New Char() {""""c}) 
     Dim stackFrames As String() = Regex.Split(stackTrace, "\\r\\n") 

     Dim r As New Regex("^\s+at .* in (?<file>.+):line (?<line>\d+)$", RegexOptions.Multiline) 
     Dim match As Match = r.Match(stackFrames(0)) 
     Dim file As String = match.Groups("file").Value 
     Dim line As Integer = Integer.Parse(match.Groups("line").Value) 

     DTE.Debugger.Breakpoints.Add("", file, line) 

    End Sub 

End Module 

Une fois cette macro est en place, revenir au bloc catch et mettre un point d'arrêt avec F9. Cliquez ensuite avec le bouton droit sur le cercle de point d'arrêt rouge et sélectionnez "When Hit ...". Au bas de la boîte de dialogue résultante, il y a une option pour lui dire d'exécuter une macro: faites défiler la liste et choisissez votre macro. Vous devriez maintenant obtenir de nouveaux points d'arrêt lorsque votre application renverra des exceptions non gérées.

Notes et mises en garde à ce sujet:

  • Je suis pas un gourou regex, je suis sûr que quelqu'un d'autre peut concocter quelque chose de mieux.
  • Cela ne gère pas les exceptions imbriquées (propriété InnerException) - vous pouvez vous battre contre cela si vous le souhaitez. :) Vérifiez GetExpression ("e.InnerException") et recurse, peut-être.
  • Il effectue l'analyse de texte sur la chaîne StackTrace de l'excpetion, pas d'analyse de graphe d'objet plus sophistiquée (en explorant Exception.TargetSite et en utilisant la réflexion). Les mises en garde habituelles s'appliquent à la fragilité de cette approche.
  • Pour une raison quelconque, il semble mettre le point d'arrêt dans un "espace alternatif". Une fois la session de débogage initiale terminée, vous ne voyez pas le nouveau point d'arrêt dans votre code. Mais il est là si vous exécutez le programme à nouveau dans le débogueur, et des choses comme "Désactiver tous les points d'arrêt" l'affectent. Ce serait bien d'apprendre ce qui se passe, si quelqu'un a envie de trouver un moyen de nettoyer cela. Peut-être creuser dans le fichier .suo?

Espérons que cela aide!

+0

wow !!! (Je voulais taper seulement 'wow !!!' mais cela ne permettrait pas moins de 15 caractères :)) –

+0

Sweet Lordy. S'il y avait un moyen que je pourrais vous donner +10, je le ferais. – DevinB

+0

J'ai besoin de mettre un rappel dans mon téléphone pour revenir et re-voter cette réponse demain quand j'aurai de nouveaux votes. :-) – Henric

44

Vous pouvez simplement appeler System.Diagnostics.Debugger.Break().

Vous pouvez également demander à Visual Studio d'interrompre toutes les exceptions, même celles qui sont gérées, en allant dans le menu Debug->Exceptions... et en vérifiant Thrown partout où il est actuellement coché "Non-géré par l'utilisateur".

+0

Tout d'abord, (+1) c'est génial, je n'avais aucune idée que ça existait. Mais, la documentation indique que "Si aucun débogueur n'est attaché, on demande aux utilisateurs s'ils veulent attacher un débogueur." Cela signifie que cette méthode intourrait le flux d'exécution * current * si elle s'exécutait sans débogueur. Dans mon cas, je veux qu'il mette un point d'arrêt pour la prochaine fois. En outre, la question indiquait que je voulais définir le point d'arrêt dans la fonction appelante, car (vraisemblablement) la fonction appelante possède des informations contextuelles importantes qui n'ont pas été transmises. – DevinB

+0

Vous ne pouvez pas modifier le code après sa compilation. Cela signifie que ce que vous voulez faire implique d'élever votre point d'arrêt derrière un drapeau de configuration qui est toujours vérifié, et de changer la configuration de votre exception. C'est potentiellement beaucoup de configuration. Le meilleur pari dans ce cas est de casser au lancer, comme mon montage le montre. –

+7

En outre, vous pouvez vérifier Debugger.IsAttached avant d'appeler break. –

2

Je ne pense pas que vous pouvez vraiment « ajouter un point d'arrêt », mais vous pouvez charger le débogueur de suspendre l'exécution afin que vous puissiez vérifier ce qui a mal tourné, en appelant System.Diagnostics.Debugger.Break()

4

Il n'est pas vraiment réactif à votre question, mais vous pouvez provoquer la rupture du débogueur en fonction des conditions que vous avez définies à l'aide de Debug.Assert. Ainsi, au lieu de dire "la prochaine fois que j'exécuterai une fonction qui a provoqué une exception, cassez", vous pouvez ajouter des assertions à votre fonction pour les interrompre lorsque les conditions ne sont pas ce qu'elles devraient être. Après tout, il n'y a aucune garantie qu'une fonction lancera une exception cette fois simplement parce qu'elle a jeté une exception la dernière fois. :)

0

En outre, Visual Basic dispose d'un mot-clé appelé Stop qui agit essentiellement comme un point d'arrêt et interrompt l'exécution.

+1

Je crois que vous avez peut-être mal compris la question. Je veux que le programme en cours d'exécution modifie le fichier pdb, * pas * le code source. De cette façon, je pourrais l'exécuter normalement la première fois, et voir comment il gère, et ensuite je pourrais attacher un débogueur, et il y aurait des points d'arrêt déjà installés à tous les endroits qui ont échoué la dernière fois. Votre solution Stop * arrêtera toujours l'exécution. – DevinB

Questions connexes