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();
}
}