September 2009 - Posts

Attach to what? To the process you want to debug, of course.

How many developers attach to and debug arbitrary processes running on their machines? Very few, I’d imagine. And I’d think that even those few people typically prefer Windbg or an equivalent debugger to the one supplied with Visual Studio.

Which means that aside from ASP .NET developers, almost all of us using Visual Studio attach to the application that we are working on and nothing else. To attach

1. You summon the “Attach to Process” dialog by hitting Ctrl + Alt + P. Or if you are a mouse person, you go to Debug –> Attach to Process.

2. You search for your application in the list of processes shown and select it.

3. You optionally change some settings and then hit OK.

The second step can be particularly annoying, especially if the name of your application starts with a particularly common letter that occurs in the latter half of the English alphabet (it’s a tie between ‘s’ and ‘v’ on my machine). Even otherwise, if you’ve worked on the application for a significant amount of time, the keystrokes to select it becomes part of muscle memory (down arrow, down arrow, Enter, for e.g.,), and you occasionally end up attaching to the wrong application because some other process sneaked in. Surely there must be a better way?

Enter JustAttach – a macro that does just that. It finds out the output file path of the startup project of your solution and automatically attaches to it.

The full macro code is at the end of the blog post. You can also download, unzip, open Macro Explorer (View –> Other Windows –> Macro Explorer) and select Load Macro Project to start using it right away.

Do your fingers a favor by binding the command to a VS shortcut (Tools –> Options –> Keyboard, type JustAttach in the textbox and choose a shortcut like Ctrl + Alt+ Y) ; your fingers will thank you for it :).

   1: Imports System
   2: Imports EnvDTE
   3: Imports EnvDTE80
   4: Imports EnvDTE90
   5: Imports System.Diagnostics
   6:  
   7: Public Module SenthilMacros
   8:     Public Sub JustAttach()
   9:         Dim solutionBuild As SolutionBuild = DTE.Solution.SolutionBuild
  10:         Dim startupProjectName As String = solutionBuild.StartupProjects(0)
  11:  
  12:         If String.IsNullOrEmpty(startupProjectName) Then
  13:             MsgBox("Could not attach because the startup project could not be determined", MsgBoxStyle.Critical, "Failed to Attach")
  14:             Return
  15:         End If
  16:  
  17:         Dim startupProject As Project = FindProject(startupProjectName.Trim())
  18:         Dim outputFilePath As String = FindOutputFileForProject(startupProject)
  19:  
  20:         If String.IsNullOrEmpty(outputFilePath) Then
  21:             MsgBox("Could not attach because output file path for the startup project could not be determined", MsgBoxStyle.Critical, "Failed to Attach")
  22:             Return
  23:         End If
  24:  
  25:         Attach(outputFilePath)
  26:     End Sub
  27:     Sub Attach(ByVal file As String)
  28:         Dim process As EnvDTE.Process
  29:  
  30:         For Each process In DTE.Debugger.LocalProcesses
  31:             If process.Name = file Then
  32:                 process.Attach()
  33:                 Return
  34:             End If
  35:         Next
  36:  
  37:         MsgBox("Could not attach because " + file + " is not found in the list of running processes", MsgBoxStyle.Critical, "Failed to Attach")
  38:     End Sub
  39:  
  40:     Function FindProject(ByVal projectName As String) As Project
  41:         Dim project As Project
  42:         For Each project In DTE.Solution.Projects
  43:             If project.UniqueName = projectName Then
  44:                 Return project
  45:             End If
  46:         Next
  47:     End Function
  48:     Function FindOutputFileForProject(ByVal project As Project) As String
  49:         Dim fileName As String = project.Properties.Item("OutputFileName").Value.ToString()
  50:         Dim projectPath As String = project.Properties.Item("LocalPath").Value.ToString()
  51:  
  52:         Dim config As Configuration = project.ConfigurationManager.ActiveConfiguration
  53:         Dim buildPath = config.Properties.Item("OutputPath").Value.ToString()
  54:  
  55:         If String.IsNullOrEmpty(fileName) Or String.IsNullOrEmpty(projectPath) Then
  56:             Return ""
  57:         End If
  58:  
  59:         Dim folderPath As String = System.IO.Path.Combine(projectPath, buildPath)
  60:         Return System.IO.Path.Combine(folderPath, fileName)
  61:  
  62:     End Function
  63: End Module
  64:  
Posted by Senthil | with no comments

The previous post discussed having anonymous methods as event handlers and ended with a question – why doesn’t unsubscription work while subscription works out alright?

Vivek got the answer spot on – the way the C# compiler handles and translates anonymous methods is the reason.

Here’s the code involved.

   1: public void Initialize()
   2: {
   3:     control.KeyPressed += IfEnabledThenDo(control_KeyPressed);
   4:     control.MouseMoved += IfEnabledThenDo(control_MouseMoved);
   5: }
   6:  
   7: public void Destroy()
   8: {
   9:     control.KeyPressed -= IfEnabledThenDo(control_KeyPressed);
  10:     control.MouseMoved -= IfEnabledThenDo(control_MouseMoved);
  11: }
  12:  
  13: public EventHandler<Control.ControlEventArgs> IfEnabledThenDo(EventHandler<Control.ControlEventArgs> actualAction)
  14: {
  15:     return (sender, args) => { if (args.Control.Enabled) actualAction(sender, args); };
  16: }

The compiler translates IfEnabledThenDo into this

   1: public EventHandler<Control.ControlEventArgs> IfEnabledThenDo(EventHandler<Control.ControlEventArgs> actualAction)
   2: {
   3:     <>c__DisplayClass1 CS$<>8__locals2 = new <>c__DisplayClass1();
   4:     CS$<>8__locals2.actualAction = actualAction;
   5:     return new EventHandler<Control.ControlEventArgs>(CS$<>8__locals2.<IfEnabledThenDo>b__0);
   6: }

Now the problem should be fairly obvious – every time the function is called, a new object gets created, and the event handler returned actually refers to a method (<IfEnabledThenDo>b__0) on the new instance. And that’s what breaks unsubscription. –= will not remove a delegate of a different instance of the same class from the invocation list – if it did, the consequences would not be pleasant if multiple instances of the same class subscribe to an event.

But why does the compiler translate our lambda expression this way? Raymond Chen has a great blog post explaining why, but the short answer is that it is needed to “hold” actualAction (the method parameter to IfEnabledThenDo) so that it is available when the event handler actually executes.

Now that we know why, the way to get around this issue is to cache the delegate instance returned by IfEnabledThenDo and use the same instance for subscription and unsubscription.

   1: EventHandler<Control.ControlEventArgs> keyPressed;
   2: EventHandler<Control.ControlEventArgs> mouseMoved;
   3:  
   4: public void Initialize()
   5: {
   6:    keyPressed = IfEnabledThenDo(control_KeyPressed);
   7:    mouseMoved = IfEnabledThenDo(control_MouseMoved);
   8:  
   9:    control.KeyPressed += keyPressed;
  10:    control.MouseMoved += mouseMoved;
  11: }        
  12:  
  13: public void Destroy()
  14: {
  15:    control.KeyPressed -= keyPressed;
  16:    control.MouseMoved -= mouseMoved;
  17: }

Knowing how things work under the hood has its advantages, I guess :)

 

PS : A very small syntactic change to the original example would have made the code work right away. If you’ve followed along this far, you should be able to figure out why.

   1: public void Initialize()
   2: {
   3:    actualAction = control_KeyPressed;
   4:    control.KeyPressed += IfEnabledThenDo();
   5: }        
   6:  
   7: public void Destroy()
   8: {
   9:     control.KeyPressed -= IfEnabledThenDo();
  10: }
  11:  
  12: EventHandler<Control.ControlEventArgs> actualAction;
  13: public EventHandler<Control.ControlEventArgs> IfEnabledThenDo()
  14: {
  15:     return (sender, args) => { if (args.Control.Enabled) actualAction(sender, args); };
  16: }
Posted by Senthil | 2 comment(s)
Filed under: , , ,

The syntactic sugar offered by anonymous methods makes them great candidates for writing event handlers; together with smart type inference, they reduce the amount of code written by an order of magnitude.

And that’s without considering the power offered by closures. With event handlers, closures allow you to kind of “stuff” extra parameters into the handler, without changing the actual number of formal parameters. This blog post shows a situation where an anonymous method acting as an event handler makes code simpler, and then goes on to show a gotcha with un-subscription and anonymous methods.

Here’s a simple Control class that fires a bunch of events.

   1: class Control
   2: {
   3:     public class ControlEventArgs : EventArgs
   4:     {
   5:         public Control Control {get;set;}
   6:     }
   7:  
   8:     public bool Enabled { get; set; }
   9:  
  10:     public event EventHandler<ControlEventArgs> KeyPressed;
  11:     public event EventHandler<ControlEventArgs> LeftButtonClicked;
  12:     public event EventHandler<ControlEventArgs> RightButtonClicked;
  13:     public event EventHandler<ControlEventArgs> MouseMoved;
  14: }

Let’s say you’re developing a GUI application with this class, and you want to handle events only if the originating control is visually enabled i.e., Enabled set to true. Pretty reasonable constraint, but tedious to implement, if you go the standard way of adding the check to the start of each of your event handlers.

   1: class GUIApp
   2: {
   3:     public void Initialize()
   4:     {
   5:         Control control = new Control();
   6:         control.KeyPressed += new EventHandler<Control.ControlEventArgs>(control_KeyPressed);
   7:         control.MouseMoved += new EventHandler<Control.ControlEventArgs>(control_MouseMoved);
   8:     }
   9:  
  10:     void control_MouseMoved(object sender, Control.ControlEventArgs e)
  11:     {
  12:         if (e.Control.Enabled)
  13:         {
  14:             /// 
  15:         }
  16:     }
  17:  
  18:     void control_KeyPressed(object sender, Control.ControlEventArgsEventArgs e)
  19:     {
  20:         if (e.Control.Enabled)
  21:         {
  22:             ///
  23:         }
  24:     }
  25: }

With an anonymous method, you could write a far more terse and easy to maintain version

   1: class GUIApp
   2: {
   3:     public void Initialize()
   4:     {
   5:         Control control = new Control();
   6:         control.KeyPressed += IfEnabledThenDo(control_KeyPressed);
   7:         control.MouseMoved += IfEnabledThenDo(control_MouseMoved);
   8:     }
   9:  
  10:     public EventHandler<Control.ControlEventArgs> IfEnabledThenDo(EventHandler<Control.ControlEventArgs> actualAction)
  11:     {
  12:         return (sender, args) => { if (args.Control.Enabled) { actualAction(sender, args); } };
  13:     }
  14:  
  15:     void control_MouseMoved(object sender, Control.ControlEventArgs e)
  16:     {
  17:         ///
  18:     }
  19:  
  20:     void control_KeyPressed(object sender, Control.ControlEventArgs e)
  21:     {
  22:         ///
  23:     }
  24: }

IfEnabledThenDo returns an anonymous function that first checks whether the control is enabled before calling the actual function. The code is much shorter, and the condition is checked only in one place, which makes it easy to modify or add additional logic without having to remember to change every single event handler. Plus, the reads like an English statement – subscribe to the event and if enabled, then do whatever else is necessary.

Great, but unless you are a masochist who revels in littering the code base with hard to reproduce bugs that bomb your app only when demoing to your most important customer, you must, of course, write code to unsubscribe. But there’s no method name to refer to, so you do it the same way as you did when subscribing.

   1: public void Initialize()
   2: {
   3:     control.KeyPressed += IfEnabledThenDo(control_KeyPressed);
   4:     control.MouseMoved += IfEnabledThenDo(control_MouseMoved);
   5: }
   6:  
   7: public void Destroy()
   8: {
   9:     control.KeyPressed -= IfEnabledThenDo(control_KeyPressed);
  10:     control.MouseMoved -= IfEnabledThenDo(control_MouseMoved);
  11: }

This, unfortunately, won’t work – the application will still remain subscribed to those events. Can you figure out why?

Answer and more in the next blog post.

Posted by Senthil | 3 comment(s)
Filed under: , , ,