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.
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.
PingBack from http://www.alvinashcraft.com/2012/05/15/dew-drop-may-15-2012-1327/
Have you seen studioshell from codeowls ? it's opensource automation for VS. The 5 minute demos are very impressive.
Can these commandlets be bound to a shortcut key binding?
Dan,
Sadly, there is not a way that I know of to bind these to keystrokes.
Don't get me started on the missing simple extensiblity in VS 2012. Grrr.
- John Robbins
WTF? No more macros? Because "telemetry" says no one uses them? But how many programmers turn off telemetry the first time they start using Visual Studio? (Me, for example, and many of my colleagues) And how many programmers must work in virtual machines disconnected from the Internet, where no telemetry is possible?
For creating shortcut keys you can try this: http://www.codewrecks.com/blog/index.php/2012/08/24/converting-visual-studio-macro-to-visual-studio-plugin/
Nice article.
I am writing some Cmdlet too.
How do you debug the Cmdlet w/ NuGet as the host?
is the Write-Host the only way to debug it?
Thanks
Hi Sam,
Set-PSDebug works. It's the old PowerShell 1.0 way of debugging.
- John Robbins
In your excellent book "Debugging Microsoft .NET 2.0 Applications", pages 216-219, you describe cool advanced debugging techniques with Tracepoints that can call macros. Now, in VS2012, Tracepoints can only write messages, they cannot call macros because, of course, macros do not exist any longer. And here you cannot use the Powershell trick. :(
In your post I see you mention adding break points, is there any sample you have for adding a break point and when that break point is hit to run some code and then return to executing?
I.E. Something like you use to be able to do with a macro executing when a breakpoint was hit. thanks
Hi John, I hope you're well!
Here's a quick trick I just used for basic macros that add keys into the editor. I have a whole bunch of keyboard shortcuts that insert code into the editor. (At least I used to with earlier versions of VS.) When I press Ctrl+Shift+Enter, it inserts two curly braces with a blank line between them, and positions the cursor in the middle. With no more macros, I found a standalone Windows app called autohotkey (google it). I added this macro. (Hopefully the comment mechanism will get all these characters right. If not, the docs explain it pretty well.)
^+Enter::Send {{}{enter}{}}{Up}{End}{enter}
And one benefit is now I can use this across all my editors. But important note to save pulling your hair out: Run autohotkeys with administrator privs, otherwise it won't be able to communicate with apps running as admin.