diff --git a/Windows.Shell/ComRegistrar.cs b/Windows.Shell/ComRegistrar.cs
deleted file mode 100644
index 0992c87a..00000000
--- a/Windows.Shell/ComRegistrar.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using Microsoft.Win32;
-using System;
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-namespace Vanara.Windows.Shell
-{
- /// A class to register COM objects.
- public static class ComRegistrar
- {
- /// Registers the specified type as a COM Local Server.
- /// The type of the COM object.
- /// The friendly name of the COM object.
- /// The command line arguments to supply to the executable on startup.
- /// The assembly used to get the full path of the executable. If this value is , then the assembly of will be used.
- /// If set to , the COM object is registered system-wide in HKLM; otherwise it is registered for the user only in HKCU.
- /// The AppId to relate to this CLSID. If , the CLSID value will be used.
- public static void RegisterLocalServer(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();
- 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());
- }
- }
-
- /// Unregisters the COM Local Server.
- /// The type of the COM object.
- /// If set to , the COM object is unregistered system-wide from HKLM; otherwise it is unregistered for the user only in HKCU.
- /// The AppId to relate to this CLSID. If , the CLSID value will be used.
- public static void UnregisterLocalServer(bool systemWide = false, Guid? appId = null) where TComObject : ComObject
- {
- var clsid = GetClsid();
- 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() => Marshal.GenerateGuidForType(typeof(TComObject));
- }
-}
\ No newline at end of file
diff --git a/Windows.Shell/ShellRegistrar.cs b/Windows.Shell/ShellRegistrar.cs
index 66c3903b..ddf6e13b 100644
--- a/Windows.Shell/ShellRegistrar.cs
+++ b/Windows.Shell/ShellRegistrar.cs
@@ -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
{
- /// Static class that has methods used to register and unregister shell items in the Windows Registry.
+ /// Contains static methods used to register and unregister shell items in the Windows Registry.
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
*/
+ /// Gets the file extensions associated with a given ProgID.
+ /// The ProgID.
+ /// An enumeration of file extensions in the form ".ext".
public static IEnumerable 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 supportedTypes = null, IconLocation defaultIcon = null, bool noStartPage = false, IconLocation taskGroupIcon = null,
- bool useExecutableForTaskbarGroupIcon = false)
+ /// Determines if the specified type is registered as a COM Local Server.
+ /// The type of the COM object.
+ ///
+ /// The assembly used to get the full path of the executable. If this value is , then the assembly of
+ /// will be used.
+ ///
+ ///
+ /// If set to , registration is checked in HKLM; otherwise it is registered for the user only
+ /// in HKCU.
+ ///
+ /// The AppId to relate to this CLSID. If , the CLSID value will be used.
+ public static bool IsRegisteredAsLocalServer(Assembly assembly = null, bool systemWide = false, Guid? appId = null) where TComObject : ComObject
+ {
+ var cmdLine = (assembly ?? typeof(TComObject).Assembly).Location;
+ var clsid = GetClsid();
+ 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;
+ }
+
+ /// Registers the application.
+ /// The full executable path.
+ /// if set to [user only].
+ /// if set to [accepts urls].
+ /// The drop target.
+ /// Name of the friendly.
+ /// The supported types.
+ /// The default icon.
+ /// if set to [no start page].
+ /// The task group icon.
+ /// if set to [use executable for taskbar group icon].
+ /// fullExePath
+ ///
+ /// Unable to create application key in the 'App Paths' subkey.
+ /// or
+ /// Unable to create application key in the HKCR\\Applications subkey.
+ ///
+ public static void RegisterApplication(string fullExePath, bool userOnly = false, bool acceptsUrls = false, Guid? dropTarget = null,
+ IndirectString friendlyName = null, IEnumerable 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();
}
+ /// Registers the command verb.
+ /// The parent key.
+ /// The verb.
+ /// The display name.
+ /// The command.
+ ///
+ /// Unable to create required key in registry.
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)
+ /// Registers the file association.
+ /// The ext.
+ /// The ProgID.
+ /// Type of the perceived.
+ /// Type of the content.
+ ///
+ /// ext
+ /// or
+ /// progId
+ ///
+ ///
+ /// Extension must start with a '.' - ext
+ /// or
+ /// Undefined ProgId value. - progId
+ ///
+ /// Unable to create association key in the registry.
+ 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)
+ /// Registers the specified type as a COM Local Server.
+ /// The type of the COM object.
+ /// The friendly name of the COM object.
+ /// The command line arguments to supply to the executable on startup.
+ ///
+ /// The assembly used to get the full path of the executable. If this value is , then the assembly of
+ /// will be used.
+ ///
+ ///
+ /// If set to , the COM object is registered system-wide in HKLM; otherwise it is registered for the user only
+ /// in HKCU.
+ ///
+ /// The AppId to relate to this CLSID. If , the CLSID value will be used.
+ public static void RegisterLocalServer(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();
+ 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());
+ }
+ }
+
+ /// Registers the ProgID.
+ /// The ProgID.
+ /// Name of the type.
+ ///
+ /// progId
+ ///
+ /// 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
+ ///
+ 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)
+ /// Unregisters the file association.
+ /// The ext.
+ /// The ProgID.
+ /// if set to [throw on missing].
+ /// Unable to find association key in the registry.
+ 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 fileExtensions = null)
+ /// Unregisters the COM Local Server.
+ /// The type of the COM object.
+ ///
+ /// If set to , the COM object is unregistered system-wide from HKLM; otherwise it is unregistered for the user
+ /// only in HKCU.
+ ///
+ /// The AppId to relate to this CLSID. If , the CLSID value will be used.
+ public static void UnregisterLocalServer(bool systemWide = false, Guid? appId = null) where TComObject : ComObject
+ {
+ var clsid = GetClsid();
+ using (var root = GetRoot(systemWide, true))
+ {
+ root.DeleteSubKeyTree(@"AppID\" + (appId ?? clsid).ToRegString());
+ root.DeleteSubKeyTree(@"CLSID\" + clsid.ToRegString());
+ }
+ }
+
+ /// Unregisters the ProgID.
+ /// The ProgID.
+ /// The file extensions.
+ public static void UnregisterProgID(string progId, IEnumerable 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() => Marshal.GenerateGuidForType(typeof(TComObject));
+
private static bool IsDefined(string rootValue) => Registry.ClassesRoot.HasSubKey(rootValue);
}
}
\ No newline at end of file