Archive

Posts Tagged ‘Visual Studio Extensions’

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!

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!

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.