Prime Programming Proficiency, Part 2: VS.NET Macros
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.
VS.NET Extensibility Object Model for Projects
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.
Exploring the Macros IDE
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:
- I created a New Web Page template that automatically employs 25 percent of a complete Web page for a recent project, including style links, HTML table layouts, footers, and headers. (Web page inheritance will be a nice upgrade eventually.)
- With the CodeDOM, I wrote a code generator that, when provided with a connection string and table name, generates a class and data class for that table, based on a custom data-access layer pattern. (XSD is a good alternative.)
- I implemented a property code generator as a macro and added it to the toolbar. So, a user provides the field name and data type and it writes the property code for him or her, and I implemented the LineCounter to see the evolution of the project by file and lines of code.
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.
A Quick Tour of the Macros IDE
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.
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
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.