Archive

Posts Tagged ‘visual studio’

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!

      Launch Visual Studio Checkin Window With A Keystroke

      September 27th, 2013 No comments

      A few weeks ago I blogged about how you can get custom TFS checkin policies to work when committing from the command line. In that post, I had a quick aside about how you can launch the checkin window (i.e. pending changes) with a quick keystroke using AutoHotkey.  I realize that many people don’t use AutoHotkey (although you really should; it can save you a lot of time), so I thought I would show how you can accomplish this task without AutoHotkey.  It’s quite simple really, and it allows you to launch the VS Checkin window from anywhere, even if you don’t have Visual Studio open.

       

      Steps To Launch VS Checkin Window From The Visual Studio Command Prompt

      1. Open the Visual Studio Command Prompt.  To do this, just hit the windows key and type Developer Command Prompt For VS2012 if using VS 2012, or Visual Studio Command Prompt (2010) if using VS 2010.
      2. In the VS Command Prompt, change to a directory that is in your TFS workspace mapping. e.g. cd C:\Dev\TFS
      3. Type tf checkin and hit enter.

        Steps To Launch VS Checkin Window With A Shortcut Key

        1. Right click on your desktop and choose New –> Shortcut to create a new shortcut file.
          CreateShortcutOnDesktop
        2. Have the shortcut point to the TF executable. This can be found at "C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\TF.exe".
          PathToTfExeForShortcut

        3. Enter the name for your shortcut file, such as VS Checkin.
          NameShortcut

        4. Now that the shortcut has been created on your desktop, right-click on it and go the the properties (or use alt+enter). You will want to:
        • add checkin to the very end of the Target,
        • change the Start In directory to a directory you have mapped in your TFS workspace,
        • and assign it a Shortcut Key. 
          CheckinShortcutPropertiesToChange

         

        Results

        That’s it.  Go ahead and try your shortcut key.  I’ll wait.  You should see something like this:

        VsCheckinWindow

        Notice that it pops both a command prompt window and the actual checkin window.  If you don’t have any pending changes, then the command prompt window will simply open and close.

         

        More Information and Caveats

        Old Style Checkin Window

        You probably noticed in the screenshot above that even though I’m using Visual Studio 2012, it still pops the old VS 2010 style of checkin window.  I actually prefer this popped out window to the VS 2012 pending changes pane, and I know a lot of people agree.

         

        Getting The Shortcut Off Your Desktop

        Above I had you create the shortcut on your desktop, but you might not want to have it clutter up your desktop. Unfortunately if you move it to some other folder you will find that the shortcut key no longer works.  For some reason I have found that for the shortcut key to work the shortcut file must either be on your desktop, or in the Start Menu. If you are using Windows 7 you can simply drag and drop the shortcut into the Programs section of the Start Menu.  For us Windows 8.0 folks the Start Menu is gone, so just manually move the shortcut to “C:\ProgramData\Microsoft\Windows\Start Menu\Programs”.

        You may find that after moving the shortcut file the shortcut key no longer works. You just have to go back into the Shortcut file properties, assign it a new Shortcut Key, hit Apply, and then assign the original Shortcut Key back.

         

        Shelving Changes In TFS Using The Old Style Shelve Window

        If you are using TFS and want to shelve your changes instead of checking them in, you can access the old Shelve Pending Changes window in the same way.  In step 4 above, instead of adding checkin as the TF.exe argument, add shelve.  To launch it from the Visual Studio Command Prompt, type tf shelve instead of tf checkin.

         

        Getting Custom TFS Checkin Policies To Work

        As you may have guessed from the very start of the article, custom TFS checkin policies don’t run in this checkin window by default; they will throw errors.  Fortunately for you I have already created a registry file that you can run after each checkin policy update that you do which will rectify this problem.

         

        I hope this helps you be more productive.  Happy coding!

        Automatically Create Your Project’s NuGet Package Every Time It Builds, Via NuGet

        June 22nd, 2013 15 comments

        So you’ve got a super awesome library/assembly that you want to share with others, but you’re too lazy to actually use NuGet to package it up and upload it to the gallery; or maybe you don’t know how to create a NuGet package and don’t have the time or desire to learn.  Well, my friends, now this can all be handled for you automatically.

        A couple weeks ago I posted about a new PowerShell script that I wrote and put up on CodePlex, called New-NuGetPackage PowerShell Script, to make creating new NuGet packages quick and easy.  Well, I’ve taken that script one step further and use it in a new NuGet package called Create New NuGet Package From Project After Each Build (real creative name, right) that you can add to your Visual Studio projects.  The NuGet package will, you guessed it, pack your project and its dependencies up into a NuGet package (i.e. .nupkg file) and place it in your project’s output directory beside the generated dll/exe file.  Now creating your own NuGet package is as easy as adding a NuGet package to your project, which if you’ve never done before is dirt simple.

        I show how to add the NuGet package to your Visual Studio project in the New-NuGetPackage PowerShell Script documentation (hint: search for “New NuGet Package” (include quotes) to find it in the VS NuGet Package Manager search results), as well as how you can push your package to the NuGet Gallery in just a few clicks.

        Here’s a couple screenshots from the documentation on installing the NuGet Package:

        NavigateToManageNugetPackages   InstallNuGetPackageFromPackageManager

        Here you can see the new PostBuildScripts folder it adds to your project, and that when you build your project, a new .nupkg file is created in the project’s Output directory alongside the dll/exe.

        FilesAddedToProject     NuGetPackageInOutputDirectory

        So now that packaging your project up in a NuGet package can be fully automated with about 30 seconds of effort, and you can push it to the NuGet Gallery in a few clicks, there is no reason for you to not share all of the awesome libraries you write.

        Happy coding!

        Using MSBuild to publish a VS 2012 SSDT .sqlproj database project the same way as a VS 2010 .dbproj database project (using command line arguments to specify the database to publish to)

        March 18th, 2013 20 comments

        Post and code updated on March 21, 2013, and again on March 22, 2013.

        We recently upgraded from VS (Visual Studio) 2010 to VS 2012, and with it had to upgrade our .dbproj database project to a .sqlproj.  When making the switch I realized that .sqlproj database projects do not support specifying the database to deploy to as MSBuild command line arguments; instead you have to pass in the path to an xml file that has the necessary information.

        So with the old .dbproj database project, you could deploy it to a database using:

        MSBuild /t:Deploy /p:TargetDatabase="[DbName]";TargetConnectionString="Data Source=[Db.Server];Integrated Security=True;Pooling=False" /p:DeployToDatabase="True" "[PathToBranch]Database\Database.dbproj"
        

        But with the new .sqlproj database project you have to do:

        MSBuild /t:Publish /p:SqlPublishProfilePath="myPublishFile.publish.xml" "[PathToBranch]Database\Database.sqlproj"
        

        Where “myPublishFile.publish.xml” contains the database server and name to publish to.

        One other minor thing to note is that it is called “deploying” the database with .dbproj, and is called “publishing” the database with .sqlproj; so when I say Deploy or Publish, I mean the same thing.

        We use TFS at my organization and while making new builds for our Test environment, we have the build process deploy the database solution to our various Test databases.  This would mean that for us I would either need to:

        1 – create a new [DbName].publish.xml file for each database, check it into source control, and update the build template to know about the new file, or

        2 – update the file contents of our myPublishFile.publish.xml file dynamically during the build to replace the Database Name and Server in the file before publishing to the database (i.e. read in file contents, replace string, write file contents back to file, publish to DB, repeat).

        Option 1 means more work every time I want to add a new Test database to publish to.  Option 2 is better, but still means having to update my TF Build template and create a new activity to read/write the new contents to the file.

        Instead, there is a 3rd option, which is to simply add the code below to the bottom of the .sqlproj file.  This will add some new MSBuild targets to the .sqlproj that will allow us to specify the database name and connection string using similar MSBuild command line parameters that we used to deploy the .dbproj project.

        The code presented here is based on this post, but the author has closed the comments section on that post and has not replied to my emails about the bugs in his code and example, so I thought I would share my modified and enhanced solution.

          <!-- 
        	Custom targets and properties added so that we can specify the database to publish to using command line parameters with VS 2012 .sqlproj projects, like we did with VS 2010 .dbproj projects.
        	This allows us to specify the MSBuild command-line parameters TargetDatabaseName, and TargetConnectionString when Publishing, and PublishToDatabase when Building.
        	I also stumbled across the undocumented parameter, PublishScriptFileName, which can be used to specify the generated sql script file name, just like DeployScriptFileName used to in VS 2010 .dbproj projects.
        	Taken from: http://blog.danskingdom.com/using-msbuild-to-publish-a-vs-2012-ssdt-sqlproj-database-project-the-same-way-as-a-vs-2010-dbproj-database-project/
          -->
          <PropertyGroup Condition="'$(TargetDatabaseName)' != '' Or '$(TargetConnectionString)' != ''">
            <PublishToDatabase Condition="'$(PublishToDatabase)' == ''">False</PublishToDatabase>
            <TargetConnectionStringXml Condition="'$(TargetConnectionString)' != ''">
              &lt;TargetConnectionString xdt:Transform="Replace"&gt;$(TargetConnectionString)&lt;/TargetConnectionString&gt;
            </TargetConnectionStringXml>
            <TargetDatabaseXml Condition="'$(TargetDatabaseName)' != ''">
              &lt;TargetDatabaseName xdt:Transform="Replace"&gt;$(TargetDatabaseName)&lt;/TargetDatabaseName&gt;
            </TargetDatabaseXml>
            <TransformPublishXml>
                &lt;Project xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"&gt;
                &lt;PropertyGroup&gt;$(TargetConnectionStringXml)$(TargetDatabaseXml)&lt;/PropertyGroup&gt;
                &lt;/Project&gt;
            </TransformPublishXml>
            <SqlPublishProfilePath Condition="'$([System.IO.Path]::IsPathRooted($(SqlPublishProfilePath)))' == 'False'">$(MSBuildProjectDirectory)\$(SqlPublishProfilePath)</SqlPublishProfilePath>
            <!-- In order to do a transform, we HAVE to change the SqlPublishProfilePath -->
            <TransformOutputFile>$(MSBuildProjectDirectory)\Transformed_$(TargetDatabaseName).publish.xml</TransformOutputFile>
            <TransformScope>$([System.IO.Path]::GetFullPath($(MSBuildProjectDirectory)))</TransformScope>
            <TransformStackTraceEnabled Condition="'$(TransformStackTraceEnabled)'==''">False</TransformStackTraceEnabled>
          </PropertyGroup>
          <Target Name="AfterBuild" Condition="'$(PublishToDatabase)'=='True'">
            <CallTarget Targets="Publish" />
          </Target>
          <UsingTask TaskName="ParameterizeTransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll" />
          <Target Name="BeforePublish" Condition="'$(TargetDatabaseName)' != '' Or '$(TargetConnectionString)' != ''">
            <Message Text="TargetDatabaseName = '$(TargetDatabaseName)', TargetConnectionString = '$(TargetConnectionString)', PublishScriptFileName = '$(PublishScriptFileName)', Transformed Sql Publish Profile Path = '$(TransformOutputFile)'" Importance="high" />
            <!-- If TargetDatabaseName or TargetConnectionString, is passed in then we use the tokenize transform to create a parameterized sql publish file -->
            <Error Condition="!Exists($(SqlPublishProfilePath))" Text="The SqlPublishProfilePath '$(SqlPublishProfilePath)' does not exist, please specify a valid file using msbuild /p:SqlPublishProfilePath='Path'" />
            <ParameterizeTransformXml Source="$(SqlPublishProfilePath)" IsSourceAFile="True" Transform="$(TransformPublishXml)" IsTransformAFile="False" Destination="$(TransformOutputFile)" IsDestinationAFile="True" Scope="$(TransformScope)" StackTrace="$(TransformStackTraceEnabled)" SourceRootPath="$(MSBuildProjectDirectory)" />
            <PropertyGroup>
              <SqlPublishProfilePath>$(TransformOutputFile)</SqlPublishProfilePath>
            </PropertyGroup>
          </Target>
        

         

        So after adding this code at the bottom of the .sqlproj file (above the </Project> tag though), you can now build and publish the database solution from the MSBuild command line using:

        MSBuild /t:Build /p:TargetDatabaseName="[DbName]";TargetConnectionString="Data Source=[Db.Server];Integrated Security=True;Pooling=False" /p:PublishToDatabase="True" /p:SqlPublishProfilePath="Template.publish.xml" "[PathToBranch]\Database\Database.sqlproj"
        

        Here you can see the 3 new parameters that we’ve added being used: TargetDatabaseName, TargetConnectionString, and PublishToDatabase.

        When the TargetDatabaseName or TargetConnectionString parameters are provided we generated a new transformed .publish.xml file, which is the same as the provided “Template.publish.xml” file, but with the database and connection string values replaced with the provided values.

        The PublishToDatabase parameter allows us to publish to the database immediately after the project is built; without this you would have to first call MSBuild to Build the database project, and then call MSBuild again to Publish it (or perhaps using “/t:Build;Publish” would work, but I didn’t test that).

        If you want to simply publish the database project without building first (generally not recommended), you can do:

        MSBuild /t:Publish /p:TargetDatabaseName="[DbName]";TargetConnectionString="Data Source=[Db.Server];Integrated Security=True;Pooling=False" /p:SqlPublishProfilePath="Template.publish.xml" "[PathToBranch]\Database\Database.sqlproj"
        

        Be careful though, since if you don’t do a Build first, any changes that have been made since the last time the .sqlproj file was built on your machine won’t be published to the database.

        Notice that I still have to provide a path to the template publish.xml file to transform, and that the path to this file is relative to the .sqlproj file (in this example the Template.publish.xml and .sqlproj files are in the same directory).  You can simply use one of the publish.xml files generated by Visual Studio, and then the TargetDatabaseName and TargetConnectionString xml element values will be replaced with those given in the command line parameters.  This allows you to still define any other publish settings as usual in the xml file.

        Also notice that the PublishToDatabase parameter is only used when doing a Build, not a Publish; providing it when doing a Publish will not hurt anything though.

        While creating my solution, I also accidentally stumbled upon what seems to be an undocumented SSDT parameter, PublishScriptFileName.  While the DeployScriptFileName parameter could be used in VS 2010 .dbproj projects to change the name of the generated .sql file, I noticed that changing its value in the .publish.xml file didn’t seem to have any affect at all (so I’m not really sure why Visual Studio puts it in there).  I randomly decided to try passing in PublishScriptFileName from the command line, and blamo, it worked!  I tried changing the <DeployScriptFileName> element in the .publish.xml file to <PublishScriptFileName>, but it still didn’t seem to have any effect.

        So now if I wanted to deploy my database project to 3 separate databases, I could do so with the following code to first Build the project, and the Publish it to the 3 databases:

        MSBuild /t:Build "[PathToBranch]\Database\Database.sqlproj"
        MSBuild /t:Publish /p:TargetDatabaseName="[DbName1]";TargetConnectionString="Data Source=[Db.Server];Integrated Security=True;Pooling=False" /p:PublishScriptFileName="[DbName1].sql" /p:SqlPublishProfilePath="Template.publish.xml" "[PathToBranch]\Database\Database.sqlproj"
        MSBuild /t:Publish /p:TargetDatabaseName="[DbName2]";TargetConnectionString="Data Source=[Db.Server];Integrated Security=True;Pooling=False" /p:PublishScriptFileName="[DbName2].sql" /p:SqlPublishProfilePath="Template.publish.xml" "[PathToBranch]\Database\Database.sqlproj"
        MSBuild /t:Publish /p:TargetDatabaseName="[DbName3]";TargetConnectionString="Data Source=[Db.Server];Integrated Security=True;Pooling=False" /p:PublishScriptFileName="[DbName3].sql" /p:SqlPublishProfilePath="Template.publish.xml" "[PathToBranch]\Database\Database.sqlproj"
        

        You could also instead just call MSBuild using the Build target with the PublishToDatabase parameter (which might actually be the safer bet); whatever you prefer.  I have found that once the database project is built once, as long as no changes are made to it then subsequent “builds” of the project only take a second or two since it detects that no changes have been made and skips doing the build.

        If you have any questions or feedback, let me know.

        Happy coding!

        Force ClickOnce applications to automatically update without prompting user – Automatically update MinimumRequiredVersion using PowerShell

        August 15th, 2012 1 comment

        Today I was thinking about using a ClickOnce application in my build process.  The problem is, when using an installed ClickOnce application (as opposed to an online one) if an update to the ClickOnce application is published, the application prompts the user to Accept or Skip downloading and applying the new update.  This would cause a problem for my automated builds as it would end up waiting forever for a user to click Accept.  This post lead me to the answer, which is:

        “If your application is an installed application, you can force updates by using the MinimumRequiredVersion attribute. If you publish your application using Visual Studio, you can set this property from the Updates Dialog.”

        Just for clarification, the dialog he mentions can be found in Visual Studio in Project Properties->Publish->Updates…  Ok, great.  This will allow the prompt to be suppressed, which is also useful if you don’t want to allow users to skip updates.

        There is still a problem however.  Every time I publish a new version of the tool I have to remember to go in and update the MinimumRequiredVersion.  If I forget to do this and then publish another release, the prompt will be back and will ruin my automated builds.

        To get around this I created a PowerShell script that keeps the MinimumRequiredVersion up to date, and I call it from a Post-Build event.  This allows me to never have to worry about manually setting the Minimum Required Version, since it gets updated automatically after every successful build.

        <EDIT>

        I’ve improved upon the powershell script below and created a NuGet package that handles all of the setup/installation for you, as described in my newer post.

        </EDIT>

        Here is the powershell script:

        # Script finds the current ClickOnce version in a project's .csproj file, and updates the MinimumRequiredVersion to be this same version.
        # This can be used to force a ClickOnce application to update automatically without prompting the user.
        
        [Parameter(Position=0, HelpMessage="Comma separated paths of the .csproj files to process")]
        Param([string]$projectFilePaths)
        
        # If a path to a project file was not provided, grab all of the project files in the same directory as this script.
        if (-not($projectFilePaths))
        {
        # Get the directory that this script is in.
        $scriptDirectory = Split-Path $MyInvocation.MyCommand.Path -Parent
        
        # Create comma-separated list of project file paths.
        Get-Item "$scriptDirectory\*.csproj" | foreach { $projectFilePaths += "$_,"}
        $projectFilePaths = $projectFilePaths.TrimEnd(',')
        }
        
        # Catch any unhandled exceptions, write its error message, and exit the process with a non-zero error code to indicate failure.
        trap
        {
        [string]$errorMessage = [string]$_
        [int]$exitCode = 1
        
        # If this is one of our custom exceptions, strip the error code off of the front.
        if ([string]$errorMessage.SubString(0, 1) -match "\d")
        {
        $exitCode = [string]$errorMessage.SubString(0, 1)
        $errorMessage = [string]$errorMessage.SubString(1)
        }
        
        Write-Error $errorMessage
        EXIT [int]$exitCode
        }
        
        Function UpdateProjectsMinimumRequiredClickOnceVersion
        {
        Param
        (
        [Parameter(Mandatory=$true, Position=0, HelpMessage="The project file (.csproj) to update.")]
        [string]$projectFilePath
        )
        if (-not([System.IO.File]::Exists($projectFilePath))) { throw "2Cannot find project file to update at the path: '$projectFilePath'" }
        
        # Build the regular expressions to find the information we will need.
        $rxMinimumRequiredVersionTag = New-Object System.Text.RegularExpressions.Regex "\<MinimumRequiredVersion\>(?<Version>.*?)\</MinimumRequiredVersion\>", SingleLine
        $rxApplicationVersionTag = New-Object System.Text.RegularExpressions.Regex "\<ApplicationVersion\>(?<Version>\d+\.\d+\.\d+\.).*?\</ApplicationVersion\>", SingleLine
        $rxApplicationRevisionTag = New-Object System.Text.RegularExpressions.Regex "\<ApplicationRevision\>(?<Revision>[0-9]+)\</ApplicationRevision\>", SingleLine
        $rxVersionNumber = [regex] "\d+\.\d+\.\d+\.\d+"
        
        # Read the file contents in.
        $text = [System.IO.File]::ReadAllText($projectFilePath)
        
        # Get the current Minimum Required Version, and the Version that it should be.
        $oldMinimumRequiredVersion = $rxMinimumRequiredVersionTag.Match($text).Groups["Version"].Value
        $majorMinorBuild = $rxApplicationVersionTag.Match($text).Groups["Version"].Value
        $revision = $rxApplicationRevisionTag.Match($text).Groups["Revision"].Value
        $newMinimumRequiredVersion = [string]$majorMinorBuild + $revision
        
        # If there was a problem constructing the new version number, throw an error.
        if (-not $rxVersionNumber.Match($newMinimumRequiredVersion).Success)
        {
        throw "3'$projectFilePath' does not appear to have any ClickOnce deployment settings in it."
        }
        
        # If we couldn't find the old Minimum Required Version, throw an error.
        if (-not $rxVersionNumber.Match($oldMinimumRequiredVersion).Success)
        {
        throw "4'$projectFilePath' is not currently set to enforce a MinimumRequiredVersion. To fix this in Visual Studio go to Project Properties->Publish->Updates... and check off 'Specify a minimum required version for this application'."
        }
        
        # Only write to the file if it is not already up to date.
        if ($newMinimumRequiredVersion -eq $oldMinimumRequiredVersion)
        {
        Write "The Minimum Required Version of '$projectFilePath' is already up-to-date on version '$newMinimumRequiredVersion'."
        }
        else
        {
        # Update the file contents and write them back to the file.
        $text = $rxMinimumRequiredVersionTag.Replace($text, "<MinimumRequiredVersion>" + $newMinimumRequiredVersion + "</MinimumRequiredVersion>")
        [System.IO.File]::WriteAllText($projectFilePath, $text)
        Write "Updated Minimum Required Version of '$projectFilePath' from '$oldMinimumRequiredVersion' to '$newMinimumRequiredVersion'"
        }
        }
        
        # Process each of the project files in the comma-separated list.
        $projectFilePaths.Split(",") | foreach { UpdateProjectsMinimumRequiredClickOnceVersion $_ }
        

        The script was actually very small at first, but after commenting it and adding some proper error handling it is fairly large now.

        So copy-paste the powershell script text into a new file, such as “UpdateClickOnceVersion.ps1”, and add this file to your project somewhere.

        The next step now is to call this script from the Post Build event, so in Visual Studio go into your ClickOnce project’s properties and go to the Build Events tab.  In the Post-build event command line put the following:

        
        REM Update the ClickOnce MinimumRequiredVersion so that it auto-updates without prompting
        PowerShell set-executionpolicy remotesigned
        PowerShell "$(ProjectDir)UpdateClickOnceVersion.ps1" "$(ProjectPath)"
        

        The first line is just a comment.  The second line may be a security concern, so you might want to remove it.  Basically by default PowerShell is not allowed to run any scripts, so trying to run our script above would result in an error.  To fix this we change the execution policy to allow remotesigned scripts to be ran.  I’m not going to pretend to understand why powershell requires this (as I just started learning it today), but that line only needs to be ran on a PC once, so if you want to remove it from here and just run it from PowerShell manually instead (if you haven’t already ran it before), feel free to do so.  I just include it here so that other developers who build this tool in the future don’t have to worry about this setting.

        The third line is where we are actually calling the powershell script, passing the path to the .csproj file to update as a parameter.  I added the powershell script to my project at the root level (so it sits right beside the .csproj file), but you can put the powershell script wherever you like.  Also, you don’t even have to include it in the project if you don’t want to, but I chose to so that it is easily visible for other developers when in Visual Studio, and so that it implicitly gets added to source control.  If you want to put the script in a folder instead of in the root of the project directory feel free; just remember to properly update the path in the post-build events.

        So after going through and adding all of my nice error messages to the powershell script, I realized that if there is a problem with the script Visual Studio does not forward the error message to the Output window like I hoped it would; it just spits out the text in the Post-build event window and says an error occurred; which doesn’t really tell us anything.  So if you find you are getting errors, copy-paste that third line into PowerShell, replace the macro variables for their absolute values, and run it there.  Powershell should then give you a much more informative error message.

        One last comment about this process is that because the powershell script modifies the .csproj outside of visual studio, after you publish a new version and build, the script will write to that .csproj file and visual studio will give you a prompt that the project was modified outside of visual studio and will want to reload it.  You can choose to reload it (which will close all file tabs for that project), or choose to ignore it; it’s up to you.  This is the one minor annoyance I haven’t been able to find a way around, but it’s still better than having to remember to update the Minimum Required Version manually after every new version of the tool I publish.

        I hope you find this post useful, and I appreciate any comments; good or bad.  Happy coding!

        VS 11 Beta merge tool is awesome, except for resolving conflicts

        April 5th, 2012 No comments

        If you’ve downloaded the new VS 11 Beta and done any merging, then you’ve probably seen the new diff and merge tools built into VS 11.  They are awesome, and by far a vast improvement over the ones included in VS 2010.  There is one problem with the merge tool though, and in my opinion it is huge.

        Basically the problem with the new VS 11 Beta merge tool is that when you are resolving conflicts after performing a merge, you cannot tell what changes were made in each file where the code is conflicting.  Was the conflicting code added, deleted, or modified in the source and target branches?  I don’t know (without explicitly opening up the history of both the source and target files), and the merge tool doesn’t tell me.  In my opinion this is a huge fail on the part of the designers/developers of the merge tool, as it actually forces me to either spend an extra minute for every conflict to view the source and target file history, or to go back to use the merge tool in VS 2010 to properly assess which changes I should take.

        I submitted this as a bug to Microsoft, but they say that this is intentional by design. WHAT?! So they purposely crippled their tool in order to make it pretty and keep the look consistent with the new diff tool?  That’s like purposely putting a little hole in the bottom of your cup for design reasons to make it look cool.  Sure, the cup looks cool, but I’m not going to use it if it leaks all over the place and doesn’t do the job that it is intended for. Bah! but I digress.

        Because this bug is apparently a feature, they asked me to open up a "feature request" to have the problem fixed. Please go vote up both my bug submission and the feature request so that this tool will actually be useful by the time the final VS 11 product is released.

        Path Too Long for Team Foundation Database Project Build

        February 5th, 2012 1 comment

        Arrggghhhh TFS and builds!  Such a love-hate relationship!  So we have our TFS builds setup to both compile our C# projects as well as compile and deploy our Team Foundation (TF) Database (DB) projects.  One day I started getting the following file path too long error message on our build server:

        $/RQ4TeamProject/Prototypes/BuildProcessTests/RQ4.Database.sln – 1 error(s), 69 warning(s), View Log File
        C:Program Files (x86)MSBuildMicrosoftVisualStudiov10.0TeamDataMicrosoft.Data.Schema.TSqlTasks.targets (80): The "SqlSetupDeployTask" task failed unexpectedly. Microsoft.Data.Schema.Build.BuildFailedException: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters. —> System.IO.PathTooLongException: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.    at System.IO.PathHelper.Append(Char value)    at System.IO.Path.NormalizePath(String path, Boolean fullCheck, Int32 maxPathLength)    at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath)    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy)    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)    at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize)    at System.IO.StreamReader..ctor(String path, Boolean detectEncodingFromByteOrderMarks)    at Microsoft.Data.Schema.Sql.Build.SqlPrePostDeploymentModifier.GenerateMergedSqlCmdFiles(DeploymentContributorConfigurationSetup setup, DeploymentContributorConfigurationFile configFile)    at Microsoft.Data.Schema.Sql.Build.SqlPrePostDeploymentModifier.OnEstablishDeploymentConfiguration(DeploymentContributorConfigurationSetup setup)    at Microsoft.Data.Schema.Build.DeploymentContributor.EstablishDeploymentConfiguration(DeploymentContributorConfigurationSetup setup)    — End of inner exception stack trace —    at Microsoft.Data.Schema.Build.DeploymentContributor.EstablishDeploymentConfiguration(DeploymentContributorConfigurationSetup setup)    at Microsoft.Data.Schema.Build.DeploymentProjectBuilder.VerifyConfiguration()    at Microsoft.Data.Schema.Tasks.DBSetupDeployTask.BuildDeploymentProject(ErrorManager errors, ExtensionManager em)    at Microsoft.Data.Schema.Tasks.DBSetupDeployTask.Execute()    at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()    at Microsoft.Build.BackEnd.TaskBuilder.ExecuteInstantiatedTask(ITaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask, Boolean& taskResult)

        Naturally I said, "Ok, our TF DB project isn’t compiling because a path is too long. Somebody must have checked in a stored procedure with a really long name".  After viewing the history of the branch I was trying to build however, I didn’t see anything that stuck out.  So for fun I thought I would shorten the Build Definition name’s length and build again.  Viola, like most path issues with TFS this fixed the issue (this is because the build definition name is often used in the path that TFS moves/builds files to).  However, we have many queries setup that match the specific Build Definition name (since it’s used in the "Integrated in Build" work item value), so shortening it wasn’t a long term solution.  As an added frustration bonus, I discovered our build definition name was only 1 character too long!

        The first thing I did was make a Path Length Checker program so I could see how long the file paths (files and directories) really were on the build server.  Oddly enough, the longest paths were 40 characters short of the maximum limit described by the error message.

        So I took a look at our database folder structure and saw that it really was wasting a lot of characters.  This is what the path to one of our stored procedure folders looks like: "..DatabaseSchema ObjectsSchemasdboProgrammabilityStored ProceduresProcs1".  I figured that I would just rename some of these folders in Visual Studio and that should be good……..OMG never try this while connected to TFS!  I got a popup warning for every single file under the directory I was renaming (thousands of them), with something along the lines of "Cannot access file X, or it is locked…..blah blah. Please press OK".  So after holding down the enter key for a over an hour to get past all these prompts it finally finished.  When I reviewed the changes to check in, I saw that many duplicate folders had been created, and there were miscellaneous files all over the place; some got moved, some never; what a mess.  So I went ahead and reverted my changes.

        So I thought, "Ok, let’s try this again, but first going offline so as not to connect to TFS".  So I disabled my internet connection and opened the database solution (this is the only way that I know of to work "offline" in TFS 🙁 ).  I then tried to change the high level folder "Schema Objects" to just "Schema".  Nope, Visual Studio complained that the folder was locked and couldn’t be changed.  I thought to myself, "TFS makes all non-checked out files read-only, and I’m offline so it can’t check them out.  That must be the problem".  So I opened up explorer and made all of the files and folders writable and tried again. Nope, no deal; same error message.

        So I thought, "Alright, let’s try doing a low level directory instead".  It seems that VS would only let me rename a directory that didn’t contain other directories.  So I renamed the "Procs1" folder to just "1".  I no longer got the warning prompt for every file, but it was still pretty slow and I could watch VS process every file in the Solution Explorer window.  After about 10 minutes it finally finished.  So I checked in my changes and tried building again.  Nope, same error message as before about the path being too long.

        So I said screw this.  I opened up the TFS Source Control Explorer and renamed the folder from there.  It worked just fine.  I then had to open up the Database.dbproj file in a text editor and do a find and replace to replace "Schema Objects" with "Schema".  This worked for refactoring the folder structure quickly, but I was still getting the "path too long" error message on the build server. Arrrrgg!

        So I went back to the build, set the verbosity to “diagnostic” and launched another build (which failed again with the path too long error).  Looking through the error message I noticed that it did complete building the DB schema, and went on to failing on building the Pre/Post deployment scripts.  Looking back to my original error message and reading it more carefully I noticed this line, “Microsoft.Data.Schema.Sql.Build.SqlPrePostDeploymentModifier.GenerateMergedSqlCmdFiles”.  So now I was pretty sure the problem was in the pre and post deployment scripts.

        Now, we have a very custom process for our database scripts, and part of this process involves using SQLCMD mode to include other script files into our pre and post deployment files when they are generated; it basically makes it look like the referenced script’s contents were in the pre/post deployment script the entire time.  This is necessary for us so that developers don’t have to look through pre and post deployment scripts that are tens of thousands of lines long.  It turns out that while none of these referenced script files themselves had a path that was over the limit, somehow during the generation of the pre/post deployment scripts it was making the path even longer.  I looked through our referenced scripts and saw a few particularly long ones.  So I refactored them to shorten the file names, and presto the build worked!  Hooray!

        I’m guessing that the reason the build wouldn’t give me an actual filename when it encountered the error is because SQLCMD mode was dynamically referencing those scripts at build time, so to the build it just looked like the pre and post deployment scripts were each thousands of lines long, when in fact they are only maybe 50 lines long, but they "include" other files, and those file references must be used at build time.

        So the morals of this story are:

        1. If VS is blowing chunks when you try to rename a folder (especially when connected to TFS), don’t do it through VS.  Instead modify the folder structure outside of VS and then manually edit the .csproj/.dbproj/.vbproj files to mirror the changes.

        2. Whenever you are stumped on a build error, go back and THOROUGHLY read the ENTIRE error message.

        3. Be careful when using compile-time language features to reference/include external files.

        Awesome Visual Studio Videos

        September 7th, 2011 No comments

        I watched an awesome Channel 9 Visual Studio Tips And Tricks video, so I thought I’d share.  Some great VS gems in there.  Even a few Windows 7 ones, like using Alt+D to jump to the address bar in Windows Explorer, and typing "cmd" in the Windows Explorer address bar to open a command prompt at the current directory.

        Also, if you’re interested in what the next Visual Studio release has in store, check out this Channel 9 video.

        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!

        Visual Studio Tips and Tricks

        June 29th, 2011 No comments

        Just found a few websites that show some Visual Studio tips that I haven’t seen before, so I thought I’d share:

        1 – Tips and Tricks for the Visual Studio .NET IDE

        2 – Essential Visual Studio Tips & Tricks that Every Developer Should Know

        3 – Channel 9’s Visual Studio Toolbox – weekly series dedicated to showing all the cool stuff that Visual Studio can do and how to be more productive with it.

        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}”

        TFS GoToWorkItem VS command and keyboard shortcut

        April 29th, 2011 No comments

        The button to jump directly to a work item by specifying its ID looks to be on the Work Item Tracking toolbar by default in VS / TFS 2010.  This button is not on the toolbar by default in VS / TFS 2008 though.  To add it yourself just go to Tools => Customize, then choose the category Team and the command Go To Work Item…, and you can drag the command into one of your existing toolbars.

        If you want to setup a keyboard shortcut for the command, just go to Tools => Options => Environment => Keyboard, and the command is called Team.GotoWorkItem.  I map it to Ctrl+Shift+/ since Ctrl+/ is the C# keyboard shortcut to search in a file.

        Some Visual Studio 2010 Shortcuts and C# 4.0 Cool Stuff

        April 17th, 2011 1 comment

        A list of some shortcus and new features to VS 2010 and C# 4.0:

        • Default values for parameters
        • Can access parameters by name (i.e. SomeFunction(name: "Dan", age: 26);
        • Can now put Labels on breakpoints and filter the breakpoints, as well as import and export breakpoints.
        • Window => New Window to open up same file in two separate tabs, or can drag the splitter at the top-right corner of the edit window.
        • Edit => Outlining => Hide Selection to collapse any region of code
        • Alt + Mouse Left Drag for box selection instead of line selection, then just start typing; you can also use Alt+Shift+Arrow Keys to do box selection with the keyboard.
        • Alt+Arrow Keys to move current line up/down.  Can also select multiple lines and use Alt+Up/Down to move the whole selection up/down.
        • In NavigateTo search window (Ctrl + Comma) use capitals to search for camel casing (i.e. CE to find displayCustomerEmails) and a space to do an "and" search (i.e. "email customer" would find displayCustomerEmails).
        • Ctrl + I to do an incremental search of a document, then F3 and Shift + F3 to move to next/previous matches.
        • Use snippets to automatically create code and save time.
        • Ctrl + Period to access VS tickler window instead of having to hover over the variable with the mouse.
        • Ctrl + Alt + Spacebar to change VS to suggest variable names instead of auto completing them.
        • Can right click in document to remove and sort using statements.
        • Enum.TryParse() has been added to match a string or number to an enumerated type.
        • Contract.Requires() and .Ensures() to ensure that function conditions are met (at compile time).
        • String.IsNullOrWhitespace(string);
        • Lazy<T> for thread-safe lazy loading of variables.
        • VS => Options => Debugging => Output Window => Data Binding to give more info about errors.
        • Using System.Threading.Tasks for parallel processing.  Parallel.For() and .ForEach
        • PLINQ => myCollection.InParallel().Where(x => …..);
        • New Dynamic keyword type => just like Object except not checked at compile time.
        • Ctrl+Shift+V to cycle through clipboard ring
        • Alt+Ctrl+Down to access tab menu
        • Ctrl+Shift+Up/Down to move between instances of the highlighted variable
        • Ctrl+] to move back and forth between a functions opening and closing braces (i.e. "{" and "}"). This appears to also work in XAML!
        • Alt+Arrow Keys to move current line up/down.  Can also select multiple lines and use Alt+Up/Down to move the whole selection up/down.
        • Rather than selecting a whole line first, just use Ctrl+C or Ctrl+X to Copy/Cut the entire line. You can also use Shift+Delete to delete an entire line without selecting it.