Add support for checkin policies for VS2017 and VS2019
Visual Studio versions >= 2017 (or is it actually 2015 ?) store the necessary information for e.g. CheckinPolicies no longer in the registry. Instead, they use a private registry usually found in C:\Users\<USER>\AppData\Local\Microsoft\VisualStudio\15.0_xxxxxx\privateregistry.bin where the xxxxxx value is differing between the different installed Visual Studio editions and/or minor versions. This commit introduces the ExternalSettingsManager which loads this private registry and we can then add the VS extension folders gathered from the SettingsStore to our assembly search path. Sadly, this only works for 32bit process and crashes in a 64bit process as soon as a method tries to create an ExternalSettingsManager instance. Therefore this is put behind a check so we only do it in a 32bit process. This implies that the checkin policies are only working in 32bit mode! In addition to the search patsh gathered from the ExternalSettingsManager, add <vsInstallDir>\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer to the search path, as otherwise some of the checkin policies used in my dev environment here didn't load because a library in that folder wasn't found. This indicated to me that we still are not 100% compatibile to what Visual Studio does, but at least this fixes all the custom checkin policies here, so it can't be that bad.
This commit is contained in:
@@ -11,9 +11,11 @@ using GitTfs.Core.TfsInterop;
|
||||
|
||||
using Microsoft.TeamFoundation.Build.Client;
|
||||
using Microsoft.TeamFoundation.Client;
|
||||
using Microsoft.TeamFoundation.Common;
|
||||
using Microsoft.TeamFoundation.Server;
|
||||
using Microsoft.TeamFoundation.VersionControl.Client;
|
||||
using Microsoft.VisualStudio.Services.Client;
|
||||
using Microsoft.VisualStudio.Settings;
|
||||
using Microsoft.VisualStudio.Setup.Configuration;
|
||||
|
||||
using StructureMap;
|
||||
@@ -42,6 +44,17 @@ namespace GitTfs.VsCommon
|
||||
|
||||
private readonly int myMajorVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Loading the ExternalSettingsManager and then GetReadOnlySettingsStore ensures
|
||||
/// that also the private Visual Studio registry hive which is usually found
|
||||
/// in a path looking similar to
|
||||
/// C:\Users\USER\AppData\Local\Microsoft\VisualStudio\15.0_xxxxxx\privateregistry.bin
|
||||
/// is loaded.
|
||||
/// Without loading that private VS registry hive, private CheckinPolicies will not work,
|
||||
/// as the assemblies are simply not found.
|
||||
/// </summary>
|
||||
private ExternalSettingsManager myExternalSettingsManager;
|
||||
|
||||
public TfsHelperVS2017Base(TfsApiBridge bridge, IContainer container, int majorVersion)
|
||||
: base(bridge, container)
|
||||
{
|
||||
@@ -51,10 +64,52 @@ namespace GitTfs.VsCommon
|
||||
myAssemblySearchPaths = new List<string>();
|
||||
if (!string.IsNullOrEmpty(myVisualStudioInstallationPath))
|
||||
{
|
||||
// Calling LoadAssemblySearchPathFromVisualStudioPrivateRegistry would immediately
|
||||
// crash with BadImageException in a 64Bit process therefore put it behind a check
|
||||
if (!Environment.Is64BitProcess)
|
||||
{
|
||||
var devenvPath = Path.Combine(myVisualStudioInstallationPath, @"Common7\IDE\devenv.exe");
|
||||
LoadAssemblySearchPathFromVisualStudioPrivateRegistry(devenvPath);
|
||||
}
|
||||
|
||||
myAssemblySearchPaths.Add(Path.Combine(myVisualStudioInstallationPath, myPrivateAssembliesFolder));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the Visual Studio private registry, which is implicitly done when creating
|
||||
/// a new ExternalSettingsManager. The private registry contains the search paths to the
|
||||
/// extensions which is required for the Check-In Policies to work.
|
||||
///
|
||||
/// Calling this method on a 64bit process will not work, as a BadImageException is thrown.
|
||||
/// </summary>
|
||||
/// <param name="devenvPath">Path to the Visual Studio installation for which the private registry shall be loaded</param>
|
||||
private void LoadAssemblySearchPathFromVisualStudioPrivateRegistry(string devenvPath)
|
||||
{
|
||||
Trace.WriteLine($"Loading VS private registry for '{devenvPath}");
|
||||
myExternalSettingsManager = ExternalSettingsManager.CreateForApplication(devenvPath);
|
||||
|
||||
Trace.WriteLine("ApplicationExtensions:" + myExternalSettingsManager.GetApplicationDataFolder(ApplicationDataFolder.ApplicationExtensions));
|
||||
Trace.WriteLine(myExternalSettingsManager.GetApplicationDataFolder(ApplicationDataFolder.Configuration));
|
||||
Trace.WriteLine(myExternalSettingsManager.GetApplicationDataFolder(ApplicationDataFolder.Documents));
|
||||
Trace.WriteLine(myExternalSettingsManager.GetApplicationDataFolder(ApplicationDataFolder.LocalSettings));
|
||||
Trace.WriteLine(myExternalSettingsManager.GetApplicationDataFolder(ApplicationDataFolder.RoamingSettings));
|
||||
Trace.WriteLine("UserExtensions :" + myExternalSettingsManager.GetApplicationDataFolder(ApplicationDataFolder.UserExtensions));
|
||||
foreach (string searchPath in myExternalSettingsManager.GetCommonExtensionsSearchPaths()) {
|
||||
Trace.WriteLine("CommonExtensionsPath :" + searchPath);
|
||||
}
|
||||
SettingsStore store = myExternalSettingsManager.GetReadOnlySettingsStore(SettingsScope.UserSettings);
|
||||
var propNames = store.GetPropertyNames(@"ExtensionManager\EnabledExtensions");
|
||||
|
||||
myAssemblySearchPaths.AddRange(myExternalSettingsManager.GetCommonExtensionsSearchPaths());
|
||||
string userExtensions = myExternalSettingsManager.GetApplicationDataFolder(ApplicationDataFolder.UserExtensions);
|
||||
if (!userExtensions.IsNullOrEmpty())
|
||||
{
|
||||
myAssemblySearchPaths.Add(Path.Combine(myVisualStudioInstallationPath, userExtensions));
|
||||
}
|
||||
myAssemblySearchPaths.Add(Path.Combine(myVisualStudioInstallationPath, myTeamExplorerFolder));
|
||||
}
|
||||
|
||||
protected override bool HasWorkItems(Changeset changeset)
|
||||
{
|
||||
return Retry.Do(() => changeset.AssociatedWorkItems.Length > 0);
|
||||
|
||||
Reference in New Issue
Block a user