mirror of https://github.com/dahall/Vanara.git
Merged ComRegistrar into ShellRegistrar. Completed work to allow local registrations in ShellRegistrar.
parent
aa2b8dac98
commit
515bedc299
|
@ -1,56 +0,0 @@
|
|||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Vanara.Windows.Shell
|
||||
{
|
||||
/// <summary>A class to register COM objects.</summary>
|
||||
public static class ComRegistrar
|
||||
{
|
||||
/// <summary>Registers the specified type as a COM Local Server.</summary>
|
||||
/// <typeparam name="TComObject">The type of the COM object.</typeparam>
|
||||
/// <param name="pszFriendlyName">The friendly name of the COM object.</param>
|
||||
/// <param name="cmdLineArgs">The command line arguments to supply to the executable on startup.</param>
|
||||
/// <param name="assembly">The assembly used to get the full path of the executable. If this value is <see langword="null"/>, then the assembly of <typeparamref name="TComObject"/> will be used.</param>
|
||||
/// <param name="systemWide">If set to <see langword="true" />, the COM object is registered system-wide in HKLM; otherwise it is registered for the user only in HKCU.</param>
|
||||
/// <param name="appId">The AppId to relate to this CLSID. If <see langword="null"/>, the CLSID value will be used.</param>
|
||||
public static void RegisterLocalServer<TComObject>(string pszFriendlyName, string cmdLineArgs = null, Assembly assembly = null, bool systemWide = false, Guid? appId = null) where TComObject : ComObject
|
||||
{
|
||||
var cmdLine = (assembly ?? typeof(TComObject).Assembly).Location;
|
||||
var clsid = GetClsid<TComObject>();
|
||||
var _appId = appId ?? clsid;
|
||||
if (!(cmdLineArgs is null)) cmdLine += " " + cmdLineArgs;
|
||||
|
||||
using (var root = GetRoot(systemWide, true))
|
||||
using (root.CreateSubKey(@"AppID\" + _appId.ToRegString(), pszFriendlyName))
|
||||
using (var rClsid = root.CreateSubKey(@"CLSID\" + clsid.ToRegString(), pszFriendlyName))
|
||||
using (rClsid.CreateSubKey("LocalServer32", cmdLine))
|
||||
{
|
||||
rClsid.SetValue("AppId", _appId.ToRegString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Unregisters the COM Local Server.</summary>
|
||||
/// <typeparam name="TComObject">The type of the COM object.</typeparam>
|
||||
/// <param name="systemWide">If set to <see langword="true" />, the COM object is unregistered system-wide from HKLM; otherwise it is unregistered for the user only in HKCU.</param>
|
||||
/// <param name="appId">The AppId to relate to this CLSID. If <see langword="null"/>, the CLSID value will be used.</param>
|
||||
public static void UnregisterLocalServer<TComObject>(bool systemWide = false, Guid? appId = null) where TComObject : ComObject
|
||||
{
|
||||
var clsid = GetClsid<TComObject>();
|
||||
using (var root = GetRoot(systemWide, true))
|
||||
{
|
||||
root.DeleteSubKeyTree(@"AppID\" + (appId ?? clsid).ToRegString());
|
||||
root.DeleteSubKeyTree(@"CLSID\" + clsid.ToRegString());
|
||||
}
|
||||
}
|
||||
|
||||
internal static RegistryKey GetRoot(bool systemWide, bool writable, string subkey = null)
|
||||
{
|
||||
var key = systemWide ? Registry.LocalMachine : Registry.CurrentUser;
|
||||
return key.OpenSubKey(@"Software\Classes" + (subkey is null ? "" : "\\" + subkey), writable);
|
||||
}
|
||||
|
||||
private static Guid GetClsid<TComObject>() => Marshal.GenerateGuidForType(typeof(TComObject));
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using static Vanara.PInvoke.Shell32;
|
||||
|
@ -10,13 +12,13 @@ using static Vanara.PInvoke.ShlwApi;
|
|||
|
||||
namespace Vanara.Windows.Shell
|
||||
{
|
||||
/// <summary>Static class that has methods used to register and unregister shell items in the Windows Registry.</summary>
|
||||
/// <summary>Contains static methods used to register and unregister shell items in the Windows Registry.</summary>
|
||||
public static class ShellRegistrar
|
||||
{
|
||||
/*
|
||||
HRESULT RegisterAppDropTarget() const;
|
||||
|
||||
// create registry entries for drop target based static verb. the specified clsid will be
|
||||
// create registry entries for drop target based static verb. the specified CLSID will be
|
||||
|
||||
HRESULT RegisterCreateProcessVerb(PCWSTR pszProgID, PCWSTR pszVerb, PCWSTR pszCmdLine, PCWSTR pszVerbDisplayName) const;
|
||||
HRESULT RegisterDropTargetVerb(PCWSTR pszProgID, PCWSTR pszVerb, PCWSTR pszVerbDisplayName) const;
|
||||
|
@ -61,19 +63,66 @@ namespace Vanara.Windows.Shell
|
|||
|
||||
*/
|
||||
|
||||
/// <summary>Gets the file extensions associated with a given ProgID.</summary>
|
||||
/// <param name="progId">The ProgID.</param>
|
||||
/// <returns>An enumeration of file extensions in the form ".ext".</returns>
|
||||
public static IEnumerable<string> GetAssociatedFileExtensions(string progId) =>
|
||||
Registry.ClassesRoot.GetSubKeyNames().Where(n => n.StartsWith(".") && Registry.ClassesRoot.HasSubKey($"{n}\\{progId}"));
|
||||
GetRoot().GetSubKeyNames().Where(n => n.StartsWith(".") && Registry.ClassesRoot.HasSubKey($"{n}\\{progId}"));
|
||||
|
||||
public static void RegisterApplication(string fullExePath, bool userOnly = false, bool acceptsUrls = false, Guid? dropTarget = null, IndirectString friendlyName = null,
|
||||
IEnumerable<string> supportedTypes = null, IconLocation defaultIcon = null, bool noStartPage = false, IconLocation taskGroupIcon = null,
|
||||
bool useExecutableForTaskbarGroupIcon = false)
|
||||
/// <summary>Determines if the specified type is registered as a COM Local Server.</summary>
|
||||
/// <typeparam name="TComObject">The type of the COM object.</typeparam>
|
||||
/// <param name="assembly">
|
||||
/// The assembly used to get the full path of the executable. If this value is <see langword="null"/>, then the assembly of
|
||||
/// <typeparamref name="TComObject"/> will be used.
|
||||
/// </param>
|
||||
/// <param name="systemWide">
|
||||
/// If set to <see langword="true"/>, registration is checked in HKLM; otherwise it is registered for the user only
|
||||
/// in HKCU.
|
||||
/// </param>
|
||||
/// <param name="appId">The AppId to relate to this CLSID. If <see langword="null"/>, the CLSID value will be used.</param>
|
||||
public static bool IsRegisteredAsLocalServer<TComObject>(Assembly assembly = null, bool systemWide = false, Guid? appId = null) where TComObject : ComObject
|
||||
{
|
||||
var cmdLine = (assembly ?? typeof(TComObject).Assembly).Location;
|
||||
var clsid = GetClsid<TComObject>();
|
||||
var _appId = appId ?? clsid;
|
||||
|
||||
using (var root = GetRoot(systemWide, true))
|
||||
{
|
||||
if (!root.HasSubKey(@"CLSID\" + clsid.ToRegString())) return false;
|
||||
using (var rClsid = root.OpenSubKey(@"CLSID\" + clsid.ToRegString() + @"\LocalServer32"))
|
||||
if (rClsid == null || !string.Equals(rClsid.GetValue("")?.ToString(), cmdLine, StringComparison.OrdinalIgnoreCase)) return false;
|
||||
if (!root.HasSubKey(@"AppID\" + _appId.ToRegString())) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Registers the application.</summary>
|
||||
/// <param name="fullExePath">The full executable path.</param>
|
||||
/// <param name="userOnly">if set to <see langword="true" /> [user only].</param>
|
||||
/// <param name="acceptsUrls">if set to <see langword="true" /> [accepts urls].</param>
|
||||
/// <param name="dropTarget">The drop target.</param>
|
||||
/// <param name="friendlyName">Name of the friendly.</param>
|
||||
/// <param name="supportedTypes">The supported types.</param>
|
||||
/// <param name="defaultIcon">The default icon.</param>
|
||||
/// <param name="noStartPage">if set to <see langword="true" /> [no start page].</param>
|
||||
/// <param name="taskGroupIcon">The task group icon.</param>
|
||||
/// <param name="useExecutableForTaskbarGroupIcon">if set to <see langword="true" /> [use executable for taskbar group icon].</param>
|
||||
/// <exception cref="ArgumentNullException">fullExePath</exception>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Unable to create application key in the 'App Paths' subkey.
|
||||
/// or
|
||||
/// Unable to create application key in the HKCR\\Applications subkey.
|
||||
/// </exception>
|
||||
public static void RegisterApplication(string fullExePath, bool userOnly = false, bool acceptsUrls = false, Guid? dropTarget = null,
|
||||
IndirectString friendlyName = null, IEnumerable<string> supportedTypes = null, IconLocation defaultIcon = null, bool noStartPage = false,
|
||||
IconLocation taskGroupIcon = null, bool useExecutableForTaskbarGroupIcon = false)
|
||||
{
|
||||
if (fullExePath == null) throw new ArgumentNullException(nameof(fullExePath));
|
||||
fullExePath = Path.GetFullPath(fullExePath);
|
||||
var fn = Path.GetFileName(fullExePath).ToLower();
|
||||
|
||||
// Handle registrations in user or machine "App Paths"
|
||||
using (var reg = ComRegistrar.GetRoot(!userOnly, true, @"Software\Microsoft\Windows\CurrentVersion\App Paths"))
|
||||
using (var reg = GetRoot(!userOnly, true, @"Software\Microsoft\Windows\CurrentVersion\App Paths"))
|
||||
using (var sk = reg?.CreateSubKey(fn))
|
||||
{
|
||||
if (sk == null) throw new InvalidOperationException("Unable to create application key in the 'App Paths' subkey.");
|
||||
|
@ -95,7 +144,7 @@ namespace Vanara.Windows.Shell
|
|||
}
|
||||
|
||||
// Handle registrations in HKCR\Applications
|
||||
using (var reg = Registry.ClassesRoot.OpenSubKey(@"Applications"))
|
||||
using (var reg = GetRoot(!userOnly, true, @"Applications"))
|
||||
using (var sk = reg?.CreateSubKey(fn))
|
||||
{
|
||||
if (sk == null) throw new InvalidOperationException("Unable to create application key in the HKCR\\Applications subkey.");
|
||||
|
@ -118,6 +167,13 @@ namespace Vanara.Windows.Shell
|
|||
NotifyShell();
|
||||
}
|
||||
|
||||
/// <summary>Registers the command verb.</summary>
|
||||
/// <param name="parentKey">The parent key.</param>
|
||||
/// <param name="verb">The verb.</param>
|
||||
/// <param name="displayName">The display name.</param>
|
||||
/// <param name="command">The command.</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException">Unable to create required key in registry.</exception>
|
||||
public static CommandVerb RegisterCommandVerb(RegistryKey parentKey, string verb, string displayName = null, string command = null)
|
||||
{
|
||||
var vkey = parentKey.CreateSubKey("shell\\" + verb) ?? throw new InvalidOperationException("Unable to create required key in registry.");
|
||||
|
@ -128,13 +184,29 @@ namespace Vanara.Windows.Shell
|
|||
return v;
|
||||
}
|
||||
|
||||
public static void RegisterFileAssociation(string ext, string progId, PERCEIVED perceivedType = PERCEIVED.PERCEIVED_TYPE_UNSPECIFIED, string contentType = null)
|
||||
/// <summary>Registers the file association.</summary>
|
||||
/// <param name="ext">The ext.</param>
|
||||
/// <param name="progId">The ProgID.</param>
|
||||
/// <param name="perceivedType">Type of the perceived.</param>
|
||||
/// <param name="contentType">Type of the content.</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// ext
|
||||
/// or
|
||||
/// progId
|
||||
/// </exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Extension must start with a '.' - ext
|
||||
/// or
|
||||
/// Undefined ProgId value. - progId
|
||||
/// </exception>
|
||||
/// <exception cref="InvalidOperationException">Unable to create association key in the registry.</exception>
|
||||
public static void RegisterFileAssociation(string ext, string progId, PERCEIVED perceivedType = PERCEIVED.PERCEIVED_TYPE_UNSPECIFIED, string contentType = null, bool systemWide = false)
|
||||
{
|
||||
if (ext == null) throw new ArgumentNullException(nameof(ext));
|
||||
if (!ext.StartsWith(".")) throw new ArgumentException("Extension must start with a '.'", nameof(ext));
|
||||
if (progId == null) throw new ArgumentNullException(nameof(progId));
|
||||
if (!IsDefined(progId)) throw new ArgumentException("Undefined ProgId value.", nameof(progId));
|
||||
using (var pkey = Registry.ClassesRoot.CreateSubKey(ext))
|
||||
using (var pkey = GetRoot(systemWide, true).CreateSubKey(ext))
|
||||
{
|
||||
if (pkey == null) throw new InvalidOperationException("Unable to create association key in the registry.");
|
||||
pkey.SetValue(null, progId);
|
||||
|
@ -146,18 +218,62 @@ namespace Vanara.Windows.Shell
|
|||
NotifyShell();
|
||||
}
|
||||
|
||||
public static ProgId RegisterProgID(string progId, string typeName)
|
||||
/// <summary>Registers the specified type as a COM Local Server.</summary>
|
||||
/// <typeparam name="TComObject">The type of the COM object.</typeparam>
|
||||
/// <param name="pszFriendlyName">The friendly name of the COM object.</param>
|
||||
/// <param name="cmdLineArgs">The command line arguments to supply to the executable on startup.</param>
|
||||
/// <param name="assembly">
|
||||
/// The assembly used to get the full path of the executable. If this value is <see langword="null"/>, then the assembly of
|
||||
/// <typeparamref name="TComObject"/> will be used.
|
||||
/// </param>
|
||||
/// <param name="systemWide">
|
||||
/// If set to <see langword="true"/>, the COM object is registered system-wide in HKLM; otherwise it is registered for the user only
|
||||
/// in HKCU.
|
||||
/// </param>
|
||||
/// <param name="appId">The AppId to relate to this CLSID. If <see langword="null"/>, the CLSID value will be used.</param>
|
||||
public static void RegisterLocalServer<TComObject>(string pszFriendlyName, string cmdLineArgs = null, Assembly assembly = null, bool systemWide = false, Guid? appId = null) where TComObject : ComObject
|
||||
{
|
||||
var cmdLine = (assembly ?? typeof(TComObject).Assembly).Location;
|
||||
var clsid = GetClsid<TComObject>();
|
||||
var _appId = appId ?? clsid;
|
||||
if (!(cmdLineArgs is null)) cmdLine += " " + cmdLineArgs;
|
||||
|
||||
using (var root = GetRoot(systemWide, true))
|
||||
using (root.CreateSubKey(@"AppID\" + _appId.ToRegString(), pszFriendlyName))
|
||||
using (var rClsid = root.CreateSubKey(@"CLSID\" + clsid.ToRegString(), pszFriendlyName))
|
||||
using (rClsid.CreateSubKey("LocalServer32", cmdLine))
|
||||
{
|
||||
rClsid.SetValue("AppId", _appId.ToRegString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Registers the ProgID.</summary>
|
||||
/// <param name="progId">The ProgID.</param>
|
||||
/// <param name="typeName">Name of the type.</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentNullException">progId</exception>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// A ProgID may not have more then 39 characters, must start with a letter, and may only contain letters, numbers and periods.
|
||||
/// or
|
||||
/// ProgID already exists - progId
|
||||
/// </exception>
|
||||
public static ProgId RegisterProgID(string progId, string typeName, bool systemWide = false)
|
||||
{
|
||||
if (progId == null) throw new ArgumentNullException(nameof(progId));
|
||||
if (progId.Length > 39 || !Regex.IsMatch(progId, @"^[a-zA-Z][\w\.]+$", RegexOptions.Singleline))
|
||||
throw new ArgumentException("A ProgID may not have more then 39 characters, must start with a letter, and may only contain letters, numbers and periods.");
|
||||
if (Registry.ClassesRoot.HasSubKey(progId)) throw new ArgumentException("ProgID already exists", nameof(progId));
|
||||
return new ProgId(progId, Registry.ClassesRoot.CreateSubKey(progId, typeName), false);
|
||||
if (GetRoot().HasSubKey(progId)) throw new ArgumentException("ProgID already exists", nameof(progId));
|
||||
return new ProgId(progId, GetRoot(systemWide, true).CreateSubKey(progId, typeName), false);
|
||||
}
|
||||
|
||||
public static void UnregisterFileAssociation(string ext, string progId, bool throwOnMissing = true)
|
||||
/// <summary>Unregisters the file association.</summary>
|
||||
/// <param name="ext">The ext.</param>
|
||||
/// <param name="progId">The ProgID.</param>
|
||||
/// <param name="throwOnMissing">if set to <see langword="true" /> [throw on missing].</param>
|
||||
/// <exception cref="InvalidOperationException">Unable to find association key in the registry.</exception>
|
||||
public static void UnregisterFileAssociation(string ext, string progId, bool throwOnMissing = true, bool systemWide = false)
|
||||
{
|
||||
using (var sk = Registry.ClassesRoot.OpenSubKey(ext, true))
|
||||
using (var sk = GetRoot(systemWide, true, ext))
|
||||
{
|
||||
if (sk == null)
|
||||
{
|
||||
|
@ -171,27 +287,57 @@ namespace Vanara.Windows.Shell
|
|||
}
|
||||
}
|
||||
|
||||
public static void UnregisterProgID(string progId, IEnumerable<string> fileExtensions = null)
|
||||
/// <summary>Unregisters the COM Local Server.</summary>
|
||||
/// <typeparam name="TComObject">The type of the COM object.</typeparam>
|
||||
/// <param name="systemWide">
|
||||
/// If set to <see langword="true"/>, the COM object is unregistered system-wide from HKLM; otherwise it is unregistered for the user
|
||||
/// only in HKCU.
|
||||
/// </param>
|
||||
/// <param name="appId">The AppId to relate to this CLSID. If <see langword="null"/>, the CLSID value will be used.</param>
|
||||
public static void UnregisterLocalServer<TComObject>(bool systemWide = false, Guid? appId = null) where TComObject : ComObject
|
||||
{
|
||||
var clsid = GetClsid<TComObject>();
|
||||
using (var root = GetRoot(systemWide, true))
|
||||
{
|
||||
root.DeleteSubKeyTree(@"AppID\" + (appId ?? clsid).ToRegString());
|
||||
root.DeleteSubKeyTree(@"CLSID\" + clsid.ToRegString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Unregisters the ProgID.</summary>
|
||||
/// <param name="progId">The ProgID.</param>
|
||||
/// <param name="fileExtensions">The file extensions.</param>
|
||||
public static void UnregisterProgID(string progId, IEnumerable<string> fileExtensions = null, bool systemWide = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
Registry.ClassesRoot.DeleteSubKeyTree(progId);
|
||||
GetRoot(systemWide, true).DeleteSubKeyTree(progId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Registry.ClassesRoot.DeleteSubKey(progId, false);
|
||||
GetRoot(systemWide, true).DeleteSubKey(progId, false);
|
||||
}
|
||||
|
||||
if (fileExtensions == null) return;
|
||||
|
||||
foreach (var ext in fileExtensions)
|
||||
UnregisterFileAssociation(ext, progId, false);
|
||||
UnregisterFileAssociation(ext, progId, false, systemWide);
|
||||
|
||||
NotifyShell();
|
||||
}
|
||||
|
||||
internal static RegistryKey GetRoot(bool systemWide = true, bool writable = false, string subkey = null)
|
||||
{
|
||||
if (!writable && subkey is null)
|
||||
return Registry.ClassesRoot;
|
||||
var key = systemWide ? Registry.LocalMachine : Registry.CurrentUser;
|
||||
return key.OpenSubKey(@"Software\Classes" + (subkey is null ? "" : "\\" + subkey), writable);
|
||||
}
|
||||
|
||||
internal static void NotifyShell() => SHChangeNotify(SHCNE.SHCNE_ASSOCCHANGED, SHCNF.SHCNF_FLUSHNOWAIT | SHCNF.SHCNF_IDLIST);
|
||||
|
||||
private static Guid GetClsid<TComObject>() => Marshal.GenerateGuidForType(typeof(TComObject));
|
||||
|
||||
private static bool IsDefined(string rootValue) => Registry.ClassesRoot.HasSubKey(rootValue);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue