diff --git a/ContextMenuRegistrar.cs b/ContextMenuRegistrar.cs
new file mode 100644
index 0000000..65d393a
--- /dev/null
+++ b/ContextMenuRegistrar.cs
@@ -0,0 +1,430 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Security.Principal;
+using Microsoft.Win32;
+
+///
+/// Предоставляет методы для регистрации и удаления пунктов контекстного меню Windows.
+///
+public static class ContextMenuRegistrar
+{
+ #region Проверка прав администратора
+
+ private static void CheckAdministrator()
+ {
+ using WindowsIdentity identity = WindowsIdentity.GetCurrent();
+ var principal = new WindowsPrincipal(identity);
+ if (!principal.IsInRole(WindowsBuiltInRole.Administrator))
+ throw new UnauthorizedAccessException("Требуются права администратора.");
+ }
+
+ #endregion
+
+ #region Вспомогательный метод для регистрации с прямой командой
+
+ private static void RegisterWithCommand(string rootPath, string menuName, string command, List createdKeys)
+ {
+ string shellPath = rootPath; // уже включает Shell, например @"SystemFileAssociations\.mp4\Shell"
+ using (RegistryKey shellKey = Registry.ClassesRoot.CreateSubKey(shellPath))
+ using (RegistryKey menuKey = shellKey.CreateSubKey(menuName))
+ {
+ menuKey.SetValue(null, menuName);
+ using RegistryKey commandKey = menuKey.CreateSubKey("command");
+ commandKey.SetValue(null, command);
+ }
+ createdKeys.Add($@"{shellPath}\{menuName}");
+ }
+
+ #endregion
+
+ #region Регистрация для файлов по расширениям
+
+ ///
+ /// Регистрирует пункт меню для указанных расширений файлов, используя текущее приложение.
+ ///
+ /// Имя пункта меню (отображается в контекстном меню).
+ /// Коллекция расширений, например {".mp4", ".avi"}.
+ /// Необязательный префикс командной строки, передаваемый перед именем файла.
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню или расширение.
+ /// extensions равно null.
+ /// Ошибка при регистрации (выполнен откат).
+ public static void RegisterFileExtensions(string menuName, IEnumerable extensions, string commandPrefix = "")
+ {
+ string appPath = Process.GetCurrentProcess().MainModule.FileName;
+ RegisterFileExtensionsForExe(menuName, extensions, appPath, commandPrefix);
+ }
+
+ ///
+ /// Регистрирует пункт меню для указанных расширений файлов с заданным исполняемым файлом.
+ ///
+ /// Имя пункта меню.
+ /// Коллекция расширений.
+ /// Полный путь к исполняемому файлу.
+ /// Необязательный префикс командной строки.
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню, расширение или путь к exe.
+ /// extensions равно null.
+ /// Ошибка при регистрации (выполнен откат).
+ public static void RegisterFileExtensionsForExe(string menuName, IEnumerable extensions, string exePath, string commandPrefix = "")
+ {
+ if (string.IsNullOrWhiteSpace(exePath))
+ throw new ArgumentException("Путь к исполняемому файлу не может быть пустым.", nameof(exePath));
+ commandPrefix ??= "";
+ string command = $"\"{exePath}\"{(string.IsNullOrEmpty(commandPrefix) ? "" : " " + commandPrefix)} \"%1\"";
+ RegisterFileExtensionsCommand(menuName, extensions, command);
+ }
+
+ ///
+ /// Регистрирует пункт меню для указанных расширений файлов с заданной полной командой.
+ ///
+ /// Имя пункта меню.
+ /// Коллекция расширений.
+ /// Полная командная строка, которая будет записана в ключ "command".
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню, расширение или пустая команда.
+ /// extensions равно null.
+ /// Ошибка при регистрации (выполнен откат).
+ public static void RegisterFileExtensionsCommand(string menuName, IEnumerable extensions, string command)
+ {
+ CheckAdministrator();
+ if (string.IsNullOrWhiteSpace(menuName))
+ throw new ArgumentException("Имя пункта меню не может быть пустым.", nameof(menuName));
+ if (extensions == null)
+ throw new ArgumentNullException(nameof(extensions));
+ if (string.IsNullOrWhiteSpace(command))
+ throw new ArgumentException("Команда не может быть пустой.", nameof(command));
+
+ List createdKeys = [];
+
+ try
+ {
+ foreach (string ext in extensions)
+ {
+ if (string.IsNullOrWhiteSpace(ext) || !ext.StartsWith("."))
+ throw new ArgumentException($"Некорректное расширение: '{ext}'. Должно начинаться с точки.", nameof(extensions));
+
+ string shellPath = $@"SystemFileAssociations\{ext}\Shell";
+ RegisterWithCommand(shellPath, menuName, command, createdKeys);
+ }
+ }
+ catch (Exception ex)
+ {
+ // Откат: удаляем созданные ключи
+ foreach (string keyPath in createdKeys)
+ {
+ try { Registry.ClassesRoot.DeleteSubKeyTree(keyPath, false); } catch { }
+ }
+ throw new InvalidOperationException($"Ошибка при регистрации: {ex.Message}", ex);
+ }
+ }
+
+ ///
+ /// Удаляет ранее зарегистрированный пункт меню для указанных расширений файлов.
+ ///
+ /// Имя пункта меню.
+ /// Коллекция расширений.
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню или расширение.
+ /// extensions равно null.
+ /// Не удалось удалить одно или несколько расширений.
+ public static void UnregisterFileExtensions(string menuName, IEnumerable extensions)
+ {
+ CheckAdministrator();
+ if (string.IsNullOrWhiteSpace(menuName))
+ throw new ArgumentException("Имя пункта меню не может быть пустым.", nameof(menuName));
+ if (extensions == null)
+ throw new ArgumentNullException(nameof(extensions));
+
+ List errors = [];
+ foreach (string ext in extensions)
+ {
+ if (string.IsNullOrWhiteSpace(ext) || !ext.StartsWith("."))
+ throw new ArgumentException($"Некорректное расширение: '{ext}'. Должно начинаться с точки.", nameof(extensions));
+
+ string keyPath = $@"SystemFileAssociations\{ext}\Shell\{menuName}";
+ try
+ {
+ Registry.ClassesRoot.DeleteSubKeyTree(keyPath, false);
+ }
+ catch (ArgumentException) { /* ключ не существует – игнорируем */ }
+ catch (Exception ex)
+ {
+ errors.Add($"{ext}: {ex.Message}");
+ }
+ }
+ if (errors.Any())
+ throw new InvalidOperationException("Не удалось удалить некоторые расширения: " + string.Join("; ", errors));
+ }
+
+ #endregion
+
+ #region Регистрация для всех файлов
+
+ ///
+ /// Регистрирует пункт меню для всех файлов, используя текущее приложение.
+ ///
+ /// Имя пункта меню.
+ /// Необязательный префикс командной строки.
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню.
+ /// Ошибка при регистрации.
+ public static void RegisterAllFiles(string menuName, string commandPrefix = "")
+ {
+ string appPath = Process.GetCurrentProcess().MainModule.FileName;
+ RegisterAllFilesForExe(menuName, appPath, commandPrefix);
+ }
+
+ ///
+ /// Регистрирует пункт меню для всех файлов с заданным исполняемым файлом.
+ ///
+ /// Имя пункта меню.
+ /// Полный путь к исполняемому файлу.
+ /// Необязательный префикс командной строки.
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню или путь к exe.
+ /// Ошибка при регистрации.
+ public static void RegisterAllFilesForExe(string menuName, string exePath, string commandPrefix = "")
+ {
+ if (string.IsNullOrWhiteSpace(exePath))
+ throw new ArgumentException("Путь к исполняемому файлу не может быть пустым.", nameof(exePath));
+ commandPrefix ??= "";
+ string command = $"\"{exePath}\"{(string.IsNullOrEmpty(commandPrefix) ? "" : " " + commandPrefix)} \"%1\"";
+ RegisterAllFilesCommand(menuName, command);
+ }
+
+ ///
+ /// Регистрирует пункт меню для всех файлов с заданной полной командой.
+ ///
+ /// Имя пункта меню.
+ /// Полная командная строка, которая будет записана в ключ "command".
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню или пустая команда.
+ /// Ошибка при регистрации.
+ public static void RegisterAllFilesCommand(string menuName, string command)
+ {
+ CheckAdministrator();
+ if (string.IsNullOrWhiteSpace(menuName))
+ throw new ArgumentException("Имя пункта меню не может быть пустым.", nameof(menuName));
+ if (string.IsNullOrWhiteSpace(command))
+ throw new ArgumentException("Команда не может быть пустой.", nameof(command));
+
+ List createdKeys = [];
+ try
+ {
+ string shellPath = @"*\Shell";
+ RegisterWithCommand(shellPath, menuName, command, createdKeys);
+ }
+ catch (Exception ex)
+ {
+ foreach (string keyPath in createdKeys)
+ {
+ try { Registry.ClassesRoot.DeleteSubKeyTree(keyPath, false); } catch { }
+ }
+ throw new InvalidOperationException($"Ошибка при регистрации: {ex.Message}", ex);
+ }
+ }
+
+ ///
+ /// Удаляет пункт меню для всех файлов.
+ ///
+ /// Имя пункта меню.
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню.
+ public static void UnregisterAllFiles(string menuName)
+ {
+ CheckAdministrator();
+ if (string.IsNullOrWhiteSpace(menuName))
+ throw new ArgumentException("Имя пункта меню не может быть пустым.", nameof(menuName));
+
+ string keyPath = $@"*\Shell\{menuName}";
+ try
+ {
+ Registry.ClassesRoot.DeleteSubKeyTree(keyPath, false);
+ }
+ catch (ArgumentException) { }
+ }
+
+ #endregion
+
+ #region Регистрация для папок (щелчок на иконке папки)
+
+ ///
+ /// Регистрирует пункт меню для папок (появляется при щелчке на иконке папки), используя текущее приложение.
+ ///
+ /// Имя пункта меню.
+ /// Необязательный префикс командной строки.
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню.
+ /// Ошибка при регистрации.
+ public static void RegisterFolderShell(string menuName, string commandPrefix = "")
+ {
+ string appPath = Process.GetCurrentProcess().MainModule.FileName;
+ RegisterFolderShellForExe(menuName, appPath, commandPrefix);
+ }
+
+ ///
+ /// Регистрирует пункт меню для папок с заданным исполняемым файлом.
+ ///
+ /// Имя пункта меню.
+ /// Полный путь к исполняемому файлу.
+ /// Необязательный префикс командной строки.
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню или путь к exe.
+ /// Ошибка при регистрации.
+ public static void RegisterFolderShellForExe(string menuName, string exePath, string commandPrefix = "")
+ {
+ if (string.IsNullOrWhiteSpace(exePath))
+ throw new ArgumentException("Путь к исполняемому файлу не может быть пустым.", nameof(exePath));
+ commandPrefix ??= "";
+ string command = $"\"{exePath}\"{(string.IsNullOrEmpty(commandPrefix) ? "" : " " + commandPrefix)} \"%1\"";
+ RegisterFolderShellCommand(menuName, command);
+ }
+
+ ///
+ /// Регистрирует пункт меню для папок с заданной полной командой.
+ ///
+ /// Имя пункта меню.
+ /// Полная командная строка, которая будет записана в ключ "command".
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню или пустая команда.
+ /// Ошибка при регистрации.
+ public static void RegisterFolderShellCommand(string menuName, string command)
+ {
+ CheckAdministrator();
+ if (string.IsNullOrWhiteSpace(menuName))
+ throw new ArgumentException("Имя пункта меню не может быть пустым.", nameof(menuName));
+ if (string.IsNullOrWhiteSpace(command))
+ throw new ArgumentException("Команда не может быть пустой.", nameof(command));
+
+ List createdKeys = [];
+ try
+ {
+ string shellPath = @"Directory\Shell";
+ RegisterWithCommand(shellPath, menuName, command, createdKeys);
+ }
+ catch (Exception ex)
+ {
+ foreach (string keyPath in createdKeys)
+ {
+ try { Registry.ClassesRoot.DeleteSubKeyTree(keyPath, false); } catch { }
+ }
+ throw new InvalidOperationException($"Ошибка при регистрации: {ex.Message}", ex);
+ }
+ }
+
+ ///
+ /// Удаляет пункт меню для папок (из раздела Shell).
+ ///
+ /// Имя пункта меню.
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню.
+ public static void UnregisterFolderShell(string menuName)
+ {
+ CheckAdministrator();
+ if (string.IsNullOrWhiteSpace(menuName))
+ throw new ArgumentException("Имя пункта меню не может быть пустым.", nameof(menuName));
+
+ string keyPath = $@"Directory\Shell\{menuName}";
+ try
+ {
+ Registry.ClassesRoot.DeleteSubKeyTree(keyPath, false);
+ }
+ catch (ArgumentException) { }
+ }
+
+ #endregion
+
+ #region Регистрация для фона папки (щелчок на свободном месте внутри папки)
+
+ ///
+ /// Регистрирует пункт меню для фона папки (появляется при щелчке на свободном месте внутри открытой папки),
+ /// используя текущее приложение. В команде рекомендуется использовать параметр "%V" для передачи пути к текущей папке.
+ ///
+ /// Имя пункта меню.
+ /// Необязательный префикс командной строки.
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню.
+ /// Ошибка при регистрации.
+ public static void RegisterFolderBackgroundShell(string menuName, string commandPrefix = "")
+ {
+ string appPath = Process.GetCurrentProcess().MainModule.FileName;
+ RegisterFolderBackgroundShellForExe(menuName, appPath, commandPrefix);
+ }
+
+ ///
+ /// Регистрирует пункт меню для фона папки с заданным исполняемым файлом.
+ /// В команде рекомендуется использовать параметр "%V" для передачи пути к текущей папке.
+ ///
+ /// Имя пункта меню.
+ /// Полный путь к исполняемому файлу.
+ /// Необязательный префикс командной строки.
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню или путь к exe.
+ /// Ошибка при регистрации.
+ public static void RegisterFolderBackgroundShellForExe(string menuName, string exePath, string commandPrefix = "")
+ {
+ if (string.IsNullOrWhiteSpace(exePath))
+ throw new ArgumentException("Путь к исполняемому файлу не может быть пустым.", nameof(exePath));
+ commandPrefix ??= "";
+ // Для фона папки рекомендуется %V
+ string command = $"\"{exePath}\"{(string.IsNullOrEmpty(commandPrefix) ? "" : " " + commandPrefix)} \"%V\"";
+ RegisterFolderBackgroundShellCommand(menuName, command);
+ }
+
+ ///
+ /// Регистрирует пункт меню для фона папки с заданной полной командой.
+ ///
+ /// Имя пункта меню.
+ /// Полная командная строка, которая будет записана в ключ "command". Рекомендуется использовать "%V".
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню или пустая команда.
+ /// Ошибка при регистрации.
+ public static void RegisterFolderBackgroundShellCommand(string menuName, string command)
+ {
+ CheckAdministrator();
+ if (string.IsNullOrWhiteSpace(menuName))
+ throw new ArgumentException("Имя пункта меню не может быть пустым.", nameof(menuName));
+ if (string.IsNullOrWhiteSpace(command))
+ throw new ArgumentException("Команда не может быть пустой.", nameof(command));
+
+ List createdKeys = [];
+ try
+ {
+ string shellPath = @"Directory\Background\Shell";
+ RegisterWithCommand(shellPath, menuName, command, createdKeys);
+ }
+ catch (Exception ex)
+ {
+ foreach (string keyPath in createdKeys)
+ {
+ try { Registry.ClassesRoot.DeleteSubKeyTree(keyPath, false); } catch { }
+ }
+ throw new InvalidOperationException($"Ошибка при регистрации: {ex.Message}", ex);
+ }
+ }
+
+ ///
+ /// Удаляет пункт меню для фона папки.
+ ///
+ /// Имя пункта меню.
+ /// Приложение не запущено от имени администратора.
+ /// Некорректное имя меню.
+ public static void UnregisterFolderBackgroundShell(string menuName)
+ {
+ CheckAdministrator();
+ if (string.IsNullOrWhiteSpace(menuName))
+ throw new ArgumentException("Имя пункта меню не может быть пустым.", nameof(menuName));
+
+ string keyPath = $@"Directory\Background\Shell\{menuName}";
+ try
+ {
+ Registry.ClassesRoot.DeleteSubKeyTree(keyPath, false);
+ }
+ catch (ArgumentException) { }
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/ContextMenuRegistrar.csproj b/ContextMenuRegistrar.csproj
new file mode 100644
index 0000000..3c22345
--- /dev/null
+++ b/ContextMenuRegistrar.csproj
@@ -0,0 +1,13 @@
+
+
+
+ netstandard2.0
+ latest
+
+
+
+
+
+
+
+
diff --git a/ContextMenuRegistrar.slnx b/ContextMenuRegistrar.slnx
new file mode 100644
index 0000000..8ef79dc
--- /dev/null
+++ b/ContextMenuRegistrar.slnx
@@ -0,0 +1,3 @@
+
+
+