For each method in file

ForEachMethodInFile is a Visual Studio macro that lets you do custom actions for each method defined in the current file. I’ve used it in the past to generate logging code to log the start and end of each method, to generate default error handling code etc..  And today someone over at CodeProject wanted to generate a breakpoint at the start of each method. The common thread here is the performing of some custom action for each method in the current file. I figured it would be useful for a lot of people if the “for each method in file” logic is available as a separate library, and that is what is ForEachMethodInFile.

The macro project is available here. To provide a custom action, all you have to do is create a new macro project, and do

  1: Imports System
  2: Imports EnvDTE
  3: Imports EnvDTE80
  4: Imports EnvDTE90
  5: Imports System.Diagnostics
  6: Imports ForEachMethodInFile
  8: Public Module BreakOnEachMethodEntry
 10:     Public Sub Run()
 11:         ForEachMethod.DoAction(AddressOf AddBreakPoint)
 12:     End Sub
 14:     Function AddBreakPoint(ByVal method As CodeFunction)
 15:         DTE.Debugger.Breakpoints.Add(method.Name)
 16:     End Function
 18: End Module

You define a callback function that takes the COM object representing a method as a parameter, and then pass on the function to the DoAction method. Your function will be called for each method defined in the file, and the cursor will be positioned at the start of the method before the callback occurs. In this case, I want to add a breakpoint, so I call the appropriate DTE method, passing the current method’s name to tell it where to place the breakpoint.

Here’s the entire code in the ForEachMethodInFile project – it basically recursively traverses code elements in the file, and invokes the callback when it runs into a method.

  1: Imports System
  2: Imports EnvDTE
  3: Imports EnvDTE80
  4: Imports System.Diagnostics
  6: Public Module ForEachMethod
  8:     Public Sub DoAction(ByRef action As Action(Of CodeFunction))
  9:         ProcessFile(action)
 10:     End Sub
 12:     Function ProcessFile(ByRef action As Action(Of CodeFunction))
 13:         Dim selection As EnvDTE.TextSelection
 14:         Dim projectItem As ProjectItem
 15:         Dim fileCodeModel As FileCodeModel
 16:         Dim codeElement As CodeElement
 17:         Dim i As Integer
 19:         Dim currentFunction As CodeFunction
 21:         projectItem = DTE.ActiveDocument.ProjectItem
 23:         fileCodeModel = projectItem.FileCodeModel
 24:         For i = 1 To fileCodeModel.CodeElements.Count
 25:             codeElement = fileCodeModel.CodeElements.Item(i)
 26:             ProcessCodeElement(codeElement, action)
 27:         Next
 29:         ' Reformat the modified code
 30:         selection = DTE.ActiveDocument.Selection
 31:         selection.SelectAll()
 32:         selection.SmartFormat()
 33:     End Function
 35:     Sub ProcessNamespace(ByVal namespaceElement As CodeNamespace, ByRef action As Action(Of CodeFunction))
 36:         Dim i As Integer
 37:         Dim codeElement As CodeElement
 39:         For i = 1 To namespaceElement.Members.Count
 40:             codeElement = namespaceElement.Members.Item(i)
 41:             ProcessCodeElement(codeElement, action)
 42:         Next
 43:     End Sub
 45:     Sub ProcessCodeElement(ByVal codeElement As CodeElement, ByRef action As Action(Of CodeFunction))
 46:         If codeElement.Kind = vsCMElement.vsCMElementNamespace Then
 47:             ProcessNamespace(codeElement, action)
 48:         ElseIf codeElement.Kind = vsCMElement.vsCMElementClass Then
 49:             ProcessType(codeElement, action)
 50:         ElseIf codeElement.Kind = vsCMElement.vsCMElementFunction Then
 51:             ProcessMethod(codeElement, action)
 52:         End If
 53:     End Sub
 55:     Sub ProcessType(ByVal typeElement As CodeClass, ByRef action As Action(Of CodeFunction))
 56:         Dim i As Integer
 57:         Dim codeElement As CodeElement
 59:         For i = 1 To typeElement.Members.Count
 60:             codeElement = typeElement.Members.Item(i)
 61:             If codeElement.Kind = vsCMElement.vsCMElementFunction Then
 62:                 ProcessMethod(codeElement, action)
 63:             ElseIf codeElement.Kind = vsCMElement.vsCMElementClass Then
 64:                 ProcessType(codeElement, action)
 65:             End If
 66:         Next
 67:     End Sub
 69:     Sub ProcessMethod(ByVal methodElement As CodeFunction, ByRef action As Action(Of CodeFunction))
 70:         Dim selection As EnvDTE.TextSelection
 71:         Dim editPoint As EnvDTE.EditPoint
 72:         Dim verifyPoint As EnvDTE.TextPoint
 73:         Dim endPointAbsCharOffset As Integer
 74:         Dim column As Integer
 75:         Dim methodRunNotifierSignature As String
 76:         Dim functionStartCode As String
 77:         Dim functionEndCode As String
 78:         Dim parameters As String
 79:         Dim parameter As EnvDTE80.CodeParameter2
 80:         Dim i As Integer
 82:         If methodElement.MustImplement Then
 83:             Return
 84:         End If
 86:         selection = DTE.ActiveDocument.Selection
 87:         editPoint = selection.ActivePoint.CreateEditPoint()
 88:         verifyPoint = selection.ActivePoint.CreateEditPoint()
 90:         ' Move to start of method
 91:         editPoint.MoveToPoint(methodElement.GetStartPoint(vsCMPart.vsCMPartBody))
 92:         selection.MoveToPoint(editPoint)
 93:         verifyPoint.MoveToPoint(methodElement.GetStartPoint(vsCMPart.vsCMPartBody))
 95:         action(methodElement)
 97:     End Sub
 98: End Module

WinMacro 2.0 beta released

WinMacro is a tiny little application that can record and replay keyboard and mouse actions that you do on your Windows desktop. It’s similar to the macro facility in Word and Excel, but works across applications.

I wrote the initial version nearly 5 years back, and it proved to be very popular, with approximately 30000 downloads and tons of email from users. Most of the emails were appreciative, and some of them touching, especially those that described how WinMacro was helping people do a better job in fields ranging from cancer research to network testing.

There were quite a few feature requests and bug reports too. WinMacro 2.0 (Beta) ( attempts to addresses some of them. The list of new features is available in the download page.

That apart, it was “interesting” to look at code I’d written 5 years ago. I found it positively revolting, to say the least. Lots of copy pasted code, no error handling and plenty of global variables and convoluted code made it scored very heavily in the WTF scale. And this was code I was rather proud of, at that time.

On the flip side, the fact that I found my old code disgusting means I’ve improved my coding skills enough to make that code look terrible. But that is still relative improvement though, it remains to be seen how much I score on the absolute WTF scale, if there is one :)