diff --git a/ContextMenuRegistrar.cs b/ContextMenuRegistrar.cs index 65d393a..616b897 100644 --- a/ContextMenuRegistrar.cs +++ b/ContextMenuRegistrar.cs @@ -22,19 +22,33 @@ public static class ContextMenuRegistrar #endregion - #region Вспомогательный метод для регистрации с прямой командой + #region Вспомогательные методы - private static void RegisterWithCommand(string rootPath, string menuName, string command, List createdKeys) + private static string BuildCommand(string exePath, string prefix, string argument) { - string shellPath = rootPath; // уже включает Shell, например @"SystemFileAssociations\.mp4\Shell" - using (RegistryKey shellKey = Registry.ClassesRoot.CreateSubKey(shellPath)) + prefix ??= ""; + return $"\"{exePath}\"{(string.IsNullOrEmpty(prefix) ? "" : " " + prefix)} {argument}"; + } + + // Возвращает путь к иконке для exe-методов + private static string? GetIconPath(string exePath, bool addIcon) + { + return addIcon ? $"{exePath},0" : null; + } + + // Регистрация с возможным указанием иконки (iconPath может быть null) + private static void RegisterWithCommand(string rootPath, string menuName, string command, List createdKeys, string? iconPath = null) + { + using (RegistryKey shellKey = Registry.ClassesRoot.CreateSubKey(rootPath)) using (RegistryKey menuKey = shellKey.CreateSubKey(menuName)) { menuKey.SetValue(null, menuName); + if (!string.IsNullOrWhiteSpace(iconPath)) + menuKey.SetValue("Icon", iconPath!); using RegistryKey commandKey = menuKey.CreateSubKey("command"); commandKey.SetValue(null, command); } - createdKeys.Add($@"{shellPath}\{menuName}"); + createdKeys.Add($@"{rootPath}\{menuName}"); } #endregion @@ -44,17 +58,14 @@ public static class ContextMenuRegistrar /// /// Регистрирует пункт меню для указанных расширений файлов, используя текущее приложение. /// - /// Имя пункта меню (отображается в контекстном меню). + /// Имя пункта меню. /// Коллекция расширений, например {".mp4", ".avi"}. - /// Необязательный префикс командной строки, передаваемый перед именем файла. - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню или расширение. - /// extensions равно null. - /// Ошибка при регистрации (выполнен откат). - public static void RegisterFileExtensions(string menuName, IEnumerable extensions, string commandPrefix = "") + /// Необязательный префикс командной строки. + /// Если true, в качестве иконки используется текущее приложение (индекс 0). + public static void RegisterFileExtensions(string menuName, IEnumerable extensions, string commandPrefix = "", bool addIcon = false) { string appPath = Process.GetCurrentProcess().MainModule.FileName; - RegisterFileExtensionsForExe(menuName, extensions, appPath, commandPrefix); + RegisterFileExtensionsForExe(menuName, extensions, appPath, commandPrefix, addIcon); } /// @@ -64,17 +75,15 @@ public static class ContextMenuRegistrar /// Коллекция расширений. /// Полный путь к исполняемому файлу. /// Необязательный префикс командной строки. - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню, расширение или путь к exe. - /// extensions равно null. - /// Ошибка при регистрации (выполнен откат). - public static void RegisterFileExtensionsForExe(string menuName, IEnumerable extensions, string exePath, string commandPrefix = "") + /// Если true, в качестве иконки используется указанный exePath (индекс 0). + public static void RegisterFileExtensionsForExe(string menuName, IEnumerable extensions, string exePath, string commandPrefix = "", bool addIcon = false) { if (string.IsNullOrWhiteSpace(exePath)) throw new ArgumentException("Путь к исполняемому файлу не может быть пустым.", nameof(exePath)); commandPrefix ??= ""; - string command = $"\"{exePath}\"{(string.IsNullOrEmpty(commandPrefix) ? "" : " " + commandPrefix)} \"%1\""; - RegisterFileExtensionsCommand(menuName, extensions, command); + string command = BuildCommand(exePath, commandPrefix, "\"%1\""); + string? iconPath = GetIconPath(exePath, addIcon); + RegisterFileExtensionsCommand(menuName, extensions, command, iconPath); } /// @@ -82,12 +91,9 @@ public static class ContextMenuRegistrar /// /// Имя пункта меню. /// Коллекция расширений. - /// Полная командная строка, которая будет записана в ключ "command". - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню, расширение или пустая команда. - /// extensions равно null. - /// Ошибка при регистрации (выполнен откат). - public static void RegisterFileExtensionsCommand(string menuName, IEnumerable extensions, string command) + /// Полная командная строка. + /// Необязательный путь к иконке. Если не указан (null), иконка не добавляется. + public static void RegisterFileExtensionsCommand(string menuName, IEnumerable extensions, string command, string? iconPath = null) { CheckAdministrator(); if (string.IsNullOrWhiteSpace(menuName)) @@ -107,12 +113,11 @@ public static class ContextMenuRegistrar throw new ArgumentException($"Некорректное расширение: '{ext}'. Должно начинаться с точки.", nameof(extensions)); string shellPath = $@"SystemFileAssociations\{ext}\Shell"; - RegisterWithCommand(shellPath, menuName, command, createdKeys); + RegisterWithCommand(shellPath, menuName, command, createdKeys, iconPath); } } catch (Exception ex) { - // Откат: удаляем созданные ключи foreach (string keyPath in createdKeys) { try { Registry.ClassesRoot.DeleteSubKeyTree(keyPath, false); } catch { } @@ -124,12 +129,6 @@ public static class ContextMenuRegistrar /// /// Удаляет ранее зарегистрированный пункт меню для указанных расширений файлов. /// - /// Имя пункта меню. - /// Коллекция расширений. - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню или расширение. - /// extensions равно null. - /// Не удалось удалить одно или несколько расширений. public static void UnregisterFileExtensions(string menuName, IEnumerable extensions) { CheckAdministrator(); @@ -149,7 +148,7 @@ public static class ContextMenuRegistrar { Registry.ClassesRoot.DeleteSubKeyTree(keyPath, false); } - catch (ArgumentException) { /* ключ не существует – игнорируем */ } + catch (ArgumentException) { } catch (Exception ex) { errors.Add($"{ext}: {ex.Message}"); @@ -166,44 +165,29 @@ public static class ContextMenuRegistrar /// /// Регистрирует пункт меню для всех файлов, используя текущее приложение. /// - /// Имя пункта меню. - /// Необязательный префикс командной строки. - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню. - /// Ошибка при регистрации. - public static void RegisterAllFiles(string menuName, string commandPrefix = "") + public static void RegisterAllFiles(string menuName, string commandPrefix = "", bool addIcon = false) { string appPath = Process.GetCurrentProcess().MainModule.FileName; - RegisterAllFilesForExe(menuName, appPath, commandPrefix); + RegisterAllFilesForExe(menuName, appPath, commandPrefix, addIcon); } /// /// Регистрирует пункт меню для всех файлов с заданным исполняемым файлом. /// - /// Имя пункта меню. - /// Полный путь к исполняемому файлу. - /// Необязательный префикс командной строки. - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню или путь к exe. - /// Ошибка при регистрации. - public static void RegisterAllFilesForExe(string menuName, string exePath, string commandPrefix = "") + public static void RegisterAllFilesForExe(string menuName, string exePath, string commandPrefix = "", bool addIcon = false) { if (string.IsNullOrWhiteSpace(exePath)) throw new ArgumentException("Путь к исполняемому файлу не может быть пустым.", nameof(exePath)); commandPrefix ??= ""; - string command = $"\"{exePath}\"{(string.IsNullOrEmpty(commandPrefix) ? "" : " " + commandPrefix)} \"%1\""; - RegisterAllFilesCommand(menuName, command); + string command = BuildCommand(exePath, commandPrefix, "\"%1\""); + string? iconPath = GetIconPath(exePath, addIcon); + RegisterAllFilesCommand(menuName, command, iconPath); } /// /// Регистрирует пункт меню для всех файлов с заданной полной командой. /// - /// Имя пункта меню. - /// Полная командная строка, которая будет записана в ключ "command". - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню или пустая команда. - /// Ошибка при регистрации. - public static void RegisterAllFilesCommand(string menuName, string command) + public static void RegisterAllFilesCommand(string menuName, string command, string? iconPath = null) { CheckAdministrator(); if (string.IsNullOrWhiteSpace(menuName)) @@ -215,7 +199,7 @@ public static class ContextMenuRegistrar try { string shellPath = @"*\Shell"; - RegisterWithCommand(shellPath, menuName, command, createdKeys); + RegisterWithCommand(shellPath, menuName, command, createdKeys, iconPath); } catch (Exception ex) { @@ -230,9 +214,6 @@ public static class ContextMenuRegistrar /// /// Удаляет пункт меню для всех файлов. /// - /// Имя пункта меню. - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню. public static void UnregisterAllFiles(string menuName) { CheckAdministrator(); @@ -252,46 +233,31 @@ public static class ContextMenuRegistrar #region Регистрация для папок (щелчок на иконке папки) /// - /// Регистрирует пункт меню для папок (появляется при щелчке на иконке папки), используя текущее приложение. + /// Регистрирует пункт меню для папок, используя текущее приложение. /// - /// Имя пункта меню. - /// Необязательный префикс командной строки. - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню. - /// Ошибка при регистрации. - public static void RegisterFolderShell(string menuName, string commandPrefix = "") + public static void RegisterFolderShell(string menuName, string commandPrefix = "", bool addIcon = false) { string appPath = Process.GetCurrentProcess().MainModule.FileName; - RegisterFolderShellForExe(menuName, appPath, commandPrefix); + RegisterFolderShellForExe(menuName, appPath, commandPrefix, addIcon); } /// /// Регистрирует пункт меню для папок с заданным исполняемым файлом. /// - /// Имя пункта меню. - /// Полный путь к исполняемому файлу. - /// Необязательный префикс командной строки. - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню или путь к exe. - /// Ошибка при регистрации. - public static void RegisterFolderShellForExe(string menuName, string exePath, string commandPrefix = "") + public static void RegisterFolderShellForExe(string menuName, string exePath, string commandPrefix = "", bool addIcon = false) { if (string.IsNullOrWhiteSpace(exePath)) throw new ArgumentException("Путь к исполняемому файлу не может быть пустым.", nameof(exePath)); commandPrefix ??= ""; - string command = $"\"{exePath}\"{(string.IsNullOrEmpty(commandPrefix) ? "" : " " + commandPrefix)} \"%1\""; - RegisterFolderShellCommand(menuName, command); + string command = BuildCommand(exePath, commandPrefix, "\"%1\""); + string? iconPath = GetIconPath(exePath, addIcon); + RegisterFolderShellCommand(menuName, command, iconPath); } /// /// Регистрирует пункт меню для папок с заданной полной командой. /// - /// Имя пункта меню. - /// Полная командная строка, которая будет записана в ключ "command". - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню или пустая команда. - /// Ошибка при регистрации. - public static void RegisterFolderShellCommand(string menuName, string command) + public static void RegisterFolderShellCommand(string menuName, string command, string? iconPath = null) { CheckAdministrator(); if (string.IsNullOrWhiteSpace(menuName)) @@ -303,7 +269,7 @@ public static class ContextMenuRegistrar try { string shellPath = @"Directory\Shell"; - RegisterWithCommand(shellPath, menuName, command, createdKeys); + RegisterWithCommand(shellPath, menuName, command, createdKeys, iconPath); } catch (Exception ex) { @@ -316,11 +282,8 @@ public static class ContextMenuRegistrar } /// - /// Удаляет пункт меню для папок (из раздела Shell). + /// Удаляет пункт меню для папок. /// - /// Имя пункта меню. - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню. public static void UnregisterFolderShell(string menuName) { CheckAdministrator(); @@ -337,52 +300,35 @@ public static class ContextMenuRegistrar #endregion - #region Регистрация для фона папки (щелчок на свободном месте внутри папки) + #region Регистрация для фона папки /// - /// Регистрирует пункт меню для фона папки (появляется при щелчке на свободном месте внутри открытой папки), - /// используя текущее приложение. В команде рекомендуется использовать параметр "%V" для передачи пути к текущей папке. + /// Регистрирует пункт меню для фона папки, используя текущее приложение. + /// В команде рекомендуется использовать "%V". /// - /// Имя пункта меню. - /// Необязательный префикс командной строки. - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню. - /// Ошибка при регистрации. - public static void RegisterFolderBackgroundShell(string menuName, string commandPrefix = "") + public static void RegisterFolderBackgroundShell(string menuName, string commandPrefix = "", bool addIcon = false) { string appPath = Process.GetCurrentProcess().MainModule.FileName; - RegisterFolderBackgroundShellForExe(menuName, appPath, commandPrefix); + RegisterFolderBackgroundShellForExe(menuName, appPath, commandPrefix, addIcon); } /// /// Регистрирует пункт меню для фона папки с заданным исполняемым файлом. - /// В команде рекомендуется использовать параметр "%V" для передачи пути к текущей папке. /// - /// Имя пункта меню. - /// Полный путь к исполняемому файлу. - /// Необязательный префикс командной строки. - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню или путь к exe. - /// Ошибка при регистрации. - public static void RegisterFolderBackgroundShellForExe(string menuName, string exePath, string commandPrefix = "") + public static void RegisterFolderBackgroundShellForExe(string menuName, string exePath, string commandPrefix = "", bool addIcon = false) { if (string.IsNullOrWhiteSpace(exePath)) throw new ArgumentException("Путь к исполняемому файлу не может быть пустым.", nameof(exePath)); commandPrefix ??= ""; - // Для фона папки рекомендуется %V - string command = $"\"{exePath}\"{(string.IsNullOrEmpty(commandPrefix) ? "" : " " + commandPrefix)} \"%V\""; - RegisterFolderBackgroundShellCommand(menuName, command); + string command = BuildCommand(exePath, commandPrefix, "\"%V\""); + string? iconPath = GetIconPath(exePath, addIcon); + RegisterFolderBackgroundShellCommand(menuName, command, iconPath); } /// /// Регистрирует пункт меню для фона папки с заданной полной командой. /// - /// Имя пункта меню. - /// Полная командная строка, которая будет записана в ключ "command". Рекомендуется использовать "%V". - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню или пустая команда. - /// Ошибка при регистрации. - public static void RegisterFolderBackgroundShellCommand(string menuName, string command) + public static void RegisterFolderBackgroundShellCommand(string menuName, string command, string? iconPath = null) { CheckAdministrator(); if (string.IsNullOrWhiteSpace(menuName)) @@ -394,7 +340,7 @@ public static class ContextMenuRegistrar try { string shellPath = @"Directory\Background\Shell"; - RegisterWithCommand(shellPath, menuName, command, createdKeys); + RegisterWithCommand(shellPath, menuName, command, createdKeys, iconPath); } catch (Exception ex) { @@ -409,9 +355,6 @@ public static class ContextMenuRegistrar /// /// Удаляет пункт меню для фона папки. /// - /// Имя пункта меню. - /// Приложение не запущено от имени администратора. - /// Некорректное имя меню. public static void UnregisterFolderBackgroundShell(string menuName) { CheckAdministrator(); diff --git a/ContextMenuRegistrar.csproj b/ContextMenuRegistrar.csproj index 3c22345..3ba52af 100644 --- a/ContextMenuRegistrar.csproj +++ b/ContextMenuRegistrar.csproj @@ -3,6 +3,7 @@ netstandard2.0 latest + enable