├── icon ├── vsix-pt.ico └── vsix-pt.png ├── src ├── VSIXPowerToys │ ├── srm.exe │ ├── Properties │ │ ├── sorry_but_I_have_to.snk │ │ └── AssemblyInfo.cs │ ├── packages.config │ ├── VsUtils.cs │ ├── DistinctBy.cs │ ├── VSIXPowerToys.Shell.csproj │ ├── VsixPowerToys.cs │ └── SystemCursor.cs ├── HostProcess │ ├── App.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── packages.config │ ├── Program.cs │ └── HostProcess.csproj ├── Shell.Interop │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Shell.Interop.csproj │ └── Interop.cs └── VSIXPowerToys.sln ├── .gitattributes ├── .gitignore ├── LICENSE └── README.md /icon/vsix-pt.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmemcpy/VSIXPowerToys/HEAD/icon/vsix-pt.ico -------------------------------------------------------------------------------- /icon/vsix-pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmemcpy/VSIXPowerToys/HEAD/icon/vsix-pt.png -------------------------------------------------------------------------------- /src/VSIXPowerToys/srm.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmemcpy/VSIXPowerToys/HEAD/src/VSIXPowerToys/srm.exe -------------------------------------------------------------------------------- /src/VSIXPowerToys/Properties/sorry_but_I_have_to.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmemcpy/VSIXPowerToys/HEAD/src/VSIXPowerToys/Properties/sorry_but_I_have_to.snk -------------------------------------------------------------------------------- /src/HostProcess/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/VSIXPowerToys/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/HostProcess/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | [assembly: AssemblyTitle("HostProcess")] 4 | [assembly: AssemblyDescription("A 32-bit host process for Visual Studio API")] 5 | [assembly: AssemblyConfiguration("")] 6 | [assembly: AssemblyCompany("Igal Tabachnik")] 7 | [assembly: AssemblyProduct("VSIX PowerToys")] 8 | [assembly: AssemblyCopyright("Copyright © 2016 Igal Tabachnik")] 9 | -------------------------------------------------------------------------------- /src/VSIXPowerToys/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | [assembly: AssemblyTitle("VSIX PowerToys")] 4 | [assembly: AssemblyProduct("VSIX PowerToys")] 5 | [assembly: AssemblyDescription("Windows Shell Extensions (and other utilities) to work with Visual Studio Extensions (vsix) files.")] 6 | [assembly: AssemblyCompany("Igal Tabachnik")] 7 | [assembly: AssemblyCopyright("Copyright © 2015-2016 Igal Tabachnik")] -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=crlf 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.c text 7 | *.h text 8 | 9 | # Declare files that will always have CRLF line endings on checkout. 10 | *.sln text eol=crlf 11 | 12 | # Denote all files that are truly binary and should not be modified. 13 | *.png binary 14 | *.jpg binary -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pdb 2 | *.suo 3 | *.vspcc 4 | .svn 5 | bin 6 | Debug 7 | Release 8 | 9 | #Armadillo files 10 | ARMADILLO_CACHE 11 | 12 | #OS junk files 13 | [Tt]humbs.db 14 | *.DS_Store 15 | 16 | #Visual Studio files 17 | *.[Oo]bj 18 | *.user 19 | *.aps 20 | *.pch 21 | *.vspscc 22 | *.vssscc 23 | *_i.c 24 | *_p.c 25 | *.ncb 26 | *.suo 27 | *.tlb 28 | *.tlh 29 | *.bak 30 | *.[Cc]ache 31 | *.ilk 32 | *.log 33 | *.lib 34 | *.sbr 35 | *.sdf 36 | *.opensdf 37 | ipch/ 38 | obj/ 39 | [Bb]in 40 | [Dd]ebug*/ 41 | [Rr]elease*/ 42 | Ankh.NoLoad 43 | packages/ 44 | 45 | #Tooling 46 | _ReSharper*/ 47 | *.resharper 48 | [Tt]est[Rr]esult* 49 | 50 | #Project files 51 | [Bb]uild/ 52 | 53 | #Subversion files 54 | .svn 55 | 56 | # Office Temp Files 57 | ~$* 58 | 59 | #ncrunch 60 | *ncrunch* 61 | *crunch*.local.xml 62 | *.nupkg 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015, Igal Tabachnik 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/Shell.Interop/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Shell.Interop")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Shell.Interop")] 13 | [assembly: AssemblyCopyright("Copyright © 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("6995335e-544f-454d-9dde-5048cff1c8a5")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://raw.githubusercontent.com/hmemcpy/VSIXPowerToys/master/icon/vsix-pt.png) 2 | # VSIX PowerToys 3 | Windows Shell Extensions (and other utilities) to work with Visual Studio Extensions (vsix) files. 4 | 5 | ## Features 6 | 7 | ### Install into specific Hive 8 | 9 | Installs the VSIX into a specified hive. If the VSIX is configured to be installed for all users, it will be installed into `Common7\IDE\Extensions` instead. 10 | 11 | ![image](https://cloud.githubusercontent.com/assets/601206/14064284/794cc4c0-f406-11e5-8b2e-697dd99f17e3.png) 12 | 13 | ### Copy VSIX ID to Clipboard 14 | 15 | Copies the VSIX manifest ID value to the clipboard. 16 | 17 | ![image](https://cloud.githubusercontent.com/assets/601206/14064257/eedbcd28-f404-11e5-9e22-d21463cbab8d.png) 18 | 19 | More PowerToys coming soon... 20 | 21 | ## Installation 22 | 23 | Grab the latest installer from the [Releases](../../releases) page. 24 | 25 | Note: Visual Studio 2015 [Community](https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx) or better is required to be installed on the machine. 26 | 27 | This project uses [Microsoft Visual Studio 2015 Installer Projects](https://visualstudiogallery.msdn.microsoft.com/f1cc3f3e-c300-40a7-8797-c509fb8933b9) extension to create the MSI setup. 28 | 29 | ## Acknowledgments 30 | 31 | VSIX PowerToys is built using the excellent [SharpShell](https://github.com/dwmkerr/sharpshell) library by [Dave Kerr](https://github.com/dwmkerr). 32 | 33 | ## Bugs? Ideas? Comments? 34 | 35 | Please feel free to [report them](../../issues)! 36 | 37 | -------------------------------------------------------------------------------- /src/HostProcess/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/VSIXPowerToys.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VSIXPowerToys.Shell", "VSIXPowerToys\VSIXPowerToys.Shell.csproj", "{C1EA0E01-E032-4478-BC78-6F50BE3E3D87}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shell.Interop", "Shell.Interop\Shell.Interop.csproj", "{1A7318F0-8180-49B3-AC2C-B04F9EBC58EA}" 9 | EndProject 10 | Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "VSIXPowerToys.Setup", "VSIXPowerToys.Setup\VSIXPowerToys.Setup.vdproj", "{99983280-380C-42CD-8A03-65474AEBE8D5}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HostProcess", "HostProcess\HostProcess.csproj", "{43E348CA-653A-4FD1-8215-0FE9BD3BDD25}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {C1EA0E01-E032-4478-BC78-6F50BE3E3D87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {C1EA0E01-E032-4478-BC78-6F50BE3E3D87}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {C1EA0E01-E032-4478-BC78-6F50BE3E3D87}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {C1EA0E01-E032-4478-BC78-6F50BE3E3D87}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {1A7318F0-8180-49B3-AC2C-B04F9EBC58EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {1A7318F0-8180-49B3-AC2C-B04F9EBC58EA}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {1A7318F0-8180-49B3-AC2C-B04F9EBC58EA}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {1A7318F0-8180-49B3-AC2C-B04F9EBC58EA}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {99983280-380C-42CD-8A03-65474AEBE8D5}.Debug|Any CPU.ActiveCfg = Debug 29 | {99983280-380C-42CD-8A03-65474AEBE8D5}.Release|Any CPU.ActiveCfg = Release 30 | {43E348CA-653A-4FD1-8215-0FE9BD3BDD25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {43E348CA-653A-4FD1-8215-0FE9BD3BDD25}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {43E348CA-653A-4FD1-8215-0FE9BD3BDD25}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {43E348CA-653A-4FD1-8215-0FE9BD3BDD25}.Release|Any CPU.Build.0 = Release|Any CPU 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /src/Shell.Interop/Shell.Interop.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {1A7318F0-8180-49B3-AC2C-B04F9EBC58EA} 8 | Library 9 | Properties 10 | Shell.Interop 11 | Shell.Interop 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 54 | -------------------------------------------------------------------------------- /src/VSIXPowerToys/VsUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Globalization; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Runtime.InteropServices; 8 | using Microsoft.Win32; 9 | 10 | namespace VSIXPowerToys 11 | { 12 | public static class VsUtils 13 | { 14 | private static readonly DirectoryInfo VsLocalDirectory = GetLocalVisualStudioDirectory(); 15 | 16 | private static DirectoryInfo GetLocalVisualStudioDirectory() 17 | { 18 | var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); 19 | return new DirectoryInfo(Path.Combine(localAppData, "Microsoft", "VisualStudio")); 20 | } 21 | 22 | public static IEnumerable GetAllHives(VsVersion vsVersion) 23 | { 24 | return VsLocalDirectory.EnumerateDirectories().Where(d => d.Name.StartsWith(vsVersion.Version)) 25 | .Select(d => new VsHive(vsVersion, d.Name.Substring(vsVersion.Version.Length))); 26 | } 27 | 28 | public static IEnumerable GetInstalledVisualStudioVersions() 29 | { 30 | using (var hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)) 31 | using (var vs = hklm.OpenSubKey("Software\\Microsoft\\VisualStudio")) 32 | { 33 | if (vs == null) 34 | yield break; 35 | 36 | foreach (var s in vs.GetSubKeyNames().Where(IsVsVersion).OrderByDescending(s => s)) 37 | { 38 | using (var setup = vs.OpenSubKey(s + "\\" + "Setup\\vs")) 39 | { 40 | var devenv = (string)setup?.GetValue("EnvironmentPath"); 41 | if (!string.IsNullOrWhiteSpace(devenv) && File.Exists(devenv)) 42 | yield return new VsVersion(s, devenv); 43 | } 44 | } 45 | } 46 | } 47 | 48 | public static void CheckLastWin32Error() 49 | { 50 | var error = Marshal.GetLastWin32Error(); 51 | if (error != 0) throw new Win32Exception(error); 52 | } 53 | 54 | private static bool IsVsVersion(string arg) 55 | { 56 | decimal v; 57 | return decimal.TryParse(arg, NumberStyles.Number, CultureInfo.InvariantCulture, out v); 58 | } 59 | } 60 | 61 | public class VsVersion 62 | { 63 | public string Version { get; } 64 | public string DevEnvPath { get; } 65 | 66 | public VsVersion(string vsVersion, string devenvPath) 67 | { 68 | Version = vsVersion; 69 | DevEnvPath = devenvPath; 70 | } 71 | } 72 | 73 | public class VsHive 74 | { 75 | public VsVersion VsVersion { get; } 76 | public string RootSuffix { get; } 77 | public bool IsMainHive { get; } 78 | 79 | public VsHive(VsVersion vsVersion, string rootSuffix) 80 | { 81 | VsVersion = vsVersion; 82 | RootSuffix = rootSuffix; 83 | IsMainHive = string.IsNullOrWhiteSpace(rootSuffix); 84 | } 85 | 86 | public bool Exists() 87 | { 88 | using (var hive = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\VisualStudio")) 89 | { 90 | if (hive == null) 91 | return false; 92 | 93 | return hive.GetSubKeyNames().Contains(ToString()); 94 | } 95 | } 96 | 97 | public override string ToString() => $"{VsVersion.Version}{RootSuffix}"; 98 | } 99 | } -------------------------------------------------------------------------------- /src/Shell.Interop/Interop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | // ReSharper disable CheckNamespace 5 | // These types don't have a publicly available PIA, so I need to make one myself 6 | 7 | [assembly: PrimaryInteropAssembly(14, 0)] 8 | 9 | namespace Microsoft.Internal.VisualStudio.Shell.Interop 10 | { 11 | [Guid("753E55C6-E779-4A7A-BCD1-FD87181D52C0"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 12 | [ComImport] 13 | public interface IVsExtensionManagerPrivate 14 | { 15 | [PreserveSig] 16 | int GetEnabledExtensionContentLocations([MarshalAs(UnmanagedType.LPWStr)] [In] string szContentTypeName, [In] uint cContentLocations, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 1)] [Out] string[] rgbstrContentLocations, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 1)] [Out] string[] rgbstrUniqueExtensionStrings, out uint pcContentLocations); 17 | [PreserveSig] 18 | int GetEnabledExtensionContentLocationsWithNames([MarshalAs(UnmanagedType.LPWStr)] [In] string szContentTypeName, [In] uint cContentLocations, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 1)] [Out] string[] rgbstrContentLocations, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 1)] [Out] string[] rgbstrUniqueExtensionStrings, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 1)] [Out] string[] rgbstrExtensionNames, out uint pcContentLocations); 19 | [PreserveSig] 20 | int GetDisabledExtensionContentLocations([MarshalAs(UnmanagedType.LPWStr)] [In] string szContentTypeName, [In] uint cContentLocations, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 1)] [Out] string[] rgbstrContentLocations, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.BStr, SizeParamIndex = 1)] [Out] string[] rgbstrUniqueExtensionStrings, out uint pcContentLocations); 21 | [PreserveSig] 22 | int GetLastConfigurationChange([MarshalAs(UnmanagedType.LPArray)] [Out] DateTime[] pTimestamp); 23 | [PreserveSig] 24 | int LogAllInstalledExtensions(); 25 | [PreserveSig] 26 | int GetUniqueExtensionString([MarshalAs(UnmanagedType.LPWStr)] [In] string szExtensionIdentifier, [MarshalAs(UnmanagedType.BStr)] out string pbstrUniqueString); 27 | } 28 | [Guid("6B741746-E3C9-434A-9E20-6E330D88C7F6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 29 | [ComImport] 30 | public interface IVsExtensionManagerPrivate2 31 | { 32 | void GetAssetProperties([MarshalAs(UnmanagedType.LPWStr)] [In] string szAssetTypeName, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] out Array prgsaNames, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] out Array prgsaVersions, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] out Array prgsaAuthors, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] out Array prgsaExtensionIDs); 33 | void GetExtensionProperties([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] out Array prgsaNames, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] out Array prgsaVersions, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] out Array prgsaAuthors, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] out Array prgsaContentLocations, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] out Array prgsaExtensionIDs); 34 | ulong GetLastWriteTime([MarshalAs(UnmanagedType.LPWStr)] [In] string szContentTypeName); 35 | } 36 | } -------------------------------------------------------------------------------- /src/HostProcess/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using Microsoft.VisualStudio.ExtensionManager; 4 | using Microsoft.VisualStudio.Settings; 5 | 6 | namespace HostProcess 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | Log("Starting HostProcess..."); 13 | 14 | if (args.Length < 2) 15 | { 16 | LogError($"Invalid number of arguments. Expected 3 but was {args.Length}: {string.Join(", ", args)}"); 17 | Environment.Exit(-1); 18 | } 19 | 20 | string vsixPath = args[0]; 21 | string devenvPath = args[1]; 22 | string hive = args.Length == 3 ? args[2] : null; 23 | 24 | try 25 | { 26 | IInstallableExtension vsix = ExtensionManagerService.CreateInstallableExtension(vsixPath); 27 | 28 | if (string.IsNullOrWhiteSpace(hive)) 29 | hive = ""; 30 | 31 | PerformInstallation(vsix, devenvPath, hive); 32 | } 33 | catch (Exception) 34 | { 35 | Environment.Exit(-1); 36 | } 37 | 38 | Log("Exiting HostProcess..."); 39 | } 40 | 41 | private static IInstalledExtension PerformInstallation(IInstallableExtension vsix, string devenvPath, string hive) 42 | { 43 | IInstalledExtension extension; 44 | 45 | using (var settingsManager = ExternalSettingsManager.CreateForApplication(devenvPath, hive)) 46 | { 47 | var identifier = vsix.Header.Identifier; 48 | var name = vsix.Header.Name; 49 | 50 | Log($"Preparing to install '{name}' with identifier '{identifier}' into '{hive}'"); 51 | 52 | var extensionManager = new ExtensionManagerService(settingsManager); 53 | 54 | if (extensionManager.TryGetInstalledExtension(identifier, out extension)) 55 | { 56 | Log($"Extension '{name}' was already installed. Uninstalling..."); 57 | try 58 | { 59 | extensionManager.Uninstall(extension); 60 | extensionManager.CommitExternalUninstall(extension); 61 | } 62 | catch (Exception ex) 63 | { 64 | LogError($"An error ocurred while trying to uninstall '{name}'. Rolling back...", ex); 65 | RevertUninstall(extensionManager, extension); 66 | throw; 67 | } 68 | } 69 | try 70 | { 71 | Log($"Starting installation of '{name}'"); 72 | extensionManager.Install(vsix, perMachine: false); 73 | extension = extensionManager.GetInstalledExtension(identifier); 74 | Log($"Installation of '{name}' into '{hive}' completed successfully."); 75 | } 76 | catch (Exception ex) 77 | { 78 | LogError($"An error ocurred while trying to install '{name}'. Rolling back...", ex); 79 | RevertUninstall(extensionManager, extension); 80 | throw; 81 | } 82 | } 83 | 84 | return extension; 85 | } 86 | 87 | private static void Log(string message) 88 | { 89 | Console.Out.WriteLine(message); 90 | } 91 | 92 | private static void LogError(string message, Exception ex = null) 93 | { 94 | Console.Error.WriteLine(message); 95 | if (ex != null) 96 | { 97 | Console.Error.WriteLine(ex); 98 | } 99 | } 100 | 101 | private static void RevertUninstall(ExtensionManagerService extensionManager, IInstalledExtension oldExtension) 102 | { 103 | if (oldExtension == null || extensionManager.IsInstalled(oldExtension)) 104 | return; 105 | 106 | Log($"Reverting uninstall of '{oldExtension.Header.Name}'..."); 107 | extensionManager.RevertUninstall(oldExtension); 108 | } 109 | 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/VSIXPowerToys/DistinctBy.cs: -------------------------------------------------------------------------------- 1 | #region License and Terms 2 | // MoreLINQ - Extensions to LINQ to Objects 3 | // Copyright (c) 2008 Jonathan Skeet. All rights reserved. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | #endregion 17 | 18 | using System; 19 | using System.Collections.Generic; 20 | 21 | #if NO_HASHSET 22 | using System.Linq; 23 | #endif 24 | 25 | namespace VSIXPowerToys 26 | { 27 | static partial class MoreEnumerable 28 | { 29 | /// 30 | /// Returns all distinct elements of the given source, where "distinctness" 31 | /// is determined via a projection and the default equality comparer for the projected type. 32 | /// 33 | /// 34 | /// This operator uses deferred execution and streams the results, although 35 | /// a set of already-seen keys is retained. If a key is seen multiple times, 36 | /// only the first element with that key is returned. 37 | /// 38 | /// Type of the source sequence 39 | /// Type of the projected element 40 | /// Source sequence 41 | /// Projection for determining "distinctness" 42 | /// A sequence consisting of distinct elements from the source sequence, 43 | /// comparing them by the specified key projection. 44 | 45 | public static IEnumerable DistinctBy(this IEnumerable source, 46 | Func keySelector) 47 | { 48 | return source.DistinctBy(keySelector, null); 49 | } 50 | 51 | /// 52 | /// Returns all distinct elements of the given source, where "distinctness" 53 | /// is determined via a projection and the specified comparer for the projected type. 54 | /// 55 | /// 56 | /// This operator uses deferred execution and streams the results, although 57 | /// a set of already-seen keys is retained. If a key is seen multiple times, 58 | /// only the first element with that key is returned. 59 | /// 60 | /// Type of the source sequence 61 | /// Type of the projected element 62 | /// Source sequence 63 | /// Projection for determining "distinctness" 64 | /// The equality comparer to use to determine whether or not keys are equal. 65 | /// If null, the default equality comparer for TSource is used. 66 | /// A sequence consisting of distinct elements from the source sequence, 67 | /// comparing them by the specified key projection. 68 | 69 | public static IEnumerable DistinctBy(this IEnumerable source, 70 | Func keySelector, IEqualityComparer comparer) 71 | { 72 | if (source == null) throw new ArgumentNullException("source"); 73 | if (keySelector == null) throw new ArgumentNullException("keySelector"); 74 | return DistinctByImpl(source, keySelector, comparer); 75 | } 76 | 77 | private static IEnumerable DistinctByImpl(IEnumerable source, 78 | Func keySelector, IEqualityComparer comparer) 79 | { 80 | #if !NO_HASHSET 81 | var knownKeys = new HashSet(comparer); 82 | foreach (var element in source) 83 | { 84 | if (knownKeys.Add(keySelector(element))) 85 | { 86 | yield return element; 87 | } 88 | } 89 | #else 90 | // 91 | // On platforms where LINQ is available but no HashSet 92 | // (like on Silverlight), implement this operator using 93 | // existing LINQ operators. Using GroupBy is slightly less 94 | // efficient since it has do all the grouping work before 95 | // it can start to yield any one element from the source. 96 | // 97 | 98 | return source.GroupBy(keySelector, comparer).Select(g => g.First()); 99 | #endif 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /src/VSIXPowerToys/VSIXPowerToys.Shell.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C1EA0E01-E032-4478-BC78-6F50BE3E3D87} 8 | Library 9 | Properties 10 | VSIXPowerToys 11 | VSIXPowerToys.Shell 12 | v4.5.2 13 | 512 14 | 15 | 16 | 17 | 18 | 19 | true 20 | full 21 | false 22 | ..\..\bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | AnyCPU 27 | 28 | 29 | pdbonly 30 | true 31 | ..\..\bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | AnyCPU 36 | 37 | 38 | 39 | 40 | 41 | true 42 | 43 | 44 | Properties\sorry_but_I_have_to.snk 45 | 46 | 47 | 48 | False 49 | $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.ExtensionManager.dll 50 | False 51 | 52 | 53 | False 54 | $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.ExtensionManager.Implementation.dll 55 | False 56 | 57 | 58 | 59 | 60 | ..\packages\SharpShell.2.2.0.0\lib\net40\SharpShell.dll 61 | True 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | PreserveNewest 82 | 83 | 84 | 85 | 86 | {1a7318f0-8180-49b3-ac2c-b04f9ebc58ea} 87 | Shell.Interop 88 | 89 | 90 | 91 | 92 | 93 | 94 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /src/VSIXPowerToys/VsixPowerToys.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | using System.Windows.Forms; 8 | using Microsoft.VisualStudio.ExtensionManager; 9 | using SharpShell.Attributes; 10 | using SharpShell.Diagnostics; 11 | using SharpShell.SharpContextMenu; 12 | 13 | namespace VSIXPowerToys 14 | { 15 | [ComVisible(true)] 16 | [DisplayName("VSIX PowerToys")] 17 | [COMServerAssociation(AssociationType.ClassOfExtension, ".vsix")] 18 | public class VsixPowerToys : SharpContextMenu 19 | { 20 | private IInstallableExtension vsix; 21 | 22 | protected override bool CanShowMenu() 23 | { 24 | var paths = SelectedItemPaths.ToArray(); 25 | if (paths.Length != 1 || 26 | Path.GetExtension(paths[0]).ToLowerInvariant() != ".vsix") 27 | return false; 28 | 29 | try 30 | { 31 | vsix = ExtensionManagerService.CreateInstallableExtension(paths[0]); 32 | } 33 | catch (Exception ex) 34 | { 35 | LogError($"Unable to load the extension '{paths[0]}'", ex); 36 | return false; 37 | } 38 | 39 | return vsix != null; 40 | } 41 | 42 | protected override ContextMenuStrip CreateMenu() 43 | { 44 | var menuStrip = new ContextMenuStrip(); 45 | var install = new ToolStripMenuItem("Install into"); 46 | CreateInstallItems(install); 47 | menuStrip.Items.Add(install); 48 | 49 | var topLevel = new ToolStripMenuItem("VSIX PowerToys"); 50 | 51 | var copyVsix = new ToolStripMenuItem("Copy VSIX ID to Clipboard"); 52 | copyVsix.Click += (sender, args) => CopyVsixIdToClipboard(); 53 | topLevel.DropDownItems.Add(copyVsix); 54 | 55 | menuStrip.Items.Add(topLevel); 56 | return menuStrip; 57 | } 58 | 59 | private void CreateInstallItems(ToolStripMenuItem root) 60 | { 61 | //var value = new ToolStripMenuItem("Custom..."); 62 | //root.DropDownItems.Add(value); 63 | 64 | var vsVersions = VsUtils.GetInstalledVisualStudioVersions().Where(IsSupportedByTarget).ToList(); 65 | foreach (var vsVersion in vsVersions) 66 | { 67 | if (root.HasDropDownItems && !(root.DropDownItems[root.DropDownItems.Count - 1] is ToolStripSeparator)) 68 | { 69 | root.DropDownItems.Add(new ToolStripSeparator()); 70 | } 71 | 72 | var hives = VsUtils.GetAllHives(vsVersion).Where(hive => hive.Exists()); 73 | 74 | foreach (var hive in hives) 75 | { 76 | var item = new ToolStripMenuItem(hive.ToString(), null, (sender, args) => InstallVsix(hive)); 77 | root.DropDownItems.Add(item); 78 | } 79 | } 80 | 81 | if (!root.HasDropDownItems) 82 | { 83 | root.DropDownItems.Add(new ToolStripMenuItem("(No compatible Visual Studio editions found)") { Enabled = false }); 84 | } 85 | } 86 | 87 | private void InstallVsix(VsHive hive) 88 | { 89 | if (vsix.Header.AllUsers && !hive.IsMainHive) 90 | { 91 | if (MessageBox.Show("This extension is configured to be installed for all users, " + 92 | $"and will be installed in the per-machine location (Common7\\IDE\\Extensions) instead of {hive}.\n\n" + 93 | "Proceed with installation?", "VSIX PowerToys", MessageBoxButtons.OKCancel, 94 | MessageBoxIcon.Warning) != DialogResult.OK) 95 | { 96 | return; 97 | } 98 | } 99 | 100 | SystemCursor.SetSystemCursor(Cursors.WaitCursor); 101 | bool result = false; 102 | try 103 | { 104 | result = PerformInstallation(hive); 105 | } 106 | catch (Exception ex) 107 | { 108 | LogError("An error ocurred while installing the extension", ex); 109 | Debugger.Launch(); 110 | } 111 | finally 112 | { 113 | SystemCursor.RestoreSystemCursor(); 114 | } 115 | 116 | if (result) 117 | { 118 | MessageBox.Show($"Installation of '{vsix.Header.Name}' into '{hive}' was successful!", 119 | "VSIX PowerToys", 120 | MessageBoxButtons.OK, 121 | MessageBoxIcon.Information); 122 | } 123 | else 124 | { 125 | MessageBox.Show($"Installation of '{vsix.Header.Name}' into '{hive}' was unsuccessful.\n\n" + 126 | "Please check the log file at %TEMP%\\VSIXPowerToys.log", 127 | "VSIX PowerToys", 128 | MessageBoxButtons.OK, 129 | MessageBoxIcon.Warning); 130 | } 131 | } 132 | 133 | private bool PerformInstallation(VsHive hive) 134 | { 135 | var codebase = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath; 136 | var hostProcess = Path.Combine(Path.GetDirectoryName(codebase), "HostProcess.exe"); 137 | var arguments = $"\"{vsix.PackagePath}\" \"{hive.VsVersion.DevEnvPath}\" {hive.RootSuffix}"; 138 | Log($"Executing '{hostProcess} {arguments}'"); 139 | 140 | var p = new Process 141 | { 142 | StartInfo = 143 | { 144 | FileName = hostProcess, 145 | Arguments = arguments, 146 | RedirectStandardOutput = true, 147 | RedirectStandardError = true, 148 | CreateNoWindow = true, 149 | UseShellExecute = false 150 | } 151 | }; 152 | p.OutputDataReceived += (sender, args) => 153 | { 154 | if (!string.IsNullOrWhiteSpace(args.Data)) Log(args.Data); 155 | }; 156 | p.ErrorDataReceived += (sender, args) => 157 | { 158 | if (!string.IsNullOrWhiteSpace(args.Data)) LogError(args.Data); 159 | }; 160 | 161 | try 162 | { 163 | p.Start(); 164 | p.BeginOutputReadLine(); 165 | p.BeginErrorReadLine(); 166 | 167 | p.WaitForExit(); 168 | return p.ExitCode == 0; 169 | } 170 | catch (Exception ex) 171 | { 172 | LogError($"An error ocurred while executing HostProcess with arguments '{p.StartInfo.Arguments}'", ex); 173 | return false; 174 | } 175 | finally 176 | { 177 | p.Dispose(); 178 | } 179 | } 180 | 181 | private bool IsSupportedByTarget(VsVersion vsVersion) 182 | { 183 | // todo add support for specific productIds 184 | return vsix.Targets.DistinctBy(req => req.VersionRange) 185 | .Any(target => target.VersionRange.Contains(Version.Parse(vsVersion.Version))); 186 | } 187 | 188 | private void CopyVsixIdToClipboard() 189 | { 190 | Clipboard.SetText(vsix.Header.Identifier); 191 | } 192 | } 193 | } -------------------------------------------------------------------------------- /src/HostProcess/HostProcess.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {43E348CA-653A-4FD1-8215-0FE9BD3BDD25} 8 | Exe 9 | Properties 10 | HostProcess 11 | HostProcess 12 | v4.5.2 13 | 512 14 | true 15 | 16 | 17 | x86 18 | true 19 | full 20 | false 21 | ..\..\bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | x86 28 | pdbonly 29 | true 30 | ..\..\bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | false 35 | 36 | 37 | 38 | False 39 | $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.ExtensionManager.dll 40 | False 41 | 42 | 43 | False 44 | $(DevEnvDir)\PrivateAssemblies\Microsoft.VisualStudio.ExtensionManager.Implementation.dll 45 | False 46 | 47 | 48 | ..\packages\Microsoft.VisualStudio.Imaging.14.1.24720\lib\net45\Microsoft.VisualStudio.Imaging.dll 49 | False 50 | 51 | 52 | ..\packages\Microsoft.VisualStudio.OLE.Interop.7.10.6070\lib\Microsoft.VisualStudio.OLE.Interop.dll 53 | False 54 | 55 | 56 | ..\packages\Microsoft.VisualStudio.Settings.14.0.14.1.24720\lib\Microsoft.VisualStudio.Settings.14.0.dll 57 | False 58 | 59 | 60 | ..\packages\Microsoft.VisualStudio.Shell.14.0.14.1.24720\lib\Microsoft.VisualStudio.Shell.14.0.dll 61 | False 62 | 63 | 64 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.10.0.10.0.30319\lib\net40\Microsoft.VisualStudio.Shell.Immutable.10.0.dll 65 | False 66 | 67 | 68 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.11.0.11.0.50727\lib\net45\Microsoft.VisualStudio.Shell.Immutable.11.0.dll 69 | False 70 | 71 | 72 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.12.0.12.0.21003\lib\net45\Microsoft.VisualStudio.Shell.Immutable.12.0.dll 73 | False 74 | 75 | 76 | ..\packages\Microsoft.VisualStudio.Shell.Immutable.14.0.14.1.24720\lib\net45\Microsoft.VisualStudio.Shell.Immutable.14.0.dll 77 | False 78 | 79 | 80 | ..\packages\Microsoft.VisualStudio.Shell.Interop.7.10.6071\lib\Microsoft.VisualStudio.Shell.Interop.dll 81 | False 82 | 83 | 84 | ..\packages\Microsoft.VisualStudio.Shell.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.Shell.Interop.8.0.dll 85 | False 86 | 87 | 88 | ..\packages\Microsoft.VisualStudio.Shell.Interop.9.0.9.0.30729\lib\Microsoft.VisualStudio.Shell.Interop.9.0.dll 89 | False 90 | 91 | 92 | ..\packages\Microsoft.VisualStudio.TextManager.Interop.7.10.6070\lib\Microsoft.VisualStudio.TextManager.Interop.dll 93 | False 94 | 95 | 96 | ..\packages\Microsoft.VisualStudio.TextManager.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.TextManager.Interop.8.0.dll 97 | False 98 | 99 | 100 | ..\packages\Microsoft.VisualStudio.Threading.14.1.114\lib\net45\Microsoft.VisualStudio.Threading.dll 101 | False 102 | 103 | 104 | ..\packages\Microsoft.VisualStudio.Utilities.14.1.24720\lib\net45\Microsoft.VisualStudio.Utilities.dll 105 | False 106 | 107 | 108 | ..\packages\Microsoft.VisualStudio.Validation.14.1.111\lib\net45\Microsoft.VisualStudio.Validation.dll 109 | False 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | {1a7318f0-8180-49b3-ac2c-b04f9ebc58ea} 126 | Shell.Interop 127 | 128 | 129 | 130 | 137 | -------------------------------------------------------------------------------- /src/VSIXPowerToys/SystemCursor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Runtime.InteropServices; 4 | using System.Windows.Forms; 5 | 6 | namespace VSIXPowerToys 7 | { 8 | internal sealed class SystemCursor 9 | { 10 | internal enum OCR_SYSTEM_CURSORS : uint 11 | { 12 | /// 13 | /// Standard arrow 14 | /// 15 | OCR_NORMAL = 32512, 16 | /// 17 | /// I-beam 18 | /// 19 | OCR_IBEAM = 32513, 20 | /// 21 | /// Hourglass 22 | /// 23 | OCR_WAIT = 32514, 24 | /// 25 | /// Crosshair 26 | /// 27 | OCR_CROSS = 32515, 28 | /// 29 | /// Vertical arrow 30 | /// 31 | OCR_UP = 32516, 32 | OCR_SIZE = 32640, 33 | OCR_ICON = 32641, 34 | /// 35 | /// Double-pointed arrow pointing northwest and southeast 36 | /// 37 | OCR_SIZENWSE = 32642, 38 | /// 39 | /// Double-pointed arrow pointing northeast and southwest 40 | /// 41 | OCR_SIZENESW = 32643, 42 | /// 43 | /// Double-pointed arrow pointing west and east 44 | /// 45 | OCR_SIZEWE = 32644, 46 | /// 47 | /// Double-pointed arrow pointing north and south 48 | /// 49 | OCR_SIZENS = 32645, 50 | /// 51 | /// Four-pointed arrow pointing north, south, east, and west 52 | /// 53 | OCR_SIZEALL = 32646, 54 | /// 55 | /// Slashed circle 56 | /// 57 | OCR_NO = 32648, 58 | /// 59 | /// Windows 2000/XP: Hand 60 | /// 61 | OCR_HAND = 32649, 62 | /// 63 | /// Standard arrow and small hourglass 64 | /// 65 | OCR_APPSTARTING = 32650, 66 | /// 67 | /// Arrow and question mark 68 | /// 69 | OCR_HELP = 32651 70 | } 71 | 72 | internal enum GetClassLongIndex 73 | { 74 | GCL_CBCLSEXTRA = -20, 75 | GCL_CBWNDEXTRA = -18, 76 | GCL_HBRBACKGROUND = -10, 77 | GCL_HCURSOR = -12, 78 | GCL_HICON = -14, 79 | GCL_HMODULE = -16, 80 | GCL_MENUNAME = -8, 81 | GCL_STYLE = -26, 82 | GCL_WNDPROC = -24 83 | } 84 | 85 | [StructLayout(LayoutKind.Sequential)] 86 | struct CURSORINFO 87 | { 88 | public Int32 cbSize; // Specifies the size, in bytes, of the structure. 89 | // The caller must set this to Marshal.SizeOf(typeof(CURSORINFO)). 90 | public Int32 flags; // Specifies the cursor state. This parameter can be one of the following values: 91 | // 0 The cursor is hidden. 92 | // 0x00000001 The cursor is showing. 93 | public IntPtr hCursor; // Handle to the cursor. 94 | public Point ptScreenPos; // A POINT structure that receives the screen coordinates of the cursor. 95 | } 96 | 97 | internal enum FUFlags : uint 98 | { 99 | /// 100 | /// The default flag; it does nothing. All it means is "not LR_MONOCHROME". 101 | /// 102 | LR_DEFAULTCOLOR = 0x0000, 103 | 104 | /// 105 | /// Loads the image in black and white. 106 | /// 107 | LR_MONOCHROME = 0x0001, 108 | 109 | /// 110 | /// Returns the original hImage if it satisfies the criteria for the copy—that is, correct dimensions and color depth—in 111 | /// which case the LR_COPYDELETEORG flag is ignored. If this flag is not specified, a new object is always created. 112 | /// 113 | LR_COPYRETURNORG = 0x0004, 114 | 115 | /// 116 | /// Deletes the original image after creating the copy. 117 | /// 118 | LR_COPYDELETEORG = 0x0008, 119 | 120 | /// 121 | /// Specifies the image to load. If the hinst parameter is non-NULL and the fuLoad parameter omits LR_LOADFROMFILE, 122 | /// lpszName specifies the image resource in the hinst module. If the image resource is to be loaded by name, 123 | /// the lpszName parameter is a pointer to a null-terminated string that contains the name of the image resource. 124 | /// If the image resource is to be loaded by ordinal, use the MAKEINTRESOURCE macro to convert the image ordinal 125 | /// into a form that can be passed to the LoadImage function. 126 | /// 127 | /// If the hinst parameter is NULL and the fuLoad parameter omits the LR_LOADFROMFILE value, 128 | /// the lpszName specifies the OEM image to load. The OEM image identifiers are defined in Winuser.h and have the following prefixes. 129 | /// 130 | /// OBM_ OEM bitmaps 131 | /// OIC_ OEM icons 132 | /// OCR_ OEM cursors 133 | /// 134 | /// To pass these constants to the LoadImage function, use the MAKEINTRESOURCE macro. For example, to load the OCR_NORMAL cursor, 135 | /// pass MAKEINTRESOURCE(OCR_NORMAL) as the lpszName parameter and NULL as the hinst parameter. 136 | /// 137 | /// If the fuLoad parameter includes the LR_LOADFROMFILE value, lpszName is the name of the file that contains the image. 138 | /// 139 | LR_LOADFROMFILE = 0x0010, 140 | 141 | /// 142 | /// Retrieves the color value of the first pixel in the image and replaces the corresponding entry in the color table 143 | /// with the default window color (COLOR_WINDOW). All pixels in the image that use that entry become the default window color. 144 | /// This value applies only to images that have corresponding color tables. 145 | /// Do not use this option if you are loading a bitmap with a color depth greater than 8bpp. 146 | /// 147 | /// If fuLoad includes both the LR_LOADTRANSPARENT and LR_LOADMAP3DCOLORS values, LRLOADTRANSPARENT takes precedence. 148 | /// However, the color table entry is replaced with COLOR_3DFACE rather than COLOR_WINDOW. 149 | /// 150 | LR_LOADTRANSPARENT = 0x0020, 151 | 152 | /// 153 | /// Uses the width or height specified by the system metric values for cursors or icons, 154 | /// if the cxDesired or cyDesired values are set to zero. If this flag is not specified and cxDesired and cyDesired are set to zero, 155 | /// the function uses the actual resource size. If the resource contains multiple images, the function uses the size of the first image. 156 | /// 157 | LR_DEFAULTSIZE = 0x0040, 158 | 159 | /// 160 | /// Uses true VGA colors. 161 | /// 162 | LR_VGACOLOR = 0x0080, 163 | 164 | /// 165 | /// Searches the color table for the image and replaces the following shades of gray with the corresponding 3-D color: Color Replaced with 166 | /// Dk Gray, RGB(128,128,128) COLOR_3DSHADOW 167 | /// Gray, RGB(192,192,192) COLOR_3DFACE 168 | /// Lt Gray, RGB(223,223,223) COLOR_3DLIGHT 169 | /// Do not use this option if you are loading a bitmap with a color depth greater than 8bpp. 170 | /// 171 | LR_LOADMAP3DCOLORS = 0x1000, 172 | 173 | /// 174 | /// When the uType parameter specifies IMAGE_BITMAP, causes the function to return a DIB section bitmap rather than a compatible bitmap. 175 | /// This flag is useful for loading a bitmap without mapping it to the colors of the display device. 176 | /// 177 | LR_CREATEDIBSECTION = 0x2000, 178 | 179 | /// 180 | /// Tries to reload an icon or cursor resource from the original resource file rather than simply copying the current image. 181 | /// This is useful for creating a different-sized copy when the resource file contains multiple sizes of the resource. 182 | /// Without this flag, CopyImage stretches the original image to the new size. If this flag is set, CopyImage uses the size 183 | /// in the resource file closest to the desired size. This will succeed only if hImage was loaded by LoadIcon or LoadCursor, 184 | /// or by LoadImage with the LR_SHARED flag. 185 | /// 186 | LR_COPYFROMRESOURCE = 0x4000, 187 | 188 | /// 189 | /// Shares the image handle if the image is loaded multiple times. If LR_SHARED is not set, a second call to LoadImage for the 190 | /// same resource will load the image again and return a different handle. 191 | /// When you use this flag, the system will destroy the resource when it is no longer needed. 192 | /// 193 | /// Do not use LR_SHARED for images that have non-standard sizes, that may change after loading, or that are loaded from a file. 194 | /// 195 | /// When loading a system icon or cursor, you must use LR_SHARED or the function will fail to load the resource. 196 | /// 197 | /// Windows 95/98/Me: The function finds the first image with the requested resource name in the cache, regardless of the size requested. 198 | /// 199 | LR_SHARED = 0x8000 200 | } 201 | 202 | internal enum CopyImageType : uint 203 | { 204 | /// 205 | /// Loads a bitmap. 206 | /// 207 | IMAGE_BITMAP = 0, 208 | 209 | /// 210 | /// Loads an icon. 211 | /// 212 | IMAGE_ICON = 1, 213 | 214 | /// 215 | /// Loads a cursor. 216 | /// 217 | IMAGE_CURSOR = 2, 218 | 219 | /// 220 | /// Loads an enhanced metafile. 221 | /// 222 | IMAGE_ENHMETAFILE = 3 223 | } 224 | 225 | private static OCR_SYSTEM_CURSORS _previousCursorType; 226 | private static IntPtr _previousCursor; 227 | 228 | public static void SetSystemCursor(Cursor cursor) 229 | { 230 | // Get previous cursor 231 | var pci = new CURSORINFO(); 232 | pci.cbSize = Marshal.SizeOf(typeof(CURSORINFO)); 233 | GetCursorInfo(out pci); 234 | 235 | // Get previous cursor type (the one to be replaced) 236 | _previousCursorType = GetCursorType(pci); 237 | 238 | // Copy previous cursor because next SetSystemCursor might destroy it 239 | _previousCursor = CopyImage(pci.hCursor, 240 | (uint)CopyImageType.IMAGE_CURSOR, 241 | Cursors.Default.Size.Width, 242 | Cursors.Default.Size.Height, 243 | (uint)FUFlags.LR_COPYFROMRESOURCE); 244 | 245 | // Copy the cursor to set 246 | IntPtr newCursor = CopyImage(cursor.Handle, 247 | (uint)CopyImageType.IMAGE_CURSOR, 248 | cursor.Size.Width, 249 | cursor.Size.Height, 250 | (uint)FUFlags.LR_COPYFROMRESOURCE); 251 | 252 | // Set the cursor in replacement of the current one 253 | bool r = SetSystemCursor(newCursor, (uint)_previousCursorType); 254 | 255 | if (!r) 256 | Console.WriteLine("Error: " + Marshal.GetLastWin32Error()); 257 | } 258 | 259 | public static void RestoreSystemCursor() 260 | { 261 | bool r = SetSystemCursor(_previousCursor, (uint)_previousCursorType); 262 | 263 | if (!r) 264 | Console.WriteLine("Error: " + Marshal.GetLastWin32Error()); 265 | } 266 | 267 | private static OCR_SYSTEM_CURSORS GetCursorType(CURSORINFO pci) 268 | { 269 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_APPSTARTING)) 270 | return OCR_SYSTEM_CURSORS.OCR_APPSTARTING; 271 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_CROSS)) 272 | return OCR_SYSTEM_CURSORS.OCR_CROSS; 273 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_HAND)) 274 | return OCR_SYSTEM_CURSORS.OCR_HAND; 275 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_HELP)) 276 | return OCR_SYSTEM_CURSORS.OCR_HELP; 277 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_IBEAM)) 278 | return OCR_SYSTEM_CURSORS.OCR_IBEAM; 279 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_ICON)) 280 | return OCR_SYSTEM_CURSORS.OCR_ICON; 281 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_NO)) 282 | return OCR_SYSTEM_CURSORS.OCR_NO; 283 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_NORMAL)) 284 | return OCR_SYSTEM_CURSORS.OCR_NORMAL; 285 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_SIZE)) 286 | return OCR_SYSTEM_CURSORS.OCR_SIZE; 287 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_SIZEALL)) 288 | return OCR_SYSTEM_CURSORS.OCR_SIZEALL; 289 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_SIZENESW)) 290 | return OCR_SYSTEM_CURSORS.OCR_SIZENESW; 291 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_SIZENS)) 292 | return OCR_SYSTEM_CURSORS.OCR_SIZENS; 293 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_SIZENWSE)) 294 | return OCR_SYSTEM_CURSORS.OCR_SIZENWSE; 295 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_SIZEWE)) 296 | return OCR_SYSTEM_CURSORS.OCR_SIZEWE; 297 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_UP)) 298 | return OCR_SYSTEM_CURSORS.OCR_UP; 299 | if (pci.hCursor == LoadCursor(IntPtr.Zero, (int)OCR_SYSTEM_CURSORS.OCR_WAIT)) 300 | return OCR_SYSTEM_CURSORS.OCR_WAIT; 301 | 302 | // If the cursor has not been recognized, use the NORMAL/DEFAULT one 303 | return OCR_SYSTEM_CURSORS.OCR_NORMAL; 304 | } 305 | 306 | #region Native imports 307 | 308 | [DllImport("user32.dll")] 309 | static extern bool SetSystemCursor(IntPtr hcur, uint id); 310 | 311 | [DllImport("user32.dll")] 312 | static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName); 313 | 314 | [DllImport("user32.dll")] 315 | static extern IntPtr GetCursor(); 316 | 317 | [DllImport("user32.dll")] 318 | static extern IntPtr SetCursor(IntPtr hCursor); 319 | 320 | [DllImport("user32.dll")] 321 | static extern int ShowCursor(bool bShow); 322 | 323 | [DllImport("user32.dll")] 324 | static extern bool GetCursorInfo(out CURSORINFO pci); 325 | 326 | private static IntPtr SetClassLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong) 327 | { 328 | if (IntPtr.Size > 4) 329 | return SetClassLongPtr64(hWnd, nIndex, dwNewLong); 330 | 331 | return new IntPtr(SetClassLongPtr32(hWnd, nIndex, unchecked((uint)dwNewLong.ToInt32()))); 332 | } 333 | 334 | [DllImport("user32.dll", EntryPoint = "SetClassLong", SetLastError = true)] 335 | static extern uint SetClassLongPtr32(IntPtr hWnd, int nIndex, uint dwNewLong); 336 | 337 | [DllImport("user32.dll", EntryPoint = "SetClassLongPtr", SetLastError = true)] 338 | static extern IntPtr SetClassLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong); 339 | 340 | private static IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex) 341 | { 342 | if (IntPtr.Size > 4) 343 | return GetClassLongPtr64(hWnd, nIndex); 344 | else 345 | return new IntPtr(GetClassLongPtr32(hWnd, nIndex)); 346 | } 347 | 348 | [DllImport("user32.dll", EntryPoint = "GetClassLong", SetLastError = true)] 349 | static extern uint GetClassLongPtr32(IntPtr hWnd, int nIndex); 350 | 351 | [DllImport("user32.dll", EntryPoint = "GetClassLongPtr", SetLastError = true)] 352 | static extern IntPtr GetClassLongPtr64(IntPtr hWnd, int nIndex); 353 | 354 | [DllImport("user32.dll", EntryPoint = "CopyCursor")] 355 | static extern IntPtr CopyCursor(IntPtr hCursor); 356 | 357 | [DllImport("user32.dll", SetLastError = true)] 358 | static extern IntPtr CopyImage(IntPtr hImage, uint uType, int cxDesired, int cyDesired, uint fuFlags); 359 | 360 | #endregion 361 | } 362 | } --------------------------------------------------------------------------------