http://www.developer.com/net/vb/article.php/3387571/Prime-Programming-Proficiency-Part-2-VSNET-Macros.htm
As soon as the first article of this Prime Programming Proficiency series hit Developer.com, I received an e-mail response about the value of lines of code as a metric. I love the dynamic nature of the Web and the opportunity to exchange ideas with people all over the world. That said, Part 1 did not advocate lines of code as the best or only metric. It is part of a mosaic of information that should be collected as a means to an end—programming proficiency. Lines of code are a relatively easy metric to obtain, and used prudently, it can tell you whether your code is growing or shrinking. Growing code could indicate more features, but it could just as easily indicate useless code bloat. Shrinking code could mean that features were removed or that refactoring is occurring. So, you see that counting lines of code creates more questions than it answers, but it is part of an information-gathering process that can be a first step toward obtaining the answers. This second article of the series introduces the VS.NET Macros IDE and gets you started on implementing the LineCounter tool for VS.NET. Parts 3 and 4 will provide the complete code listing and discuss the pattern used to implement the solution, the Visitor behavior pattern. Visual Studio .NET has a comprehensive extensibility object model. Writing macros in VB.NET is a convenient way to access this part of VS.NET. (See the help documentation VSLangProj Hierarchy Chart for specifics on the extensibility object mode for projects.) By using this object model in conjunction with macros, you or third-party tools vendors can write simple or advanced code generators or automate a wide variety of tasks to extend and customize VS.NET. In fact, by combining macros, wizards, the CodeDOM, project templates, and the extensibility object model, one can automate a wide variety of tasks and speed up software development significantly. (These features alone would justify having a fulltime toolsmith on your team, assuming you have more than a couple of developers.) For now, let's look at using the extensibility object model and macros to design and implement your line counter utility. As a consultant, I meet a lot of smart people. Oddly, though, few of them talk about the extensibility object model or even seem to know about macros. To give you an idea of the possibilities, consider some enhancements I recently devised: These functions are all automatic now, and they save a lot of time relative to the time it took to implement them. I hope this encourages you to explore all of .NET and share those explorations with others. (One of the best bangs for your buck is to join a users group. Another is to sign up for free e-mail newsletters such as CodeGuru.com's.) Now, I'll get back to macros. Macros are supported by their own IDE. To open the Macros IDE, select Tools|Macros|Macros IDE from Visual Studio .NET. The Macros IDE is a lot like Visual Studio's IDE and the Macro language is Visual Basic .NET, so you should feel right at home. The Macros IDE has a Project Explorer. When you expand the elements of the project being explored, you see VB modules—think shared class—and class files. These files are located in C:\Documents and Settings\[user name]\My Documents\Visual Studio\ MyMacros.vsmacros. (This is useful information if you want to share macros with others.) You can create plain vanilla modules or classes in the project and import or export classes and modules. I haven't tried to use a form in the Macros IDE, but I suspect you could. The important thing is that an entry point for macro code is a public method in a module that requires no arguments and returns either void or a subroutine with no parameters. After the macro has started, you can run any combination of methods, create classes, and generally perform any kind of programming task. Listing 1 offers a quick, introductory sample macro to get you started. Listing 1: Quick Macro Sample. The example macro DumpProject2 obtains an instance of the Projects collection from the active solution—DTE.Solution—and iterates each project, writing the project's name to the Macros IDE's Output window. Some solution-level elements do not have a FullName property because they may not be projects in the traditional sense of the word. You also can examine the Project.Kind property to determine the specific type of the element. The constants for Kind are defined in the EnvDTE.Constants namespace. Your primary problem is building a solution that performs the following tasks: The secondary problem is to accumulate totals for these same elements. (Don't you wish all project statements were so straightforward?) The final requirement is to print the element information and totals in the VS.NET Output window. Having a statement of work, you now can begin defining a solution. The first thing you might do is extend your sample code from Listing 1 and just keep adding inner, nested loops to handle sub-projects and files. I will submit, with some caution, that this will work fine—and it reflects how a lot of code is written. However, that's not how this article is going to do it. Although the statement of work guides the solution, it does not guide how you design your solution. To recap, the following is the statement of work listed as a set of imperatives (I really wish all projects were articulated this clearly. Oh, sorry, just daydreaming a bit.): Pretty easy, huh? Interestingly, most projects can be defined at this level of granularity using something as simple as an outline and word processor. Equally interesting is that very few projects have anything even remotely approaching this kind of definition statement. At this point, your problem is defined and the solution is stated as a list of unambiguous imperatives. (If one were negotiating this problem and solution statement as a fixed-price contract, all that would be left to do is have a lawyer examine the language for problematic loopholes in verbiage. I would also include a good change management clause in the contract.) Having the problem and solution statements, all you have left to do is implement and test the solution. In the final part of this series will do just that. As mentioned, you could hack out a bunch of nested loops, conditional statements, and the like, but using the Visitor behavior pattern will result in code that you can be proud to exhibit. Lines of code are a heuristic that is akin to drawing a line in the sand. Unfortunately, measuring lines of code is only the first step in a journey of a thousand miles. You must isolate newly produced code and evaluate it relative to the schedule, complexity of the problem, and qualitative aspects of the code. Failing to do so is working in ignorance of measurable productivity and quality. Part 3 of this article series will discuss the design pattern that makes the line counter utility more manageable. In addition to providing a team with a starting point for productivity improvement, all of these skills can be used collectively to solve other problems, which can improve a team's software development skill. If you want to read more about the CodeDOM, project templates, or macros (other than what I cover here or in Part 3), check out some of my other articles or books. A Google search of my name and the subject you are interested in should do the trick. Written by Paul Kimmel. Pkimmel@softconcepts.com Paul Kimmel is the VB Today columnist, has written several books on .NET programming, and is a software architect. You may contact him at Pkimmel@softconcepts.com if you need assistance or are interested in joining the Lansing Area .NET Users Group (glugnet.org).
Prime Programming Proficiency, Part 2: VS.NET Macros
July 30, 2004
VS.NET Extensibility Object Model for Projects
Exploring the Macros IDE
A Quick Tour of the Macros IDE
Option Strict Off
Option Explicit Off
Imports EnvDTE
Imports System.Diagnostics
Imports System.Windows
Imports System.Windows.Forms
Imports System
Imports System.IO
Imports System.Text.RegularExpressions
Imports System.text
Public Module MyUtilities
Public Sub DumpProject2()
Dim project As Project
Dim projects As Projects
projects = DTE.Solution.Projects
For Each project In projects
Try
Debug.WriteLine("Project: " & project.FullName)
Catch
Debug.WriteLine("Project: " & "<no name>")
End Try
Next
End Sub
End Module
Stating the Problem
Designing a Solution
Don't Work in Ignorance
Copyright © 2004. All Rights Reserved.
Prepared for codeguru.comBiography