Skip to main content

VS Extensibility - It's So Easy!

(Note: This is an old post that was stuck in my backlog. I am publishing it now with the intent of writing more VS extensibility posts.)

I have a colleague who laments the desire of developers to have (and tool developers to put) ‘everything in the IDE’. Anyone who has struggled with integrated source control fussing up a solution structure can probably identify with this view to some degree. Still, anyone who has used the NUnit GUI test runner for lack of having ReSharper or TestDriven.NET has had a taste of the case for IDE integration. When building small custom tools to enhance developer productivity and make it easy to perform mechanical tasks, why not stick them in Visual Studio? It turns out, it’s pretty easy to do.

Consider a situation in which a developer has user accounts for each tenant of a multitenant application. These accounts get deactivated after a period of non-use, so they need to get reactivated in order for the developer to troubleshoot problems that manifest only for a specific tenant. The developer usually just runs a script that resets the account to active; it’s about as simple as a maintenance task could be for a dev. Still, wouldn’t it be nice to have an easy way to track down the script and execute it? And wouldn’t it be nice to make sure all the developers used a standard method of performing this reset to prevent possible anomalies? We can make a little Visual Studio tool window with a button to execute this script. The tool window can be easily located in the Visual Studio menus.

First, create a Visual Studio Integration Package project

This will bring up a wizard, which is pretty self-explanatory. In this example, we’re creating just a tool window:

From there, it’s just a matter of creating a user control to do our bidding. The control is automatically included in the integration package project and named ‘MyControl’ by default. Integration and interoperability with VS is achieved through a Microsoft.VisualStudio.Shell.Package class (named ‘{PackageName}Package’ by default) and a wrapper class (‘MyToolWindow’ by default) which inherits from Microsoft.VisualStudio.Shell.ToolWindow and exposes the control to the IDE through the Window property.

I’ll spare the boring details on the implementation of this example control. The code can be found here.

Part of what makes developing the tool window so easy is the debugging experience – running the project launches an experimental hive with our custom tool window installed! Just go to View > Other Windows and select the User Reactivation tool:

This example featured a hardcoded backend with no configuration options. In the future, I’ll explore how to outfit your extensibility projects with configuration.

Comments

Popular posts from this blog

Enabling Globalization Invariant Mode for .NET Core App on Raspberry Pi Running LibreElec

I had an app I wanted to run on my Raspberry Pi 3 running LibreElec . In LibreElec you can install the dotnet core 2.2 runtime as an addon, and in Visual Studio you can compile for ARM processors with ‘Target Runtime’ set to ‘linux-arm’ in the publish profile. So, I published to a folder from VS using that profile, and I copied the output over to my RPi which had the dotnet runtime installed. I did a simple dotnet Whatever.dll to run the app (actually in this case, it was /storage/.kodi/addons/tools.dotnet-runtime/bin/dotnet Whatever.dll because of the way the addon is installed) and was met with this error: FailFast: Couldn't find a valid ICU package installed on the system. Set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support. at System.Environment.FailFast(System.String) at System.Globalization.GlobalizationMode.GetGlobalizationInvariantMode() at System.Globalization.GlobalizationMode..cctor() at Syste

Migrating Hg Repos with hg-fast-export and Windows Subsystem for Linux

Introduction I prefer Mercurial (hg) to git . I don’t really have any reason for this preference - they both do the same thing, and the user experience for 90% of the use cases is the same. It probably comes from the conditions of the DVCS landscape when I started using these systems. Some of this may have been perception only, but it looked like this: GitHub didn’t have free private repos BitBucket did have free private repos BitBucket was very hg-friendly Joel Spolsky had an amazing tutorial that served as both a how-to for hg as well as a general intro to DVCS hg was much more Windows-friendly than git Since hg was written in python, I felt like extending it would be easier than doing so for git if I ever needed to (admittedly, this is a pretty ridiculous reason) hg felt like a more unified, “coherent” system than the very linux-y feeling git and its extensions (also pretty ridiculous) Where they differed, I liked the verbs hg used better than git’s counterparts

Serializing Anonymous Methods

I’m taking some time off from not blogging to do a little blogging. I hope this doesn’t inconvenience absolutely nobody. I was doing some [binary] serialization work recently when I came across a problem – I wanted to serialize objects with delegate fields that were populated with anonymous methods at runtime. To wit, I had types like this: public delegate void MakeMove(); public class AdrianPeterson { public int GameOneYards { get; set; } public Football Ball { get; set; } public MakeMove Move { get; set; } } Populated like this: var explicitDirections = new List<string> { "left", "right", "left" }; var ap = new AdrianPeterson(); var apName = ap.GetType().Name; ap.GameOneYards = 87; ap.Move = () => Moves.Weave(apName, explicitDirections); So, I pop a [ Serializable ] on AdrianPeterson (and Football ), and I’m set, right? Wrong. Wrong like getting away from running AP in the second half when the only receiving threat you have is bei