When I first heard that macros were being dropped from Dev 11 I was gobsmacked. (I just love that world!) While the macro story up to Visual Studio 2010 wasn’t great because we couldn’t write macros in our .NET language of choice, I expected the situation would start getting better and was saddened when the fix was to remove the easy customizability all together.
People at Microsoft said that the code behind macros was too brittle to update and their telemetry said macros are feature no one was using. My theory is the reason people skipped macros are because you had to do it in a different language. (VB people: I’m not criticizing VB but most of the developers who would write macros are C#/C++ developers). By dropping macros, the argument was that there was still a valid way to extend the development environment with .VSX files. That’s great but like I really want to install an SDK, write a VSX and have Dev 11 debugging Dev 11 just to enumerate my projects and add a new define to debug build configurations.
Readers of this blog know I’ve written many macros (examples here) that make debugging in particular easier. I use my macros on a daily basis so was not looking forward to porting them to an extension and all the junk that entails. As I was navigating the file system using the now built in NuGet Package Manager Console window, why not use PowerShell as the macro language? While this works, I’ll still be pestering Microsoft a lot during the Dev 12 development cycle for a real macro system that lets me program in any .NET language.
To use PowerShell as the macro language, you need access to the Visual Studio automation model. In macros and extensions the root of life is the DTE interface. A quick look at the variables defined in the Package Manager Console shows a $dte variable so once you have that, you’ve got everything! That’s nice when things are easy.
My goal was to port all my macros over to PowerShell and you can see the results of my porting efforts at the bottom of this blog entry. For the most part, it was simply a matter of porting VB to PowerShell, which wasn’t too onerous if you know PowerShell. The main drawback I encountered is that to debug any functions you write you have to use the old PowerShell 1.0 way of debugging with Set-PSDebug and all the fun of command line debugging. It’s a bit painful, but it does work.
There were two areas where I got stumped during the porting. The first was in converting interfaces. When accessing the breakpoints in PowerShell, you use $dte.Debugger.Breakpoints. However, that returns the original IBreakpoints interface defined back in Visual Studio 2003. However, if you want to access the Filterby property, you need the IBreakpoints2 interface defined in Visual Studio 2005. As PowerShell’s COM implementation only works with IDispatch, and not explicit interface members, I was not sure I could get everything to work if I couldn’t convert. Fortunately, the NuGet developers already thought of this and provided the Get-Interface cmdlet that allows easy conversion. The following snippet shows converting an IBreakpoints interface into the IBreakpoints2 interface.
- Get-Interface $dte.Debugger.Breakpoints[0] ([ENVDTE80.Breakpoint2])
The second problem in porting I encountered was trying to use a Visual Studio method like $dte.Breakpoints.Add, which takes many optional and conflicting parameters. That’s easy to do in VB, but PowerShell sort of likes you to use all the parameters to a COM method. A bit of careful web searching lead me to a great solution by Jason Archer where he uses some seriously ninja PowerShell tricks to solve the problem. If you want to learn some advanced PowerShell, look at Invoke-NamedParameter function.
By using the Package Manager Console, I thought packaging up my cmdlets as a NuGet package would be the way to go. The only problem is that a NuGet package is designed to be used with a solution, which is the correct approach, but I wanted my cmdlets to be available at all times. To that end I chose to go with a PowerShell module, which works just fine with the Package Manager Console.
You can put the files in the download anywhere you want, but if you would like to include them in the module path, use Install-Module.PS1 to do the installation.
It looks like there’s a bug in the NuGet Package Manager Console because the $ENV:PSModulePath does not include the normal user’s documents directory in the default path. Consequently, to run Install-Module.PS1 you will need to run Dev 11 elevated to do the installation as the NuGet modules directory is in Program Files.
If you’re still using Visual Studio 2010 and you want to try out my cmdlets, please do as they all work provided you installed NuGet. If you have any questions or ideas for other macros, please don’t hesitate to let me know.
- TOPIC
- about_WintellectVSCmdlets
- SHORT DESCRIPTION
- Provides missing functionality, especially around debugging, to Visual Studio 2010 and Dev 11.
- LONG DESCRIPTION
- This describes the basic commands included in the WintellectVSCmdlets module. With Dev 11 not offering
- macros, simple extensions require installing an SDK and debugging the extensions with a second instance
- of the IDE. In all, it makes for a very poor experience when you want to do simple customization of the
- the development environment.
- These macros, which are very useful for debugging, demostrate that the NuGet Package Console is
- sufficient for many of your customization needs. Most of these cmdlets are ports of VB.NET macros that
- John Robbins has shown on his blog and books.
- All cmdlets work with both Visual Studio 2010 and Dev 11.
- Note that these cmdlets support C#, VB, and native C++. They probably support more but those were
- all the languages tested.
- If you have any questions, suggestions, or bug reports, please contact John at john@training.atmosera.com.
- The following Wintellect VS cmdlets are included.
- Cmdlet Description
- —————— ———————————————-
- Add-BreakpointsOnAllDocMethods Sets breakpoints on methods in the current code document. This
- is very useful in .NET languages as the debugger expression
- evaluator does not support that.
- Remove-BreakpointsOnAllDocMethods Removes all the breakpoints set with Add-BreakpointsOnAllDocMethods.
- This cmdlet will not remove any of your breakpoints.
- Add-InterestingThreadFilterToBreakpoints Adds the filter “ThreadName==InterestingThread” to all breakpoints to
- make it easier to debug through a single transaction.
- Remove-InterestingThreadFilterFromBreakpoints Removes the “ThreadName==InterestingThread” filter applied with
- Add-InterestingThreadFilterToBreakpoints.
- Disable-NonActiveThreads Freezes all but the active thread so you can single step to the end
- of a method without dramatically bouncing to another thread when you
- least expect it.
- Resume-NonActiveThreads Thaws all threads previously frozen with Disable-NonActiveThreads.
- Get-Breakpoints Returns the latest version of the IBreakpoints derived list.
- Get-Threads Returns all the threads.
- Invoke-NamedParameter A wonderful cmdlet that lets you easily call methods with many
- optional parameters. Full credit to Jason Archer for this cmdlet.
- Invoke-WinDBG VS/Dev 11 have ease of use, where WinDBG (with SOS + SOSEX) have
- tons of power to tell you what’s going on in your application. This
- cmdlet starts WinDBG on the process you’re currently debugging in the
- IDE so you can have the best of both worlds.
- Open-LastIntelliTraceRecording When you stop debugging, your current IntelliTrace log disappears. This
- cmdlet fixes that by opening the last log you produced so you can post-mortem
- look at your debugging session.
- SEE ALSO
- Online help and updates: https://training.atmosera.com/CS/blogs/jrobbins/default.aspx
- Add-BreakpointsOnAllDocMethods
- Remove-BreakpointsOnAllDocMethods
- Add-InterestingThreadFilterToBreakpoints
- Remove-InterestingThreadFilterFromBreakpoints
- Disable-NonActiveThreads
- Resume-NonActiveThreads
- Get-Breakpoints
- Get-Threads
- Invoke-NamedParameter
- Invoke-WinDBG
- Open-LastIntelliTraceRecording