├── 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 | 
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 | 
12 |
13 | ### Copy VSIX ID to Clipboard
14 |
15 | Copies the VSIX manifest ID value to the clipboard.
16 |
17 | 
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 | }
--------------------------------------------------------------------------------