-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
456 additions
and
40 deletions.
There are no files selected for viewing
189 changes: 189 additions & 0 deletions
189
TGit/Helpers/AsyncPackageHelpers/AsyncPackageRegistrationAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
{ | ||
/// <devdoc> | ||
/// 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. | ||
/// </devdoc> | ||
[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; | ||
|
||
/// <devdoc> | ||
/// Select between specifying the Codebase entry or the Assembly entry in the registry. | ||
/// This can be overriden during registration | ||
/// </devdoc> | ||
public RegistrationMethod RegisterUsing | ||
{ | ||
get | ||
{ | ||
return registrationMethod; | ||
} | ||
set | ||
{ | ||
registrationMethod = value; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// For managed resources, there should not be a native ui dll registered. | ||
/// </summary> | ||
public bool UseManagedResourcesOnly | ||
{ | ||
get { return useManagedResources; } | ||
set { useManagedResources = value; } | ||
} | ||
|
||
/// <summary> | ||
/// Package is safe to load on a background thread. | ||
/// </summary> | ||
public bool AllowsBackgroundLoading | ||
{ | ||
get { return allowsBackgroundLoad; } | ||
set { allowsBackgroundLoad = value; } | ||
} | ||
|
||
/// <summary> | ||
/// 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 | ||
/// </summary> | ||
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")); | ||
} | ||
|
||
/// <devdoc> | ||
/// 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. | ||
/// </devdoc> | ||
/// <param name="context"> | ||
/// 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. | ||
/// </param> | ||
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(); | ||
} | ||
} | ||
|
||
/// <devdoc> | ||
/// Unregister this package. | ||
/// </devdoc> | ||
/// <param name="context"></param> | ||
public override void Unregister(RegistrationContext context) | ||
{ | ||
context.RemoveKey(RegKeyName(context)); | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
{ | ||
/// <summary> | ||
/// Helper method to use async/await with IAsyncServiceProvider implementation | ||
/// </summary> | ||
/// <param name="asyncServiceProvider">IAsyncServciceProvider instance</param> | ||
/// <param name="serviceType">Type of the Visual Studio service requested</param> | ||
/// <returns>Service object as type of T</returns> | ||
public static async Task<T> GetServiceAsync<T>(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; | ||
} | ||
|
||
/// <summary> | ||
/// Gets if async package is supported in the current instance of Visual Studio | ||
/// </summary> | ||
/// <param name="serviceProvider">an IServiceProvider instance, usually a Package instance</param> | ||
/// <returns>true if async packages are supported</returns> | ||
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; | ||
} | ||
} | ||
} |
109 changes: 109 additions & 0 deletions
109
TGit/Helpers/AsyncPackageHelpers/ProvideAutoloadAttribute.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
using System; | ||
using System.Globalization; | ||
using Microsoft.VisualStudio.Shell; | ||
|
||
namespace SamirBoulema.TGit.Helpers.AsyncPackageHelpers | ||
{ | ||
[Flags] | ||
public enum PackageAutoLoadFlags | ||
{ | ||
/// <summary> | ||
/// Indicates no special auto-load behavior. This is the default flag value if not specified. | ||
/// </summary> | ||
None = 0x0, | ||
|
||
/// <summary> | ||
/// When set package will not auto load in newer Visual Studio versions with rule based UI contexts | ||
/// </summary> | ||
SkipWhenUIContextRulesActive = 0x1, | ||
|
||
/// <summary> | ||
/// When set, if the associated package is marked as allowing background loads (via the <see cref="AsyncPackageRegistrationAttribute"/>), | ||
/// then the package will be loaded asynchronously, in the background, when the associated UI context is triggered. | ||
/// </summary> | ||
BackgroundLoad = 0x2 | ||
} | ||
|
||
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] | ||
public sealed class ProvideAutoLoadAttribute : RegistrationAttribute | ||
{ | ||
|
||
private Guid loadGuid = Guid.Empty; | ||
|
||
/// <summary> | ||
/// Specify that the package should get loaded when this context is active. | ||
/// </summary> | ||
/// <param name="cmdUiContextGuid">Context which should trigger the loading of your package.</param> | ||
public ProvideAutoLoadAttribute(string cmdUiContextGuid) : this(cmdUiContextGuid, PackageAutoLoadFlags.None) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Specify that the package should get loaded when this context is active. | ||
/// </summary> | ||
/// <param name="cmdUiContextGuid">Context which should trigger the loading of your package.</param> | ||
public ProvideAutoLoadAttribute(string cmdUiContextGuid, PackageAutoLoadFlags flags = PackageAutoLoadFlags.None) | ||
{ | ||
loadGuid = new Guid(cmdUiContextGuid); | ||
Flags = flags; | ||
} | ||
|
||
/// <summary> | ||
/// Context Guid which triggers the loading of the package. | ||
/// </summary> | ||
public Guid LoadGuid | ||
{ | ||
get | ||
{ | ||
return loadGuid; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Specifies the options for package auto load entry | ||
/// </summary> | ||
public PackageAutoLoadFlags Flags | ||
{ | ||
get; | ||
private set; | ||
} | ||
|
||
/// <summary> | ||
/// The reg key name of this AutoLoad. | ||
/// </summary> | ||
private string RegKeyName | ||
{ | ||
get | ||
{ | ||
return string.Format(CultureInfo.InvariantCulture, "AutoLoadPackages\\{0}", loadGuid.ToString("B")); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
public override void Register(RegistrationContext context) | ||
{ | ||
using (Key childKey = context.CreateKey(RegKeyName)) | ||
{ | ||
childKey.SetValue(context.ComponentType.GUID.ToString("B"), (int)Flags); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Unregister this AutoLoad specification. | ||
/// </summary> | ||
public override void Unregister(RegistrationContext context) | ||
{ | ||
context.RemoveValue(RegKeyName, context.ComponentType.GUID.ToString("B")); | ||
} | ||
} | ||
} |
Oops, something went wrong.