Archive

Archive for the ‘Visual Studio Extensions’ Category

Strongly sign your Visual Studio extension’s 3rd party assemblies to avoid assembly-loading errors at runtime

June 12th, 2017 No comments

When trying to create a Visual Studio 2017 version of my Diff All Files Visual Studio extension, I was encountering a runtime error indicating that a module could not be loaded in one of the 3rd party libraries I was referencing (LibGit2Sharp):

System.TypeInitializationException occurred
  HResult=0x80131534
  Message=The type initializer for 'LibGit2Sharp.Core.NativeMethods' threw an exception.
  Source=LibGit2Sharp
  StackTrace:
   at LibGit2Sharp.Core.NativeMethods.git_repository_discover(GitBuf buf, FilePath start_path, Boolean across_fs, FilePath ceiling_dirs)
   at LibGit2Sharp.Core.Proxy.<>c__DisplayClass3e.<git_repository_discover>b__3d(GitBuf buf)
   at LibGit2Sharp.Core.Proxy.ConvertPath(Func`2 pathRetriever)
   at LibGit2Sharp.Core.Proxy.git_repository_discover(FilePath start_path)
   at LibGit2Sharp.Repository.Discover(String startingPath)
   at VS_DiffAllFiles.StructuresAndEnums.GitHelper.GetGitRepositoryPath(String path) in D:\dev\Git\VS.DiffAllFiles\VS.DiffAllFiles\GitHelper.cs:line 39

Inner Exception 1:
DllNotFoundException: Unable to load DLL 'git2-a5cf255': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

By using the Assembly Binding Log Viewer, a.k.a. fuslogvw.exe, and being sure to run it “as administrator” so I could modify the settings, I was able to see this error at runtime when it tried to load my extension:

LOG: Binding succeeds. Returns assembly from C:\USERS\DAN.SCHROEDER\APPDATA\LOCAL\MICROSOFT\VISUALSTUDIO\15.0_B920D444EXP\EXTENSIONS\DANSKINGDOM\DIFF ALL FILES FOR VS2017\1.0\LibGit2Sharp.dll.
LOG: Assembly is loaded in LoadFrom load context.
WRN: Multiple versions of the same assembly were loaded into one context of an application domain:
WRN: Context: LoadFrom | Domain ID: 1 | Assembly Name: LibGit2Sharp, Version=0.23.1.0, Culture=neutral, PublicKeyToken=7cbde695407f0333
WRN: Context: LoadFrom | Domain ID: 1 | Assembly Name: LibGit2Sharp, Version=0.22.0.0, Culture=neutral, PublicKeyToken=7cbde695407f0333
WRN: This might lead to runtime failures.
WRN: It is recommended that you remove the dependency on multiple versions, and change the app.config file to point to the required version of the assembly only.
WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information.

My extension was using the LibGit2Sharp v0.23.1.0 version, but here it said there was already v0.22.0.0 of that assembly loaded in the app domain. Looking at some of the other logs in the Assembly Binding Log Viewer from when Visual Studio was running, I could see that the GitHub Extension for Visual Studio that I had installed was loading the 0.22.0.0 version into the app domain before my extension had been initiated. So the problem was that Visual Studio had already loaded an older version of the assembly that my extension depended on, so my extension was using that older version instead of the intended version. The solution to this problem was for me to give a new strong name to the LibGit2Sharp.dll that I included with my extension, so that both assemblies could be loaded into the app domain without conflicting with one another. To do this, I ran the following batch script against the LibGit2Sharp.dll file in the packages directory (replacing $(SolutionDirectory) with the path to my solution):

:: Strongly sign the LibGit2Sharp.dll, as VS Extensions want strongly signed assemblies and we want to avoid runtime version conflicts.
:: http://www.codeproject.com/Tips/341645/Referenced-assembly-does-not-have-a-strong-name
cd "$(SolutionDir)packages\LibGit2Sharp.0.23.1\lib\net40\"
"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.2 Tools\ildasm.exe" /all /out=LibGit2Sharp.il LibGit2Sharp.dll
"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.2 Tools\sn.exe" -k MyLibGit2SharpKey.snk
"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\ilasm.exe" /dll /key=MyLibGit2SharpKey.snk LibGit2Sharp.il

This overwrote the LibGit2Sharp.dll file in the packages directory with a new custom-signed version. One caveat with this is you need to make sure you have the Windows SDK installed (Win 10, Win 8.1, or Win 7) and make sure all the paths to ildasm.exe and sn.exe are correct, as the version in the file path may be different depending on which version of Windows you are running. Once I had the new custom-signed .dll file, I added it to a new location source control and then removed any project references from the old assembly and added the new references to the custom-signed assembly. This process will need to be repeated any time I update to a new version of the assembly.

There may be other better solutions to this problem, but this one worked for me. Another possible solution is to use ILMerge to combine the required .dll files into a single .dll file; I haven’t tried this method myself however, so I cannot comment on if it is better/easier or not. One thing to mention about the ILMerge method however is it requires a post-build step, either to be added to a project in Visual Studio or to your build scripts. With the method I mentioned above, you only need to take additional steps when updating the version of the 3rd party assembly being used. If you know of another way around this problem, please share it in the comments.

I’m also going to give a shout out to the community in the Gitter Microsoft/extendvs channel, as they have been very helpful in helping me diagnose problems and come up with solutions while trying to port my extension to Visual Studio 2017.

For those interested, I documented the process I took to update my extension to support VS 2017, and the process required to update LibGit2Sharp to a new version in the future.

Hopefully you have found this post helpful. Happy coding!

Adding a WPF Settings Page To The Tools Options Dialog Window For Your Visual Studio Extension

April 25th, 2014 1 comment

I recently created my first Visual Studio extension, Diff All Files, which allows you to quickly compare the changes to all files in a TFS changeset, shelveset, or pending changes (Git support coming soon). One of the first challenges I faced when I started the project was where to display my extension’s settings to the user, and where to save them.  My first instinct was to create a new Menu item to launch a page with all of the settings to display, since the wizard you go through to create the project has an option to automatically add a new Menu item the Tools menu.  After some Googling though, I found the more acceptable solution is to create a new section within the Tools -> Options window for your extension, as this will also allow the user to import and export your extension’s settings.

Adding a grid or custom Windows Forms settings page

Luckily I found this Stack Overflow answer that shows a Visual Basic example of how to do this, and links to the MSDN page that also shows how to do this in C#.  The MSDN page is a great resource, and it shows you everything you need to create your settings page as either a Grid Page, or a Custom Page using Windows Forms (FYI: when it says to add a UserControl, it means a System.Windows.Forms.UserControl, not a System.Windows.Controls.UserControl).  My extension’s settings page needed to have buttons on it to perform some operations, which is something the Grid Page doesn’t support, so I had to make a Custom Page.  I first made it using Windows Forms as the page shows, but it quickly reminded me how out-dated Windows Forms is (no binding!), and my settings page would have to be a fixed width and height, rather than expanding to the size of the users Options dialog window, which I didn’t like.

Adding a custom WPF settings page

The steps to create a Custom WPF settings page are the same as for creating a Custom Windows Forms Page, except instead having your settings control inherit from System.Forms.DialogPage (steps 1 and 2 on that page), it needs to inherit from Microsoft.VisualStudio.Shell.UIElementDialogPage.  And when you create your User Control for the settings page’s UI, it will be a WPF System.Windows.Controls.UserControl.  Also, instead of overriding the Window method of the DialogPage class, you will override the Child method of the UIElementDialogPage class.

Here’s a sample of what the Settings class might look like:

using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell;

namespace VS_DiffAllFiles.Settings
{
	[ClassInterface(ClassInterfaceType.AutoDual)]
	[Guid("1D9ECCF3-5D2F-4112-9B25-264596873DC9")]	// Special guid to tell it that this is a custom Options dialog page, not the built-in grid dialog page.
	public class DiffAllFilesSettings : UIElementDialogPage, INotifyPropertyChanged
	{
		#region Notify Property Changed
		/// <summary>
		/// Inherited event from INotifyPropertyChanged.
		/// </summary>
		public event PropertyChangedEventHandler PropertyChanged;

		/// <summary>
		/// Fires the PropertyChanged event of INotifyPropertyChanged with the given property name.
		/// </summary>
		/// <param name="propertyName">The name of the property to fire the event against</param>
		public void NotifyPropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
		}
		#endregion

		/// <summary>
		/// Get / Set if new files being added to source control should be compared.
		/// </summary>
		public bool CompareNewFiles { get { return _compareNewFiles; } set { _compareNewFiles = value; NotifyPropertyChanged("CompareNewFiles"); } }
		private bool _compareNewFiles = false;

		#region Overridden Functions

		/// <summary>
		/// Gets the Windows Presentation Foundation (WPF) child element to be hosted inside the Options dialog page.
		/// </summary>
		/// <returns>The WPF child element.</returns>
		protected override System.Windows.UIElement Child
		{
			get { return new DiffAllFilesSettingsPageControl(this); }
		}

		/// <summary>
		/// Should be overridden to reset settings to their default values.
		/// </summary>
		public override void ResetSettings()
		{
			CompareNewFiles = false;
			base.ResetSettings();
		}

		#endregion
	}
}

 

And what the code-behind for the User Control might look like:

using System;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Navigation;

namespace VS_DiffAllFiles.Settings
{
	/// <summary>
	/// Interaction logic for DiffAllFilesSettingsPageControl.xaml
	/// </summary>
	public partial class DiffAllFilesSettingsPageControl : UserControl
	{
		/// <summary>
		/// A handle to the Settings instance that this control is bound to.
		/// </summary>
		private DiffAllFilesSettings _settings = null;

		public DiffAllFilesSettingsPageControl(DiffAllFilesSettings settings)
		{
			InitializeComponent();
			_settings = settings;
			this.DataContext = _settings;
		}

		private void btnRestoreDefaultSettings_Click(object sender, RoutedEventArgs e)
		{
			_settings.ResetSettings();
		}

		private void UserControl_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
		{
			// Find all TextBoxes in this control force the Text bindings to fire to make sure all changes have been saved.
			// This is required because if the user changes some text, then clicks on the Options Window's OK button, it closes 
			// the window before the TextBox's Text bindings fire, so the new value will not be saved.
			foreach (var textBox in DiffAllFilesHelper.FindVisualChildren<TextBox>(sender as UserControl))
			{
				var bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);
				if (bindingExpression != null) bindingExpression.UpdateSource();
			}
		}
	}
}

 

And here’s the corresponding xaml for the UserControl:

<UserControl x:Class="VS_DiffAllFiles.Settings.DiffAllFilesSettingsPageControl"
						 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
						 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
						 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
						 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
						 xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
						 xmlns:QC="clr-namespace:QuickConverter;assembly=QuickConverter"
						 mc:Ignorable="d" 
						 d:DesignHeight="350" d:DesignWidth="400" LostKeyboardFocus="UserControl_LostKeyboardFocus">
	<UserControl.Resources>
	</UserControl.Resources>

	<Grid>
		<StackPanel Orientation="Vertical">
			<CheckBox Content="Compare new files" IsChecked="{Binding Path=CompareNewFiles}" ToolTip="If files being added to source control should be compared." />
			<Button Content="Restore Default Settings" Click="btnRestoreDefaultSettings_Click" />
		</StackPanel>
	</Grid>
</UserControl>

You can see that I am binding the CheckBox directly to the CompareNewFiles property on the instance of my Settings class; yay, no messing around with Checked events 🙂

This is a complete, but very simple example. If you want a more detailed example that shows more controls, check out the source code for my Diff All Files extension.

A minor problem

One problem I found was that when using a TextBox on my Settings Page UserControl, if I edited text in a TextBox and then hit the OK button on the Options dialog to close the window, the new text would not actually get applied.  This was because the window would get closed before the TextBox bindings had a chance to fire; so if I instead clicked out of the TextBox before clicking the OK button, everything worked correctly.  I know you can change the binding’s UpdateSourceTrigger to PropertyChanged, but I perform some additional logic when some of my textbox text is changed, and I didn’t want that logic firing after every key press while the user typed in the TextBox.

To solve this problem I added a LostKeyboardFocus event to the UserControl, and in that event I find all TextBox controls on the UserControl and force their bindings to update.  You can see the code for this in the snippets above.  The one piece of code that’s not shown is the FindVisualChildren<TextBox> method, so here it is:

/// <summary>
/// Recursively finds the visual children of the given control.
/// </summary>
/// <typeparam name="T">The type of control to look for.</typeparam>
/// <param name="dependencyObject">The dependency object.</param>
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject dependencyObject) where T : DependencyObject
{
	if (dependencyObject != null)
	{
		for (int index = 0; index < VisualTreeHelper.GetChildrenCount(dependencyObject); index++)
		{
			DependencyObject child = VisualTreeHelper.GetChild(dependencyObject, index);
			if (child != null &amp;&amp; child is T)
			{
				yield return (T)child;
			}

			foreach (T childOfChild in FindVisualChildren<T>(child))
			{
				yield return childOfChild;
			}
		}
	}
}

 

And that’s it.  Now you know how to make a nice Settings Page for your Visual Studio extension using WPF, instead of the archaic Windows Forms.

Happy coding!

Template Solution For Deploying TFS Checkin Policies To Multiple Versions Of Visual Studio And Having Them Automatically Work From “TF.exe Checkin” Too

March 24th, 2014 No comments

Get the source code

Let’s get right to it by giving you the source code.  You can get it from the MSDN samples here.

 

Explanation of source code and adding new checkin policies

If you open the Visual Studio (VS) solution the first thing you will likely notice is that there are 5 projects.  CheckinPolicies.VS2012 simply references all of the files in CheckinPolicies.VS2013 as links (i.e. shortcut files); this is because we need to compile the CheckinPolicies.VS2012 project using TFS 2012 assemblies, and the CheckinPolicies.VS2013 project using TFS2013 assemblies, but want both projects to have all of the same checkin policies.  So the projects contain all of the same files; just a few of their references are different.  A copy of the references that are different between the two projects are stored in the project’s “Dependencies” folder; these are the Team Foundation assemblies that are specific to VS 2012 and 2013.  Having these assemblies stored in the solution allows us to still build the VS 2012 checkin policies, even if you (or a colleague) only has VS 2013 installed.

Update: To avoid having multiple CheckinPolicy.VS* projects, we could use the msbuild targets technique that P. Kelly shows on his blog. However, I believe we would still need multiple deployment projects, as described below, in order to have the checkin policies work outside of Visual Studio.

The other projects are CheckinPolicyDeployment.VS2012 and CheckinPolicyDeployment.VS2013 (both of which are VSPackage projects), and CheckinPolicyDeploymentShared.  The CheckinPolicyDeployment.VS2012/VS2013 projects will generate the VSIX files that are used to distribute the checkin policies, and CheckinPolicyDeploymentShared contains files/code that are common to both of the projects (the projects reference the files by linking to them).

Basically everything is ready to go.  Just start adding new checkin policy classes to the CheckinPolicy.VS2013 project, and then also add them to the CheckinPolicy.VS2012 project as a link.  You can add a file as a link in 2 different ways in the Solution Explorer:

  1. Right-click on the CheckinPolicies.VS2012 project and choose Add -> Existing Item…, and then navigate to the new class file that you added to the CheckinPolicy.VS2013 project.  Instead of clicking the Add button though, click the little down arrow on the side of the Add button and then choose Add As Link.
  2. Drag and drop the file from the CheckinPolicy.VS2013 project to the CheckinPolicy.VS2012 project, but while releasing the left mouse button to drop the file, hold down the Alt key; this will change the operation from adding a copy of the file to that project, to adding a shortcut file that links back to the original file.
    There is a DummyCheckinPolicy.cs file in the CheckinPolicies.VS2013 project that shows you an example of how to create a new checkin policy.  Basically you just need to create a new public, serializable class that extends the CheckinPolicyBase class.  The actual logic for your checkin policy to perform goes in the Evaluate() function. If there is a policy violation in the code that is trying to be checked in, just add a new PolicyFailure instance to the failures list with the message that you want the user to see.

      Building a new version of your checkin policies

      Once you are ready to deploy your policies, you will want to update the version number in the source.extension.vsixmanifest file in both the CheckinPolicyDeployment.VS2012 and CheckinPolicyDeployment.VS2013 projects.  Since these projects will both contain the same policies, I recommend giving them the same version number as well.  Once you have updated the version number, build the solution in Release mode.  From there you will find the new VSIX files at "CheckinPolicyDeployment.VS2012\bin\Release\TFS Checkin Policies VS2012.vsix" and "CheckinPolicyDeployment.VS2013\bin\Release\TFS Checkin Policies VS2013.vsix".  You can then distribute them to your team; I recommend setting up an internal VS Extension Gallery, but the poor-man’s solution is to just email the vsix file out to everyone on your team.

      Having the policies automatically work outside of Visual Studio

      This is already hooked up and working in the template solution, so nothing needs to be changed there, but I will explain how it works here.  A while back I blogged about how to get your Team Foundation Server (TFS) checkin polices to still work when checking code in from the command line via the “tf checkin” command; by default when installing your checkin policies via a VSIX package (the MS recommended approach) you can only get them to work in Visual Studio.  I hated that I would need to manually run the script I provided each time the checkin policies were updated, so I posted a question on Stack Overflow about how to run a script automatically after the VSIX package installs the extension.  So it turns out that you can’t do that, but what you can do is use a VSPackage instead, which still uses VSIX to deploy the extension, but then also allows us to hook into Visual Studio events to run our script when VS starts up or exits.

      Here is the VSPackage class code to hook up the events and call our UpdateCheckinPoliciesInRegistry() function:

      /// <summary>
      /// This is the class that implements the package exposed by this assembly.
      ///
      /// The minimum requirement for a class to be considered a valid package for Visual Studio
      /// is to implement the IVsPackage interface and register itself with the shell.
      /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
      /// to do it: it derives from the Package class that provides the implementation of the 
      /// IVsPackage interface and uses the registration attributes defined in the framework to 
      /// register itself and its components with the shell.
      /// </summary>
      // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is
      // a package.
      [PackageRegistration(UseManagedResourcesOnly = true)]
      // This attribute is used to register the information needed to show this package
      // in the Help/About dialog of Visual Studio.
      [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)]
      // Auto Load our assembly even when no solution is open (by using the Microsoft.VisualStudio.VSConstants.UICONTEXT_NoSolution guid).
      [ProvideAutoLoad("ADFC4E64-0397-11D1-9F4E-00A0C911004F")]
      public abstract class CheckinPolicyDeploymentPackage : Package
      {
      	private EnvDTE.DTEEvents _dteEvents;
      
      	/// <summary>
      	/// Initialization of the package; this method is called right after the package is sited, so this is the place
      	/// where you can put all the initialization code that rely on services provided by VisualStudio.
      	/// </summary>
      	protected override void Initialize()
      	{
      		base.Initialize();
      
      		var dte = (DTE2)GetService(typeof(SDTE));
      		_dteEvents = dte.Events.DTEEvents;
      		_dteEvents.OnBeginShutdown += OnBeginShutdown;
      
      		UpdateCheckinPoliciesInRegistry();
      	}
      
      	private void OnBeginShutdown()
      	{
      		_dteEvents.OnBeginShutdown -= OnBeginShutdown;
      		_dteEvents = null;
      
      		UpdateCheckinPoliciesInRegistry();
      	}
      
      	private void UpdateCheckinPoliciesInRegistry()
      	{
      		var dte = (DTE2)GetService(typeof(SDTE));
      		string visualStudioVersionNumber = dte.Version;
      		string customCheckinPolicyEntryName = "CheckinPolicies";
      
      		// Create the paths to the registry keys that contains the values to inspect.
      		string desiredRegistryKeyPath = string.Format("HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\{0}_Config\\TeamFoundation\\SourceControl\\Checkin Policies", visualStudioVersionNumber);
      		string currentRegistryKeyPath = string.Empty;
      		if (Environment.Is64BitOperatingSystem)
      			currentRegistryKeyPath = string.Format("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\VisualStudio\\{0}\\TeamFoundation\\SourceControl\\Checkin Policies", visualStudioVersionNumber);
      		else
      			currentRegistryKeyPath = string.Format("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\{0}\\TeamFoundation\\SourceControl\\Checkin Policies", visualStudioVersionNumber);
      
      		// Get the value that the registry should have, and the value that it currently has.
      		var desiredRegistryValue = Registry.GetValue(desiredRegistryKeyPath, customCheckinPolicyEntryName, null);
      		var currentRegistryValue = Registry.GetValue(currentRegistryKeyPath, customCheckinPolicyEntryName, null);
      
      		// If the registry value is already up to date, just exit without updating the registry.
      		if (desiredRegistryValue == null || desiredRegistryValue.Equals(currentRegistryValue))
      			return;
      
      		// Get the path to the PowerShell script to run.
      		string powerShellScriptFilePath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(CheckinPolicyDeploymentPackage)).Location),
      			"FilesFromShared", "UpdateCheckinPolicyInRegistry.ps1");
      
      		// Start a new process to execute the batch file script, which calls the PowerShell script to do the actual work.
      		var process = new Process
      		{
      			StartInfo =
      			{
      				FileName = "PowerShell",
      				Arguments = string.Format("-NoProfile -ExecutionPolicy Bypass -File \"{0}\" -VisualStudioVersion \"{1}\" -CustomCheckinPolicyEntryName \"{2}\"", powerShellScriptFilePath, visualStudioVersionNumber, customCheckinPolicyEntryName),
      
      				// Hide the PowerShell window while we run the script.
      				CreateNoWindow = true,
      				UseShellExecute = false
      			}
      		};
      		process.Start();
      	}
      }
      

      All of the attributes on the class are put there by default, except for the “[ProvideAutoLoad("ADFC4E64-0397-11D1-9F4E-00A0C911004F")]” one; this attribute is the one that actually allows the Initialize() function to get called when Visual Studio starts.  You can see in the Initialize method that we hook up an event so that the UpdateCheckinPoliciesInRegistry() function gets called when VS is closed, and we also call that function from Initialize(), which is called when VS starts up.

      You might have noticed that this class is abstract.  This is because the VS 2012 and VS 2013 classed need to have a unique ID attribute, so the actual VSPackage class just inherits from this one.  Here is what the VS 2013 one looks like:

      [Guid(GuidList.guidCheckinPolicyDeployment_VS2013PkgString)]
      public sealed class CheckinPolicyDeployment_VS2013Package : CheckinPolicyDeploymentShared.CheckinPolicyDeploymentPackage
      { }
      

      The UpdateCheckinPoliciesInRegistry() function checks to see if the appropriate registry key has been updated to allow the checkin policies to run from the “tf checkin” command prompt command.  If they have, then it simply exits, otherwise it calls a PowerShell script to set the keys for us.  A PowerShell script is used because modifying the registry requires admin permissions, and we can easily run a new PowerShell process as admin (assuming the logged in user is an admin on their local machine, which is the case for everyone in our company).

      The one variable to note here is the customCheckinPolicyEntryName. This corresponds to the registry key name that I’ve specified in the RegistryKeyToAdd.pkgdef file, so if you change it be sure to change it in both places.  This is what the RegistryKeyToAdd.pkgdef file contains:

      // We use "\..\" in the value because the projects that include this file place it in a "FilesFromShared" folder, and we want it to look for the dll in the root directory.
      [$RootKey$\TeamFoundation\SourceControl\Checkin Policies]
      "CheckinPolicies"="$PackageFolder$\..\CheckinPolicies.dll"
      

      And here are the contents of the UpdateCheckinPolicyInRegistry.ps1 PowerShell file.  This is basically just a refactored version of the script I posted on my old blog post:

      # This script copies the required registry value so that the checkin policies will work when doing a TFS checkin from the command line.
      param
      (
      	[parameter(Mandatory=$true,HelpMessage="The version of Visual Studio to update in the registry (i.e. '11.0' for VS 2012, '12.0' for VS 2013)")]
      	[string]$VisualStudioVersion,
      
      	[parameter(HelpMessage="The name of the Custom Checkin Policy Entry in the Registry Key.")]
      	[string]$CustomCheckinPolicyEntryName = 'CheckinPolicies'
      )
      
      # Turn on Strict Mode to help catch syntax-related errors.
      # 	This must come after a script's/function's param section.
      # 	Forces a function to be the first non-comment code to appear in a PowerShell Module.
      Set-StrictMode -Version Latest
      
      $ScriptBlock = {
      	function UpdateCheckinPolicyInRegistry([parameter(Mandatory=$true)][string]$VisualStudioVersion, [string]$CustomCheckinPolicyEntryName)
      	{
      		$status = 'Updating registry to allow checkin policies to work outside of Visual Studio version ' + $VisualStudioVersion + '.'
      		Write-Output $status
      
      		# Get the Registry Key Entry that holds the path to the Custom Checkin Policy Assembly.
      		$HKCUKey = 'HKCU:\Software\Microsoft\VisualStudio\' + $VisualStudioVersion + '_Config\TeamFoundation\SourceControl\Checkin Policies'
      		$CustomCheckinPolicyRegistryEntry = Get-ItemProperty -Path $HKCUKey -Name $CustomCheckinPolicyEntryName
      		$CustomCheckinPolicyEntryValue = $CustomCheckinPolicyRegistryEntry.($CustomCheckinPolicyEntryName)
      
      		# Create a new Registry Key Entry for the iQ Checkin Policy Assembly so they will work from the command line (as well as from Visual Studio).
      		if ([Environment]::Is64BitOperatingSystem)
      		{ $HKLMKey = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\' + $VisualStudioVersion + '\TeamFoundation\SourceControl\Checkin Policies' }
      		else
      		{ $HKLMKey = 'HKLM:\SOFTWARE\Microsoft\VisualStudio\' + $VisualStudioVersion + '\TeamFoundation\SourceControl\Checkin Policies' }
      		Set-ItemProperty -Path $HKLMKey -Name $CustomCheckinPolicyEntryName -Value $CustomCheckinPolicyEntryValue
      	}
      }
      
      # Run the script block as admin so it has permissions to modify the registry.
      Start-Process -FilePath PowerShell -Verb RunAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass -Command &amp; {$ScriptBlock UpdateCheckinPolicyInRegistry -VisualStudioVersion ""$VisualStudioVersion"" -CustomCheckinPolicyEntryName ""$CustomCheckinPolicyEntryName""}"
      

      While I could have just used a much smaller PowerShell script that simply set a given registry key to a given value, I chose to have some code duplication between the C# code and this script so that this script can still be used as a stand-alone script if needed.

      The slight downside to using a VSPackage is that this script still won’t get called until the user closes or opens a new instance of Visual Studio, so the checkin policies won’t work immediately from the “tf checkin” command after updating the checkin policies extension, but this still beats having to remember to manually run the script.

       

      Conclusion

      So I’ve given you a template solution that you can use without any modification to start creating your VS 2012 and VS 2013 compatible checkin policies; Just add new class files to the CheckinPolicies.VS2013 project, and then add them to the CheckinPolicies.VS2012 project as well as links.  By using links it allows you to only have to modify checkin policy files once, and have the changes go to both the 2012 and 2013 VSIX packages.  Hopefully this template solution helps you to get your TFS checkin policies up and running faster.

      Happy Coding!

      TFS Power Tools Features And How To Run Them From Visual Studio

      July 11th, 2011 No comments

      There are a few great features in the TFS Power Tools that I wasn’t aware of, such as TreeDiff which let’s you compare folders (or even entire branches) to see the differences between them,  and Undo Unchanged which undoes files that are checked out, but don’t have any changes made to them, so that they don’t appear in the Pending Changes window.  This blog explains some of the features, and this one shows how to add the console-app-only features as External Tools in Visual Studio to easily run them from Visual Studio. Very cool!

      Making solutions with lots of projects load and build faster in Visual Studio

      June 1st, 2011 No comments

      I came across this great article which talks about simply unloading projects from a solution to make the solution load and build faster in Visual Studio.  This is great, as some of the solution files I work in contain over 300 projects and it can sometimes take a while to load.  Also, because the information about which projects to load is stored in the .suo file (not the .sln file itself), this can be configured per developer so they only have to load the projects that they work in (the .suo files should never be stored in source control).

      Now, unloading 300 projects one at a time would take forever, but luckily there the Visual Studio 2010 extension PowerCommands allows us to quickly Load or Unload all of the projects in the solution (or a solution folder) with a couple clicks.  So I typically just unload all of the projects in the solution, and then enable the ones I am working with.

      Unload Projects From Solution

      The one caveat with this is that because Visual Studio only builds the projects that are loaded, if you get your team’s latest code from source control and then try to build the solution from within visual studio, the build may fail since it will only build the projects you have loaded, and these may depend on changes made to the other projects that you don’t have loaded.  So you can easily reload all of the projects in the solution, build, and then unload all the ones you don’t want again, or what I prefer to do is simply build the solution from MSBuild, as this ignores the .suo file and builds all projects referenced in the .sln file.  I often build my solution files from MSBuild anyways since it has the added benefit of not locking up my visual studio UI while building :).

      Happy Coding!

      Automatically deploying TFS Checkin Policies to the entire team

      May 24th, 2011 No comments

      I stumbled across this great post showing how you can use the TFS Power Tools to automatically deploy your custom TFS check-in policies to everyone on your team in Visual Studio 2010.  It’s so awesome I had to share.

      Awesome VS 2010 extensions I can’t live without

      May 3rd, 2011 No comments

      With the ability to extend Visual Studio 2010, lots of great extensions have popped up.  These not only increase productivity, but make the whole coding experience much nicer.  To find and install extensions, in Visual Studio just go to Tools -> Extension Manager.  Here’s a list of my favorites:

      PowerCommands for Visual Studio 2010 – tons of features.

      Productivity Power Tools – tons of features.

      – Regex Editor – Quickly test your regular expressions to make sure they match all of your cases, and save them for future use. Also provides some common regex’s by default.  Automatically pops up as soon as you type "Regex r = new ".

      – Search Work Items for TFS 2010 – If you’re using TFS this one is a must have; it allows you to easily do a text search on all work items.

      Snippet Designer – If you create snippets, this provides a great GUI; much better than writing out all of the xml.

      Spell Checker – Simple spell checker.  Great for fixing spelling mistakes in comments.  Automatically knows to ignore variable names, etc.

      VSCommands 2010 – tons of features.

      Friendly visual studio solution names for working with multiple branches

      April 29th, 2011 6 comments

      If you have the latest version of the Visual Studio 2010 extension VSCommands you can give your solutions friendly names that display in the window’s title bar.  This is nice when you are working in different branches, so that you can differentiate which solution you are actually looking at.  I wrote the following regex to put the Branch Name after the Solution name, so for example if you have the client solution open in both Dev and Release, one will be called “Client.sln – Dev” and the other “Client.sln – Release”.

      To use this, in Visual Studio go to Tools -> Options -> VSCommands 2010-> IDE Enhancements and then paste in the following (without the quotes):

      Friendly Name: “{solutionName} – {branchName}”

      Friendly Name – Solution Path Regex: “.*\(?<branchName>.*)\(?<solutionName>.*(?:.sln))”

          WindowTitle1

      WindowTitle2

      Happy coding!

      — Update —

      Here is the new regex that I prefer to use instead now which shows the directories that the solution is sitting in:

      Friendly Name: “{solutionName} – {dir1}{dir2}{dir3}”

      Regex: “.*\(?<dir1>.*)\(?<dir2>.*)\(?<dir3>.*)\(?<solutionName>.*(.sln)Z)”

      — Update 2 for VS 2012 —

      These are the settings that I like to use for VS Commands 11 for VS 2012:

      Branch Name Regex: “.*\(?<dir1>.*)\(?<dir2>.*)\(?<branchDirectoryName>.*)\(?<solutionFileName>.*(.sln)Z)”

      Branch Name Pattern: “{branchDirectoryName} Branch”

      Git Branch Name Pattern: “{git:head} Branch”

      Main Window Title Pattern: “{solutionFileName} – {dir1}{dir2}{branchDirectoryName} ({sln:activeConfig}|{sln:activePlatform})”

      Solution Explorer Window Title Pattern: “ – {solutionFileName} • {vsc:branchName}”