From be2bb20b9f4c02548673683aee4d8240655032b7 Mon Sep 17 00:00:00 2001 From: "Samir L. Boulema" Date: Mon, 11 Jun 2018 13:17:38 +0200 Subject: [PATCH] Switch to AsyncPackage --- .../AsyncPackageRegistrationAttribute.cs | 189 ++++++++++++++++++ .../AsyncPackageHelpers/ExtensionMethods.cs | 48 +++++ .../ProvideAutoloadAttribute.cs | 109 ++++++++++ TGit/TGIT.csproj | 48 +++-- TGit/TGITPackage.cs | 83 ++++++-- TGit/packages.config | 19 +- 6 files changed, 456 insertions(+), 40 deletions(-) create mode 100644 TGit/Helpers/AsyncPackageHelpers/AsyncPackageRegistrationAttribute.cs create mode 100644 TGit/Helpers/AsyncPackageHelpers/ExtensionMethods.cs create mode 100644 TGit/Helpers/AsyncPackageHelpers/ProvideAutoloadAttribute.cs diff --git a/TGit/Helpers/AsyncPackageHelpers/AsyncPackageRegistrationAttribute.cs b/TGit/Helpers/AsyncPackageHelpers/AsyncPackageRegistrationAttribute.cs new file mode 100644 index 0000000..127e264 --- /dev/null +++ b/TGit/Helpers/AsyncPackageHelpers/AsyncPackageRegistrationAttribute.cs @@ -0,0 +1,189 @@ +using System; +using System.Globalization; +using System.IO; +using System.ComponentModel; +using System.ComponentModel.Design; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace SamirBoulema.TGit.Helpers.AsyncPackageHelpers +{ + /// + /// This attribute is defined on a package to get it to be registered. It + /// is internal because packages are meant to be registered, so it is + /// implicit just by having a package in the assembly. + /// + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] + public sealed class AsyncPackageRegistrationAttribute : RegistrationAttribute + { + private RegistrationMethod registrationMethod = RegistrationMethod.Default; + private bool useManagedResources = false; + private bool allowsBackgroundLoad = false; + private string satellitePath = null; + + /// + /// Select between specifying the Codebase entry or the Assembly entry in the registry. + /// This can be overriden during registration + /// + public RegistrationMethod RegisterUsing + { + get + { + return registrationMethod; + } + set + { + registrationMethod = value; + } + } + + /// + /// For managed resources, there should not be a native ui dll registered. + /// + public bool UseManagedResourcesOnly + { + get { return useManagedResources; } + set { useManagedResources = value; } + } + + /// + /// Package is safe to load on a background thread. + /// + public bool AllowsBackgroundLoading + { + get { return allowsBackgroundLoad; } + set { allowsBackgroundLoad = value; } + } + + /// + /// To specify a resource dll located in a different location then the default, + /// set this property. This can be useful if your package is installed in the GAC. + /// If this is not set, the directory where the package is located will be use. + /// + /// Note that the dll should be located at the following path: + /// SatellitePath\lcid\PackageDllNameUI.dll + /// + public string SatellitePath + { + get { return satellitePath; } + set { satellitePath = value; } + } + + private string RegKeyName(RegistrationContext context) + { + return String.Format(CultureInfo.InvariantCulture, "Packages\\{0}", context.ComponentType.GUID.ToString("B")); + } + + /// + /// Called to register this attribute with the given context. The context + /// contains the location where the registration information should be placed. + /// it also contains such as the type being registered, and path information. + /// + /// This method is called both for registration and unregistration. The difference is + /// that unregistering just uses a hive that reverses the changes applied to it. + /// + /// + /// Contains the location where the registration information should be placed. + /// It also contains other information such as the type being registered + /// and path of the assembly. + /// + public override void Register(RegistrationContext context) + { + Type t = context.ComponentType; + + Key packageKey = null; + try + { + packageKey = context.CreateKey(RegKeyName(context)); + + //use a friendly description if it exists. + DescriptionAttribute attr = TypeDescriptor.GetAttributes(t)[typeof(DescriptionAttribute)] as DescriptionAttribute; + if (attr != null && !String.IsNullOrEmpty(attr.Description)) + { + packageKey.SetValue(string.Empty, attr.Description); + } + else + { + packageKey.SetValue(string.Empty, t.Name); + } + + packageKey.SetValue("InprocServer32", context.InprocServerPath); + packageKey.SetValue("Class", t.FullName); + + // If specified on the command line, let the command line option override + if (context.RegistrationMethod != RegistrationMethod.Default) + { + registrationMethod = context.RegistrationMethod; + } + + // Select registration method + switch (registrationMethod) + { + case RegistrationMethod.Assembly: + case RegistrationMethod.Default: + packageKey.SetValue("Assembly", t.Assembly.FullName); + break; + + case RegistrationMethod.CodeBase: + packageKey.SetValue("CodeBase", context.CodeBase); + break; + } + + Key childKey = null; + if (!useManagedResources) + { + try + { + childKey = packageKey.CreateSubkey("SatelliteDll"); + + // Register the satellite dll + string satelliteDllPath; + if (SatellitePath != null) + { + // Use provided path + satelliteDllPath = context.EscapePath(SatellitePath); + } + else + { + // Default to package path + satelliteDllPath = context.ComponentPath; + } + childKey.SetValue("Path", satelliteDllPath); + childKey.SetValue("DllName", String.Format(CultureInfo.InvariantCulture, "{0}UI.dll", Path.GetFileNameWithoutExtension(t.Assembly.ManifestModule.Name))); + } + finally + { + if (childKey != null) + childKey.Close(); + } + } + + if (allowsBackgroundLoad) + { + packageKey.SetValue("AllowsBackgroundLoad", true); + } + + if (typeof(IVsPackageDynamicToolOwner).IsAssignableFrom(context.ComponentType) || + typeof(IVsPackageDynamicToolOwnerEx).IsAssignableFrom(context.ComponentType)) + { + packageKey.SetValue("SupportsDynamicToolOwner", true); + } + } + finally + { + if (packageKey != null) + packageKey.Close(); + } + } + + /// + /// Unregister this package. + /// + /// + public override void Unregister(RegistrationContext context) + { + context.RemoveKey(RegKeyName(context)); + } + + } +} diff --git a/TGit/Helpers/AsyncPackageHelpers/ExtensionMethods.cs b/TGit/Helpers/AsyncPackageHelpers/ExtensionMethods.cs new file mode 100644 index 0000000..8e45088 --- /dev/null +++ b/TGit/Helpers/AsyncPackageHelpers/ExtensionMethods.cs @@ -0,0 +1,48 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace SamirBoulema.TGit.Helpers.AsyncPackageHelpers +{ + public static class ExtensionMethods + { + /// + /// Helper method to use async/await with IAsyncServiceProvider implementation + /// + /// IAsyncServciceProvider instance + /// Type of the Visual Studio service requested + /// Service object as type of T + public static async Task GetServiceAsync(this Microsoft.VisualStudio.Shell.Interop.IAsyncServiceProvider asyncServiceProvider, Type serviceType) where T : class + { + T returnValue = null; + + await ThreadHelper.JoinableTaskFactory.RunAsync(async () => + { + object serviceInstance = null; + Guid serviceTypeGuid = serviceType.GUID; + serviceInstance = await asyncServiceProvider.QueryServiceAsync(ref serviceTypeGuid); + + // We have to make sure we are on main UI thread before trying to cast as underlying implementation + // can be an STA COM object and doing a cast would require calling QueryInterface/AddRef marshaling + // to main thread via COM. + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + returnValue = serviceInstance as T; + }); + + return returnValue; + } + + /// + /// Gets if async package is supported in the current instance of Visual Studio + /// + /// an IServiceProvider instance, usually a Package instance + /// true if async packages are supported + public static bool IsAsyncPackageSupported(this IServiceProvider serviceProvider) + { + Microsoft.VisualStudio.Shell.Interop.IAsyncServiceProvider asyncServiceProvider = serviceProvider.GetService(typeof(SAsyncServiceProvider)) as Microsoft.VisualStudio.Shell.Interop.IAsyncServiceProvider; + return asyncServiceProvider != null; + } + } +} diff --git a/TGit/Helpers/AsyncPackageHelpers/ProvideAutoloadAttribute.cs b/TGit/Helpers/AsyncPackageHelpers/ProvideAutoloadAttribute.cs new file mode 100644 index 0000000..5ffa397 --- /dev/null +++ b/TGit/Helpers/AsyncPackageHelpers/ProvideAutoloadAttribute.cs @@ -0,0 +1,109 @@ +using System; +using System.Globalization; +using Microsoft.VisualStudio.Shell; + +namespace SamirBoulema.TGit.Helpers.AsyncPackageHelpers +{ + [Flags] + public enum PackageAutoLoadFlags + { + /// + /// Indicates no special auto-load behavior. This is the default flag value if not specified. + /// + None = 0x0, + + /// + /// When set package will not auto load in newer Visual Studio versions with rule based UI contexts + /// + SkipWhenUIContextRulesActive = 0x1, + + /// + /// When set, if the associated package is marked as allowing background loads (via the ), + /// then the package will be loaded asynchronously, in the background, when the associated UI context is triggered. + /// + BackgroundLoad = 0x2 + } + + /// + /// This attribute registers the package as an extender. The GUID passed in determines + /// what is being extended. The attributes on a package do not control the behavior of + /// the package, but they can be used by registration tools to register the proper + /// information with Visual Studio. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] + public sealed class ProvideAutoLoadAttribute : RegistrationAttribute + { + + private Guid loadGuid = Guid.Empty; + + /// + /// Specify that the package should get loaded when this context is active. + /// + /// Context which should trigger the loading of your package. + public ProvideAutoLoadAttribute(string cmdUiContextGuid) : this(cmdUiContextGuid, PackageAutoLoadFlags.None) + { + } + + /// + /// Specify that the package should get loaded when this context is active. + /// + /// Context which should trigger the loading of your package. + public ProvideAutoLoadAttribute(string cmdUiContextGuid, PackageAutoLoadFlags flags = PackageAutoLoadFlags.None) + { + loadGuid = new Guid(cmdUiContextGuid); + Flags = flags; + } + + /// + /// Context Guid which triggers the loading of the package. + /// + public Guid LoadGuid + { + get + { + return loadGuid; + } + } + + /// + /// Specifies the options for package auto load entry + /// + public PackageAutoLoadFlags Flags + { + get; + private set; + } + + /// + /// The reg key name of this AutoLoad. + /// + private string RegKeyName + { + get + { + return string.Format(CultureInfo.InvariantCulture, "AutoLoadPackages\\{0}", loadGuid.ToString("B")); + } + } + + /// + /// Called to register this attribute with the given context. The context + /// contains the location where the registration information should be placed. + /// it also contains such as the type being registered, and path information. + /// + public override void Register(RegistrationContext context) + { + using (Key childKey = context.CreateKey(RegKeyName)) + { + childKey.SetValue(context.ComponentType.GUID.ToString("B"), (int)Flags); + } + } + + /// + /// Unregister this AutoLoad specification. + /// + public override void Unregister(RegistrationContext context) + { + context.RemoveValue(RegKeyName, context.ComponentType.GUID.ToString("B")); + } + } +} diff --git a/TGit/TGIT.csproj b/TGit/TGIT.csproj index 3218d84..f68867f 100644 --- a/TGit/TGIT.csproj +++ b/TGit/TGIT.csproj @@ -97,13 +97,11 @@ True + ..\packages\Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.14.3.26930\lib\net20\Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll True - ..\packages\Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.14.3.25407\lib\Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll - True - ..\packages\Microsoft.VisualStudio.OLE.Interop.7.10.6070\lib\Microsoft.VisualStudio.OLE.Interop.dll - True + ..\packages\Microsoft.VisualStudio.OLE.Interop.7.10.6071\lib\Microsoft.VisualStudio.OLE.Interop.dll ..\packages\VSSDK.Shell.12.12.0.4\lib\net45\Microsoft.VisualStudio.Shell.12.0.dll @@ -133,31 +131,36 @@ True - ..\packages\Microsoft.VisualStudio.Shell.Interop.7.10.6071\lib\Microsoft.VisualStudio.Shell.Interop.dll - True + ..\packages\Microsoft.VisualStudio.Shell.Interop.7.10.6072\lib\net11\Microsoft.VisualStudio.Shell.Interop.dll + + + ..\packages\Microsoft.VisualStudio.Shell.Interop.10.0.10.0.30320\lib\net20\Microsoft.VisualStudio.Shell.Interop.10.0.dll + True + + + ..\packages\Microsoft.VisualStudio.Shell.Interop.11.0.11.0.61031\lib\net20\Microsoft.VisualStudio.Shell.Interop.11.0.dll + True - - - true + + ..\packages\Microsoft.VisualStudio.Shell.Interop.12.0.12.0.30111\lib\net20\Microsoft.VisualStudio.Shell.Interop.12.0.dll + True - - true + + ..\packages\Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.14.3.26929\lib\net20\Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll + True - ..\packages\Microsoft.VisualStudio.Shell.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.Shell.Interop.8.0.dll - True + ..\packages\Microsoft.VisualStudio.Shell.Interop.8.0.8.0.50728\lib\net11\Microsoft.VisualStudio.Shell.Interop.8.0.dll ..\packages\Microsoft.VisualStudio.Shell.Interop.9.0.9.0.30729\lib\Microsoft.VisualStudio.Shell.Interop.9.0.dll True - ..\packages\Microsoft.VisualStudio.TextManager.Interop.7.10.6070\lib\Microsoft.VisualStudio.TextManager.Interop.dll - True + ..\packages\Microsoft.VisualStudio.TextManager.Interop.7.10.6071\lib\net11\Microsoft.VisualStudio.TextManager.Interop.dll - ..\packages\Microsoft.VisualStudio.TextManager.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.TextManager.Interop.8.0.dll - True + ..\packages\Microsoft.VisualStudio.TextManager.Interop.8.0.8.0.50728\lib\net11\Microsoft.VisualStudio.TextManager.Interop.8.0.dll ..\packages\Microsoft.VisualStudio.Threading.14.1.111\lib\net45\Microsoft.VisualStudio.Threading.dll @@ -203,6 +206,9 @@ + + + @@ -387,6 +393,10 @@ MSBuild:Compile + + + + true @@ -398,8 +408,12 @@ + + + +