using Microsoft.Win32; using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.InteropServices; using Vanara.Windows.Shell.Registration; using static Vanara.PInvoke.Shell32; namespace Vanara.Windows.Shell { /// Contains static methods used to register and unregister shell items in the Windows Registry. public static class ShellRegistrar { /// Gets a dictionary of registered applications in the current system. /// The dictionary of registered applications. public static IReadOnlyDictionary Applications { get; } = new AppDictionary(true); /// Gets a dictionary of registered file type associations in the current system. /// The dictionary of file type associations. public static IReadOnlyDictionary FileTypeAssociations { get; } = new FileTypeDictionary(true); /// Gets a dictionary of registered ProgId's in the current system. /// The dictionary of ProgId values. public static IReadOnlyDictionary ProgIds { get; } = new ProgIdDictionary(true); /// Gets a dictionary of registered shell associations in the current system. /// The dictionary of shell associations. public static IReadOnlyDictionary ShellAssociations { get; } = new ShellAssociationDictionary(true); /// Gets the CLSID for the specified type. /// The type. /// The CLSID value for the type. Calls to get the value. public static Guid CLSID(this Type type) => type.GUID; /// 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; } #if NETFRAMEWORK /// 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 { if (assembly == null) assembly = typeof(TComObject).Assembly; var cmdLine = assembly.Location; var qCmdLine = string.Concat("\"", cmdLine, "\""); var typelib = Marshal.GetTypeLibGuidForAssembly(assembly); var clsid = GetClsid(); var _appId = appId ?? clsid; if (!(cmdLineArgs is null)) qCmdLine += " " + 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("TypeLib", typelib.ToRegString())) using (var rLS32 = rClsid.CreateSubKey("LocalServer32", cmdLineArgs is null ? qCmdLine : string.Concat("\"", qCmdLine, "\""))) { rClsid.SetValue("AppId", _appId.ToRegString()); rLS32.SetValue("ServerExecutable", cmdLine); } } #endif /// 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 = true, bool writable = false, string subkey = null) { if (!writable && subkey is null) return Registry.ClassesRoot; var key = systemWide ? Registry.LocalMachine : Registry.CurrentUser; var fullsubkey = @"Software\Classes" + (subkey is null ? "" : "\\" + subkey); return writable ? key.CreateSubKey(fullsubkey) : key.OpenSubKey(fullsubkey, writable); } internal static void NotifyShell() => SHChangeNotify(SHCNE.SHCNE_ASSOCCHANGED, SHCNF.SHCNF_FLUSHNOWAIT | SHCNF.SHCNF_IDLIST); private static Guid GetClsid() => typeof(TComObject).CLSID(); } }