Scripting Visual Studio with PowerShell – great for demos
In this post I’ll show a solution for automating tasks when opening a solution in Visual Studio.
NuGet brings PowerShell
In the grand scheme of things it hasn’t been that long since NuGet first arrived, but I can barely remember what I did without it! Aside from the standard benefits, one of the things I love about it is the Package Manager Console. Yes, I’m a keyboard fan and (having assigned a shortcut key for it) I can easily add packages without taking my hands of the keyboard. But the console also gives me PowerShell inside Visual Studio!
When running inside the console, you can invoke standard PowerShell cmdlets and aliases. For example, dir or Get-ChildItem will work (and you start in the solution directory which is convenient!). NuGet also adds additional cmdlets, e.g. Install-Package, Get-Project. Added to that, you get some extra context, so $dte will give you access to the top-level DTE instance for the Visual Studio automation API. So entering $dte.Solution.FullName will give you the full path to the currently open solution.
NuGet Package Scripts
You probably know that NuGet packages can add references to assemblies and also add content files (handy for adding script files for web projects, or bootstrapper classes). NuGet packages can also run PowerShell scripts to help with the installation process, e.g. to add sections to a config file. The documentation has a section that outlines the scripts:
- Install.ps1 – runs when the package is installed
- Uninstall.ps1 – runs when the package is uninstalled (no surprise there!)
- Init.ps1 – this is the script that caught my interest as it runs every time the solution is opened
When I’m creating demos I try to be reasonably disciplined and do things like
- create scripts that restore the demo folder to a clean state
- write demo notes that include what files to open for the demo, what pages on the site to open (I demo web stuff a lot!)
- create copy of the demo solution in final form – this is great for those days when nothing seems to go right and you just want to skip to the end and still have something to show!
I had a particular session a while back that I originally co-presented with a colleague, but subsequently ended up presenting it on my own. This was a bit of a challenge as some of the demos needed a few configuration steps after loading the solution to keep the flow of the session. When it was just me presenting there was no time to do that, so I pondered how to solve this. The solution? NuGet and PowerShell scripts!
Putting the bits together
The solution was actually quite simple: create a NuGet package with an Init.ps1 that runs when the solution loads and finds and executes a known script in the solution.
I’ve not done any thorough testing of the package, but it has worked for me . Feel free to give it a go and let me know how you get on!
To get started
- install the leeksnet.VisualStudioStartupAutomation package. Either do this via the package manager UI, or type the following in the console:
- Add a _startup.ps1 script to the solution
When the solution is loaded, the _startup.ps1 will be executed, so any steps you want to happen automatically can be added here. Don’t forget that you have access to the DTE object to automate Visual Studio!
I’m not going to go into the implementation details in depth here as I think the concept is fairly simple once you know what the moving parts are. There are a couple of other things that I thought are worth mentioning.
Preventing _startup.ps1 from executing.
Despite the usefulness of the automation, occasionally you don’t want the script to run. In these cases, hold down the shift key while the solution loads. When the package’s Init.ps1 script runs it checks whether the shift key is pressed before executing the _startup.ps1 script.
I found that there were a few common things that I wanted to do in my _startup.ps1 scripts, so I added them to the package as cmdlets. The DTE object model for Visual Studio is powerful, but not necessarily friendly
- Open-SolutionFile. Takes the filename to open. Normally my demo steps specify the files to open as part of the initialisation, so this is handy.
- Open-ProjectFile. Takes project name and filename and opens the file within that project. This simply saves a step of adding the project folder into the filename.
- Close-AllDocuments. If I’m going to open the files that I want then I don’t want any other files, so I call this first
- Invoke-SolutionBuild. Build the solution!
- Start-Solution. Build and run the solution
The Open-SolutionFile and Open-ProjectFile cmdlets both return an EnvDTE.Window object, so you can invoke methods on this, e.g.
(Open-ProjectFile "MyProject" "Foo\SomeFile.cs").Selection.GotoLine(10, $false) # params are lineno,select