├── ContextMenuManager ├── Properties │ ├── Resources │ │ ├── ShellNew │ │ │ ├── 0.rtf │ │ │ ├── 0.zip │ │ │ ├── 0.xml │ │ │ ├── 0.c │ │ │ ├── 0.rar │ │ │ ├── 0.reg │ │ │ ├── 0.xlsx │ │ │ └── 0.html │ │ ├── Images │ │ │ ├── Add.png │ │ │ ├── Up.png │ │ │ ├── Web.png │ │ │ ├── About.png │ │ │ ├── Home.png │ │ │ ├── Jump.png │ │ │ ├── Open.png │ │ │ ├── Sort.png │ │ │ ├── Star.png │ │ │ ├── Type.png │ │ │ ├── User.png │ │ │ ├── Custom.png │ │ │ ├── Delete.png │ │ │ ├── Donate.png │ │ │ ├── DownLoad.png │ │ │ ├── Enhance.png │ │ │ ├── NewItem.png │ │ │ ├── Refresh.png │ │ │ ├── Select.png │ │ │ ├── Setting.png │ │ │ ├── SubItems.png │ │ │ ├── BackupItem.png │ │ │ ├── NewFolder.png │ │ │ ├── Translate.png │ │ │ ├── AddExisting.png │ │ │ ├── AddSeparator.png │ │ │ ├── CheckUpdate.png │ │ │ ├── MicrosoftStore.png │ │ │ └── ContextMenuStyle.png │ │ └── Texts │ │ │ └── UwpModeItemsDic.xml │ ├── AppIcon.ico │ ├── Settings.settings │ ├── AssemblyInfo.cs │ ├── PublishProfiles │ │ └── Net9.0-AnyCPU.pubxml │ ├── Settings.Designer.cs │ └── App.manifest ├── BluePointLilac.Controls │ ├── MyStatusBar.cs │ ├── ControlExtensions.cs │ ├── IconDialog.cs │ ├── ResizeLimitedForm.cs │ ├── ReadOnlyTextBox.cs │ ├── UAWebClient.cs │ ├── InputDialog.cs │ ├── DownloadDialog.cs │ ├── PictureButton.cs │ ├── SelectDialog.cs │ └── LoadingDialog.Designer.cs ├── Controls │ ├── Interfaces │ │ ├── IProtectOpenItem.cs │ │ ├── IBtnMoveUpDownItem.cs │ │ ├── ITsiRestoreItem.cs │ │ ├── ITsiWebSearchItem.cs │ │ ├── IBtnDeleteItem.cs │ │ ├── IBtnShowMenuItem.cs │ │ ├── ITsiRegPathItem.cs │ │ ├── IChkVisibleItem.cs │ │ ├── ITsiFilePathItem.cs │ │ ├── ITsiCommandItem.cs │ │ ├── ITsiIconItem.cs │ │ ├── ITsiTextItem.cs │ │ ├── ITsiAdministratorItem.cs │ │ ├── ITsiRegExportItem.cs │ │ └── ITsiDeleteItem.cs │ ├── NewItem.cs │ ├── EnhanceMenusDialog.cs │ ├── DetailedEditDialog.cs │ ├── RToolStripMenuItem.cs │ ├── SubItemsForm.cs │ ├── ExplorerRestarter.cs │ ├── SearchableListBase.cs │ ├── FileExtensionDialog.cs │ ├── SwitchDicList.cs │ ├── IEList.cs │ ├── NewIEDialog.cs │ ├── RestoreItem.cs │ ├── GuidBlockedList.cs │ ├── OpenWithList.cs │ ├── RForm.cs │ ├── NewOpenWithDialog.cs │ ├── SendToList.cs │ ├── UwpModeItem.cs │ ├── GuidBlockedItem.cs │ ├── NewItemForm.cs │ ├── IEItem.cs │ ├── FoldSubItem.cs │ └── DictionariesBox.cs ├── App.config ├── BluePointLilac.Methods │ ├── StringExtension.cs │ ├── ToolTipBox.cs │ ├── ComboBoxExtension.cs │ ├── DirectoryEx.cs │ ├── GuidEx.cs │ ├── FormExtension.cs │ ├── ResourceString.cs │ ├── WinOsVersion.cs │ ├── ControlExtension.cs │ ├── HighDpi.cs │ ├── TextBoxExtension.cs │ ├── ImageExtension.cs │ ├── FileExtension.cs │ ├── SingleInstance.cs │ ├── EncodingType.cs │ └── ElevatedFileDroper.cs ├── Methods │ ├── AppMessageBox.cs │ ├── DesktopIni.cs │ └── UwpHelper.cs └── Program.cs ├── Logo └── Logo.png ├── languages └── ru-RU.ini ├── Screenshot ├── AppImage.png ├── Screenshot.png ├── AppImage-dark.png ├── Screenshot-en.png ├── Screenshot-dark.png └── Screenshot-en-dark.png ├── .github ├── workflows │ ├── pr_assignee.yml │ ├── remove-old-artifacts.yml │ ├── build.yml │ └── publish.yml ├── dependabot.yml ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── feature_request.yml │ ├── feature_request_en.yml │ ├── bug_report.yml │ └── bug_report_en.yml ├── ContextMenuManager.sln ├── .gitattributes ├── README.md ├── README-en.md └── .gitignore /ContextMenuManager/Properties/Resources/ShellNew/0.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1} -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/ShellNew/0.zip: -------------------------------------------------------------------------------- 1 | PK -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/ShellNew/0.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Logo/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/Logo/Logo.png -------------------------------------------------------------------------------- /languages/ru-RU.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/languages/ru-RU.ini -------------------------------------------------------------------------------- /Screenshot/AppImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/Screenshot/AppImage.png -------------------------------------------------------------------------------- /Screenshot/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/Screenshot/Screenshot.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/ShellNew/0.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | return 0; 6 | } -------------------------------------------------------------------------------- /Screenshot/AppImage-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/Screenshot/AppImage-dark.png -------------------------------------------------------------------------------- /Screenshot/Screenshot-en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/Screenshot/Screenshot-en.png -------------------------------------------------------------------------------- /Screenshot/Screenshot-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/Screenshot/Screenshot-dark.png -------------------------------------------------------------------------------- /Screenshot/Screenshot-en-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/Screenshot/Screenshot-en-dark.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/AppIcon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/AppIcon.ico -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Add.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Up.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Web.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/ShellNew/0.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/ShellNew/0.rar -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/ShellNew/0.reg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/ShellNew/0.reg -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/About.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/About.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Home.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Jump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Jump.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Open.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Sort.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Star.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Type.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/User.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/User.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/ShellNew/0.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/ShellNew/0.xlsx -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Controls/MyStatusBar.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/BluePointLilac.Controls/MyStatusBar.cs -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Custom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Custom.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Delete.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Donate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Donate.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/DownLoad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/DownLoad.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Enhance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Enhance.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/NewItem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/NewItem.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Refresh.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Select.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Setting.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/SubItems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/SubItems.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/BackupItem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/BackupItem.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/NewFolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/NewFolder.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/Translate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/Translate.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/AddExisting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/AddExisting.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/AddSeparator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/AddSeparator.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/CheckUpdate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/CheckUpdate.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/MicrosoftStore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/MicrosoftStore.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Images/ContextMenuStyle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jack251970/ContextMenuManager/HEAD/ContextMenuManager/Properties/Resources/Images/ContextMenuStyle.png -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/ShellNew/0.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/IProtectOpenItem.cs: -------------------------------------------------------------------------------- 1 | namespace ContextMenuManager.Controls.Interfaces 2 | { 3 | interface IProtectOpenItem 4 | { 5 | bool ItemVisible { get; set; } 6 | bool TryProtectOpenItem(); 7 | } 8 | } -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ContextMenuManager/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/workflows/pr_assignee.yml: -------------------------------------------------------------------------------- 1 | name: Assign PR to creator 2 | 3 | on: 4 | pull_request_target: 5 | types: [opened] 6 | branches-ignore: 7 | - l10n_dev 8 | 9 | permissions: 10 | pull-requests: write 11 | 12 | jobs: 13 | automation: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Assign PR to creator 17 | uses: toshimaru/auto-author-assign@v2.1.1 18 | -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/StringExtension.cs: -------------------------------------------------------------------------------- 1 | namespace BluePointLilac.Methods 2 | { 3 | //为兼容.Net Framework 3.5,无法引用Microsoft.CSharp.dll(中的string.IsNullOrWhiteSpace)写了这个扩展方法 4 | public static class StringExtension 5 | { 6 | public static bool IsNullOrWhiteSpace(this string str) 7 | { 8 | if (string.IsNullOrEmpty(str)) return true; 9 | return str.Trim().Length == 0; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /ContextMenuManager/Methods/AppMessageBox.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using System.Windows.Forms; 3 | 4 | namespace ContextMenuManager.Methods 5 | { 6 | public static class AppMessageBox 7 | { 8 | public static DialogResult Show(string text, MessageBoxButtons buttons = MessageBoxButtons.OK, 9 | MessageBoxIcon icon = MessageBoxIcon.Warning, string caption = null) 10 | { 11 | return MessageBoxEx.Show(text, caption ?? AppString.General.AppName, buttons, icon); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/IBtnMoveUpDownItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using ContextMenuManager.Methods; 3 | 4 | namespace ContextMenuManager.Controls.Interfaces 5 | { 6 | interface IBtnMoveUpDownItem 7 | { 8 | MoveButton BtnMoveUp { get; set; } 9 | MoveButton BtnMoveDown { get; set; } 10 | } 11 | 12 | sealed class MoveButton : PictureButton 13 | { 14 | public MoveButton(IBtnMoveUpDownItem item, bool isUp) : base(isUp ? AppImage.Up : AppImage.Down) 15 | { 16 | ((MyListItem)item).AddCtr(this); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/ITsiRestoreItem.cs: -------------------------------------------------------------------------------- 1 | using ContextMenuManager.Methods; 2 | 3 | namespace ContextMenuManager.Controls.Interfaces 4 | { 5 | interface ITsiRestoreItem 6 | { 7 | DeleteMeMenuItem TsiDeleteMe { get; set; } 8 | void RestoreMe(); 9 | } 10 | 11 | sealed class RestoreMeMenuItem : RToolStripMenuItem 12 | { 13 | public RestoreMeMenuItem(ITsiRestoreItem item) : base(AppString.Menu.RestoreBackup) 14 | { 15 | Click += (sender, e) => 16 | { 17 | item.RestoreMe(); 18 | }; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/remove-old-artifacts.yml: -------------------------------------------------------------------------------- 1 | name: Remove old artifacts 2 | 3 | on: 4 | schedule: 5 | # Every day at 1am 6 | - cron: '0 1 * * *' 7 | 8 | jobs: 9 | remove-old-artifacts: 10 | runs-on: ubuntu-latest 11 | timeout-minutes: 10 12 | 13 | # For private repos 14 | permissions: 15 | actions: write 16 | 17 | steps: 18 | - name: Remove old artifacts 19 | uses: c-hive/gha-remove-artifacts@v1.4.0 20 | with: 21 | age: '7 days' # ' ', e.g. 5 days, 2 years, 90 seconds, parsed by Moment.js 22 | # Optional inputs 23 | # skip-tags: true 24 | # skip-recent: 5 -------------------------------------------------------------------------------- /ContextMenuManager/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("右键菜单管理器")] 5 | [assembly: AssemblyDescription("右键菜单管理器")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("蓝点lilac Jack251970")] 8 | [assembly: AssemblyProduct("右键菜单管理器")] 9 | [assembly: AssemblyCopyright("Copyright @ 2020-2025 蓝点lilac Jack251970")] 10 | [assembly: AssemblyTrademark("蓝点lilac Jack25197")] 11 | [assembly: AssemblyCulture("")] 12 | [assembly: ComVisible(false)] 13 | [assembly: Guid("35190ec1-2515-488d-a2e9-825d6ff67aa2")] 14 | [assembly: AssemblyVersion("3.6.0.0")] 15 | [assembly: AssemblyFileVersion("3.6.0.0")] -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "nuget" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | open-pull-requests-limit: 10 13 | - package-ecosystem: "github-actions" 14 | directory: "/" 15 | schedule: 16 | interval: "weekly" 17 | -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/ToolTipBox.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Forms; 2 | 3 | namespace BluePointLilac.Methods 4 | { 5 | public static class ToolTipBox 6 | { 7 | public static void SetToolTip(Control ctr, string tip) 8 | { 9 | if (tip.IsNullOrWhiteSpace()) return; 10 | ToolTip toolTip = new ToolTip { InitialDelay = 1 }; 11 | toolTip.SetToolTip(ctr, tip); 12 | ctr.Disposed += (sender, e) => toolTip.Dispose(); 13 | } 14 | 15 | public static void SetToolTip(ToolStripItem item, string tip) 16 | { 17 | //必须先设置item的Owner 18 | item.Owner.ShowItemToolTips = true; 19 | item.ToolTipText = tip; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/ITsiWebSearchItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using ContextMenuManager.Methods; 3 | 4 | namespace ContextMenuManager.Controls.Interfaces 5 | { 6 | interface ITsiWebSearchItem 7 | { 8 | string SearchText { get; } 9 | WebSearchMenuItem TsiSearch { get; set; } 10 | } 11 | 12 | sealed class WebSearchMenuItem : RToolStripMenuItem 13 | { 14 | public WebSearchMenuItem(ITsiWebSearchItem item) : base(AppString.Menu.WebSearch) 15 | { 16 | Click += (sender, e) => 17 | { 18 | string url = AppConfig.EngineUrl.Replace("%s", item.SearchText); 19 | ExternalProgram.OpenWebUrl(url); 20 | }; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/ComboBoxExtension.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using System; 3 | using System.Windows.Forms; 4 | 5 | namespace BluePointLilac.Methods 6 | { 7 | public static class ComboBoxExtension 8 | { 9 | public static void AutosizeDropDownWidth(this RComboBox box) 10 | { 11 | box.DropDown += (sender, e) => 12 | { 13 | int maxWidth = 0; 14 | foreach (var item in box.Items) 15 | { 16 | maxWidth = Math.Max(maxWidth, TextRenderer.MeasureText(item.ToString(), box.Font).Width); 17 | } 18 | maxWidth = Math.Max(maxWidth, box.Width); 19 | box.DropDownWidth = maxWidth; 20 | }; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /ContextMenuManager/Properties/PublishProfiles/Net9.0-AnyCPU.pubxml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | FileSystem 8 | Release 9 | Any CPU 10 | net9.0-windows 11 | ..\Output 12 | win-x64 13 | false 14 | true 15 | false 16 | false 17 | none 18 | 19 | -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Resources/Texts/UwpModeItemsDic.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/IBtnDeleteItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using ContextMenuManager.Methods; 3 | using System.Windows.Forms; 4 | 5 | namespace ContextMenuManager.Controls.Interfaces 6 | { 7 | interface IBtnDeleteItem 8 | { 9 | DeleteButton BtnDelete { get; set; } 10 | void DeleteMe(); 11 | } 12 | 13 | sealed class DeleteButton : PictureButton 14 | { 15 | public DeleteButton(IBtnDeleteItem item) : base(AppImage.Delete) 16 | { 17 | MyListItem listItem = (MyListItem)item; 18 | listItem.AddCtr(this); 19 | MouseDown += (sender, e) => 20 | { 21 | if (AppMessageBox.Show(AppString.Message.ConfirmDelete, MessageBoxButtons.YesNo) == DialogResult.Yes) item.DeleteMe(); 22 | }; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/NewItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System; 5 | 6 | namespace ContextMenuManager.Controls 7 | { 8 | class NewItem : MyListItem 9 | { 10 | public NewItem() : this(AppString.Other.NewItem) { } 11 | 12 | public NewItem(string text) 13 | { 14 | Text = text; 15 | Image = AppImage.NewItem; 16 | AddCtr(BtnAddNewItem); 17 | ToolTipBox.SetToolTip(BtnAddNewItem, text); 18 | BtnAddNewItem.MouseDown += (sender, e) => AddNewItem?.Invoke(); 19 | MouseDoubleClick += (sender, e) => AddNewItem?.Invoke(); 20 | 21 | } 22 | public Action AddNewItem; 23 | readonly PictureButton BtnAddNewItem = new PictureButton(AppImage.AddNewItem); 24 | } 25 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Controls/ControlExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Windows.Forms; 3 | 4 | namespace BluePointLilac.Controls 5 | { 6 | // 扩展方法,用于安全地设置控件样式 7 | public static class ControlExtensions 8 | { 9 | public static void SetStyle(this Control control, ControlStyles flag, bool value) 10 | { 11 | try 12 | { 13 | // 使用反射调用 SetStyle 方法 14 | MethodInfo method = typeof(Control).GetMethod("SetStyle", 15 | BindingFlags.NonPublic | BindingFlags.Instance); 16 | 17 | if (method != null) 18 | { 19 | method.Invoke(control, new object[] { flag, value }); 20 | } 21 | } 22 | catch 23 | { 24 | // 如果反射失败,忽略错误 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/DirectoryEx.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace BluePointLilac.Methods 4 | { 5 | public static class DirectoryEx 6 | { 7 | public static void CopyTo(string srcDirPath, string dstDirPath) 8 | { 9 | DirectoryInfo srcDi = new DirectoryInfo(srcDirPath); 10 | DirectoryInfo dstDi = new DirectoryInfo(dstDirPath); 11 | dstDi.Create(); 12 | foreach (FileInfo srcFi in srcDi.GetFiles()) 13 | { 14 | string dstFilePath = $@"{dstDirPath}\{srcFi.Name}"; 15 | srcFi.CopyTo(dstFilePath, true); 16 | } 17 | foreach (DirectoryInfo srcSubDi in srcDi.GetDirectories()) 18 | { 19 | DirectoryInfo dstSubDi = dstDi.CreateSubdirectory(srcSubDi.Name); 20 | CopyTo(srcSubDi.FullName, dstSubDi.FullName); 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/IBtnShowMenuItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using ContextMenuManager.Methods; 3 | using System.Windows.Forms; 4 | 5 | namespace ContextMenuManager.Controls.Interfaces 6 | { 7 | interface IBtnShowMenuItem 8 | { 9 | ContextMenuStrip ContextMenuStrip { get; set; } 10 | MenuButton BtnShowMenu { get; set; } 11 | } 12 | 13 | sealed class MenuButton : PictureButton 14 | { 15 | public MenuButton(IBtnShowMenuItem item) : base(AppImage.Setting) 16 | { 17 | item.ContextMenuStrip = new ContextMenuStrip(); 18 | ((MyListItem)item).AddCtr(this); 19 | bool isShow = false; 20 | MouseDown += (sender, e) => 21 | { 22 | if (!isShow) item.ContextMenuStrip.Show(this, 0, Height); 23 | else item.ContextMenuStrip.Close(); 24 | isShow = !isShow; 25 | }; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: https://ko-fi.com/jackye # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/ITsiRegPathItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using ContextMenuManager.Methods; 3 | using System.Windows.Forms; 4 | 5 | namespace ContextMenuManager.Controls.Interfaces 6 | { 7 | interface ITsiRegPathItem 8 | { 9 | string RegPath { get; } 10 | string ValueName { get; } 11 | ContextMenuStrip ContextMenuStrip { get; set; } 12 | RegLocationMenuItem TsiRegLocation { get; set; } 13 | } 14 | 15 | sealed class RegLocationMenuItem : RToolStripMenuItem 16 | { 17 | public RegLocationMenuItem(ITsiRegPathItem item) : base(AppString.Menu.RegistryLocation) 18 | { 19 | Click += (sender, e) => ExternalProgram.JumpRegEdit(item.RegPath, item.ValueName, AppConfig.OpenMoreRegedit); 20 | item.ContextMenuStrip.Opening += (sender, e) => 21 | { 22 | using (var key = RegistryEx.GetRegistryKey(item.RegPath)) 23 | Visible = key != null; 24 | }; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/EnhanceMenusDialog.cs: -------------------------------------------------------------------------------- 1 | using ContextMenuManager.Methods; 2 | using System; 3 | using System.Drawing; 4 | using System.Windows.Forms; 5 | 6 | namespace ContextMenuManager.Controls 7 | { 8 | sealed class EnhanceMenusDialog : CommonDialog 9 | { 10 | public string ScenePath { get; set; } 11 | 12 | public override void Reset() { } 13 | 14 | protected override bool RunDialog(IntPtr hwndOwner) 15 | { 16 | using (SubItemsForm frm = new SubItemsForm()) 17 | using (EnhanceMenuList list = new EnhanceMenuList()) 18 | { 19 | frm.Text = AppString.SideBar.EnhanceMenu; 20 | frm.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath); 21 | frm.TopMost = true; 22 | frm.AddList(list); 23 | list.ScenePath = ScenePath; 24 | list.UseUserDic = XmlDicHelper.EnhanceMenuPathDic[ScenePath]; 25 | list.LoadItems(); 26 | frm.ShowDialog(); 27 | } 28 | return false; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Controls/IconDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | using System.Windows.Forms; 5 | 6 | namespace BluePointLilac.Controls 7 | { 8 | public sealed class IconDialog : CommonDialog 9 | { 10 | [DllImport("shell32.dll", CharSet = CharSet.Unicode, EntryPoint = "#62", SetLastError = true)] 11 | private static extern bool PickIconDlg(IntPtr hWnd, StringBuilder pszFileName, int cchFileNameMax, ref int pnIconIndex); 12 | 13 | private const int MAXLENGTH = 260; 14 | private int iconIndex; 15 | public int IconIndex { get => iconIndex; set => iconIndex = value; } 16 | public string IconPath { get; set; } 17 | 18 | public override void Reset() { } 19 | 20 | protected override bool RunDialog(IntPtr hwndOwner) 21 | { 22 | StringBuilder sb = new StringBuilder(IconPath, MAXLENGTH); 23 | bool flag = PickIconDlg(hwndOwner, sb, MAXLENGTH, ref iconIndex); 24 | IconPath = flag ? sb.ToString() : null; 25 | return flag; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /ContextMenuManager/Program.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System; 5 | using System.IO; 6 | using System.Windows.Forms; 7 | 8 | namespace ContextMenuManager 9 | { 10 | //兼容.Net3.5和.Net4.0,兼容Vista - Win11 11 | static class Program 12 | { 13 | [STAThread] 14 | static void Main() 15 | { 16 | #if DEBUG 17 | if (AppConfig.EnableLog) 18 | { 19 | using (StreamWriter sw = new StreamWriter(AppConfig.DebugLogPath, true)) 20 | { 21 | sw.WriteLine($@"--------------------{DateTime.Now:yyyy-MM-dd HH:mm:ss}--------------------"); 22 | } 23 | } 24 | #endif 25 | MyMainForm.InitColors(); 26 | Application.EnableVisualStyles(); 27 | Application.SetCompatibleTextRenderingDefault(false); 28 | if (SingleInstance.IsRunning()) return; 29 | AppString.LoadStrings(); 30 | Updater.PeriodicUpdate(); 31 | XmlDicHelper.ReloadDics(); 32 | Application.Run(new MainForm()); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/GuidEx.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | 4 | namespace BluePointLilac.Methods 5 | { 6 | //为兼容.Net Framework 3.5,无法引用Microsoft.CSharp.dll(中的Guid.TryParse)写了这个扩展方法 7 | public static class GuidEx 8 | { 9 | public static bool TryParse(string str, out Guid guid) 10 | { 11 | if (IsGuid(str)) 12 | { 13 | guid = new Guid(str); 14 | return true; 15 | } 16 | else 17 | { 18 | guid = Guid.Empty; 19 | return false; 20 | } 21 | } 22 | 23 | private static readonly Regex GuidRegex = new Regex(@"[A-F0-9]{8}(\-[A-F0-9]{4}){3}\-[A-F0-9]{12}", RegexOptions.IgnoreCase); 24 | 25 | public static bool IsGuid(string str) 26 | { 27 | if (string.IsNullOrEmpty(str)) return false; 28 | if (str.Length == 38 && str.StartsWith("{") && str.EndsWith("}") && GuidRegex.IsMatch(str)) return true; 29 | if (str.Length == 36 && GuidRegex.IsMatch(str)) return true; 30 | return false; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/IChkVisibleItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using ContextMenuManager.Methods; 3 | 4 | namespace ContextMenuManager.Controls.Interfaces 5 | { 6 | interface IChkVisibleItem 7 | { 8 | bool ItemVisible { get; set; } 9 | VisibleCheckBox ChkVisible { get; set; } 10 | } 11 | 12 | sealed class VisibleCheckBox : MyCheckBox 13 | { 14 | public VisibleCheckBox(IChkVisibleItem item) 15 | { 16 | MyListItem listItem = (MyListItem)item; 17 | listItem.AddCtr(this); 18 | CheckChanged += () => item.ItemVisible = Checked; 19 | listItem.ParentChanged += (sender, e) => 20 | { 21 | if (listItem.IsDisposed) return; 22 | if (listItem.Parent == null) return; 23 | Checked = item.ItemVisible; 24 | if (listItem is FoldSubItem subItem && subItem.FoldGroupItem != null) return; 25 | if (listItem.FindForm() is ShellStoreDialog.ShellStoreForm) return; 26 | if (AppConfig.HideDisabledItems) listItem.Visible = Checked; 27 | }; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /ContextMenuManager/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ContextMenuManager.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.6.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ContextMenuManager/Properties/App.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | true 23 | 24 | 25 | -------------------------------------------------------------------------------- /ContextMenuManager/Controls/DetailedEditDialog.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using ContextMenuManager.Methods; 3 | using System; 4 | using System.Windows.Forms; 5 | 6 | namespace ContextMenuManager.Controls 7 | { 8 | sealed class DetailedEditDialog : CommonDialog 9 | { 10 | public Guid GroupGuid { get; set; } 11 | 12 | public override void Reset() { } 13 | 14 | protected override bool RunDialog(IntPtr hwndOwner) 15 | { 16 | using (SubItemsForm frm = new SubItemsForm()) 17 | using (DetailedEditList list = new DetailedEditList()) 18 | { 19 | var location = GuidInfo.GetIconLocation(GroupGuid); 20 | frm.Icon = ResourceIcon.GetIcon(location.IconPath, location.IconIndex); 21 | frm.Text = AppString.Dialog.DetailedEdit.Replace("%s", GuidInfo.GetText(GroupGuid)); 22 | frm.TopMost = true; 23 | frm.AddList(list); 24 | list.GroupGuid = GroupGuid; 25 | list.UseUserDic = XmlDicHelper.DetailedEditGuidDic[GroupGuid]; 26 | list.LoadItems(); 27 | frm.ShowDialog(); 28 | } 29 | return false; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: 新功能提交 2 | description: 写下您对添加新功能的一些建议 3 | title: "[Feat]: " 4 | labels: [ "enhancement" ] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: "# ContextMenuManager 新功能请求" 9 | - type: checkboxes 10 | id: feature_check 11 | attributes: 12 | label: 检查项 13 | description: 在提交前,请确认以下内容: 14 | options: 15 | - label: 此功能未被提出过(可以在[这里](https://github.com/Jack251970/ContextMenuManager/issues?q=is%3Aissue)进行搜索) 16 | required: true 17 | - label: ContextMenuManager 的最新版本并未添加此功能 18 | required: true 19 | - label: 我已知晓:如果只是某些想法,而不是具体的新功能,需要在 [Discussions](https://github.com/Jack251970/ContextMenuManager/discussions/new?category=%E6%83%B3%E6%B3%95) 中提交 20 | required: true 21 | - type: input 22 | id: feature_summary 23 | attributes: 24 | label: 简述 25 | description: 请简要叙述您所希望添加的新功能 26 | placeholder: | 27 | <对此功能的简要描述> 28 | - type: textarea 29 | id: feature_details 30 | attributes: 31 | label: 详细内容 32 | description: 请详细描述需要此功能的原因,以及此功能的内容 33 | placeholder: | 34 | <需要此功能的原因,以及此功能的详细内容> 35 | - type: markdown 36 | attributes: 37 | value: "### 感谢您对 ContextMenuManager 的支持!" 38 | -------------------------------------------------------------------------------- /ContextMenuManager/Methods/DesktopIni.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using System.IO; 3 | 4 | namespace ContextMenuManager.Methods 5 | { 6 | static class DesktopIni 7 | { 8 | private const string LocalizedFileNames = "LocalizedFileNames"; 9 | 10 | public static string GetDesktopIniPath(string filePath) => $@"{Path.GetDirectoryName(filePath)}\desktop.ini"; 11 | 12 | public static void DeleteLocalizedFileNames(string filePath) 13 | { 14 | IniWriter writer = new IniWriter(GetDesktopIniPath(filePath)); 15 | string fileName = Path.GetFileName(filePath); 16 | writer.DeleteKey(LocalizedFileNames, fileName); 17 | } 18 | 19 | public static void SetLocalizedFileNames(string filePath, string name) 20 | { 21 | IniWriter writer = new IniWriter(GetDesktopIniPath(filePath)); 22 | string fileName = Path.GetFileName(filePath); 23 | writer.SetValue(LocalizedFileNames, fileName, name); 24 | } 25 | 26 | public static string GetLocalizedFileNames(string filePath, bool translate = false) 27 | { 28 | IniWriter writer = new IniWriter(GetDesktopIniPath(filePath)); 29 | string fileName = Path.GetFileName(filePath); 30 | string name = writer.GetValue(LocalizedFileNames, fileName); 31 | if (translate) name = ResourceString.GetDirectString(name); 32 | return name; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | paths-ignore: 9 | - '*.md' 10 | pull_request: 11 | paths-ignore: 12 | - '*.md' 13 | 14 | jobs: 15 | 16 | build: 17 | 18 | permissions: 19 | contents: write 20 | pull-requests: write 21 | 22 | runs-on: windows-latest 23 | 24 | env: 25 | Solution_Path: ContextMenuManager.sln 26 | 27 | steps: 28 | 29 | # Checkout codes 30 | - name: Checkout 31 | uses: actions/checkout@v6 32 | 33 | # Install the .NET Core workload 34 | - name: Setup .NET 35 | uses: actions/setup-dotnet@v5 36 | with: 37 | dotnet-version: | 38 | 9.0.x 39 | 40 | # Restore dependencies for entire solution 41 | - name: Restore dependencies 42 | run: dotnet restore ${{ env.Solution_Path }} 43 | 44 | # Build the entire solution 45 | - name: Build 46 | run: dotnet build ${{ env.Solution_Path }} --configuration Release 47 | 48 | # Execute all unit tests in the solution 49 | - name: Execute unit tests 50 | run: dotnet test ${{ env.Solution_Path }} --configuration Release --no-build 51 | 52 | # Upload the Build package 53 | - name: Upload Build package 54 | uses: actions/upload-artifact@v6 55 | with: 56 | retention-days: 7 57 | path: | 58 | ContextMenuManager/bin/Release/net9.0-windows 59 | compression-level: 0 60 | -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/ITsiFilePathItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using ContextMenuManager.Methods; 3 | using System.IO; 4 | using System.Windows.Forms; 5 | 6 | namespace ContextMenuManager.Controls.Interfaces 7 | { 8 | interface ITsiFilePathItem 9 | { 10 | string ItemFilePath { get; } 11 | ContextMenuStrip ContextMenuStrip { get; set; } 12 | FileLocationMenuItem TsiFileLocation { get; set; } 13 | FilePropertiesMenuItem TsiFileProperties { get; set; } 14 | } 15 | 16 | sealed class FileLocationMenuItem : RToolStripMenuItem 17 | { 18 | public FileLocationMenuItem(ITsiFilePathItem item) : base(AppString.Menu.FileLocation) 19 | { 20 | item.ContextMenuStrip.Opening += (sender, e) => 21 | { 22 | Visible = item.ItemFilePath != null; 23 | }; 24 | Click += (sender, e) => ExternalProgram.JumpExplorer(item.ItemFilePath, AppConfig.OpenMoreExplorer); 25 | } 26 | } 27 | 28 | sealed class FilePropertiesMenuItem : RToolStripMenuItem 29 | { 30 | public FilePropertiesMenuItem(ITsiFilePathItem item) : base(AppString.Menu.FileProperties) 31 | { 32 | item.ContextMenuStrip.Opening += (sender, e) => 33 | { 34 | string path = item.ItemFilePath; 35 | Visible = Directory.Exists(path) || File.Exists(path); 36 | }; 37 | Click += (sender, e) => ExternalProgram.ShowPropertiesDialog(item.ItemFilePath); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/RToolStripMenuItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using System; 3 | using System.Drawing; 4 | using System.Windows.Forms; 5 | 6 | namespace ContextMenuManager.Controls 7 | { 8 | internal class RToolStripMenuItem : ToolStripMenuItem 9 | { 10 | public RToolStripMenuItem() : base() 11 | { 12 | ForeColor = MyMainForm.FormFore; 13 | BackColor = MyMainForm.FormBack; 14 | } 15 | 16 | public RToolStripMenuItem(string text) : base(text) 17 | { 18 | ForeColor = MyMainForm.FormFore; 19 | BackColor = MyMainForm.FormBack; 20 | } 21 | 22 | public RToolStripMenuItem(string text, Image image) : base(text, image) 23 | { 24 | ForeColor = MyMainForm.FormFore; 25 | BackColor = MyMainForm.FormBack; 26 | } 27 | 28 | public RToolStripMenuItem(string text, Image image, EventHandler onClick) : base(text, image, onClick) 29 | { 30 | ForeColor = MyMainForm.FormFore; 31 | BackColor = MyMainForm.FormBack; 32 | } 33 | 34 | public RToolStripMenuItem(string text, Image image, EventHandler onClick, string name) : base(text, image, onClick, name) 35 | { 36 | ForeColor = MyMainForm.FormFore; 37 | BackColor = MyMainForm.FormBack; 38 | } 39 | } 40 | 41 | public class RToolStripSeparator : ToolStripSeparator 42 | { 43 | public RToolStripSeparator() : base() 44 | { 45 | ForeColor = MyMainForm.FormFore; 46 | BackColor = MyMainForm.FormBack; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/ITsiCommandItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System.Drawing; 5 | using System.Windows.Forms; 6 | 7 | namespace ContextMenuManager.Controls.Interfaces 8 | { 9 | interface ITsiCommandItem 10 | { 11 | string ItemCommand { get; set; } 12 | ChangeCommandMenuItem TsiChangeCommand { get; set; } 13 | } 14 | 15 | sealed class ChangeCommandMenuItem : RToolStripMenuItem 16 | { 17 | public bool CommandCanBeEmpty { get; set; } 18 | 19 | public ChangeCommandMenuItem(ITsiCommandItem item) : base(AppString.Menu.ChangeCommand) 20 | { 21 | Click += (sender, e) => 22 | { 23 | string command = ChangeCommand(item.ItemCommand); 24 | if (command != null) item.ItemCommand = command; 25 | }; 26 | } 27 | 28 | private string ChangeCommand(string command) 29 | { 30 | using (InputDialog dlg = new InputDialog()) 31 | { 32 | dlg.Text = command; 33 | dlg.Title = AppString.Menu.ChangeCommand; 34 | dlg.Size = new Size(530, 260).DpiZoom(); 35 | if (dlg.ShowDialog() != DialogResult.OK) return null; 36 | if (!CommandCanBeEmpty && string.IsNullOrEmpty(dlg.Text)) 37 | { 38 | AppMessageBox.Show(AppString.Message.CommandCannotBeEmpty); 39 | return ChangeCommand(command); 40 | } 41 | else return dlg.Text; 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/FormExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Windows.Forms; 3 | 4 | namespace BluePointLilac.Methods 5 | { 6 | public static class FormExtension 7 | { 8 | /// 移动窗体时同时移动另一个窗体 9 | /// 主动移动的窗体 10 | /// 同时被移动的窗体 11 | public static void MoveAsMove(this Form frm1, Form frm2) 12 | { 13 | if (frm2 == null) return; 14 | Point pLast = Point.Empty; 15 | frm1.Load += (sender, e) => pLast = frm1.Location; 16 | frm1.LocationChanged += (sender, e) => 17 | { 18 | if (pLast == Point.Empty) return; 19 | frm2.Left += frm1.Left - pLast.X; 20 | frm2.Top += frm1.Top - pLast.Y; 21 | pLast = frm1.Location; 22 | }; 23 | } 24 | 25 | /// 给窗体添加ESC键关闭功能 26 | /// 指定窗口 27 | /// 关闭窗口时的对话框返回值 28 | /// 也可以重写Form的ProcessDialogKey事件, 29 | /// 这个方法更简单,但遍历窗体控件时切记多了一个不可见的关闭按钮 30 | public static void AddEscapeButton(this Form frm, DialogResult dr = DialogResult.Cancel) 31 | { 32 | Button btn = new Button 33 | { 34 | Parent = frm, 35 | Size = Size.Empty, 36 | DialogResult = dr 37 | }; 38 | frm.CancelButton = btn; 39 | frm.Disposed += (sender, e) => btn.Dispose(); 40 | frm.FormClosing += (sender, e) => btn.PerformClick(); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/ITsiIconItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System.Drawing; 5 | using System.Windows.Forms; 6 | 7 | namespace ContextMenuManager.Controls.Interfaces 8 | { 9 | interface ITsiIconItem 10 | { 11 | ChangeIconMenuItem TsiChangeIcon { get; set; } 12 | string IconLocation { get; set; } 13 | string IconPath { get; set; } 14 | int IconIndex { get; set; } 15 | Image Image { get; set; } 16 | Icon ItemIcon { get; } 17 | } 18 | 19 | sealed class ChangeIconMenuItem : RToolStripMenuItem 20 | { 21 | public ChangeIconMenuItem(ITsiIconItem item) : base(AppString.Menu.ChangeIcon) 22 | { 23 | Click += (sender, e) => 24 | { 25 | using (IconDialog dlg = new IconDialog()) 26 | { 27 | dlg.IconPath = item.IconPath; 28 | dlg.IconIndex = item.IconIndex; 29 | if (dlg.ShowDialog() != DialogResult.OK) return; 30 | using (Icon icon = ResourceIcon.GetIcon(dlg.IconPath, dlg.IconIndex)) 31 | { 32 | Image image = icon?.ToBitmap(); 33 | if (image == null) return; 34 | item.Image = image; 35 | item.IconPath = dlg.IconPath; 36 | item.IconIndex = dlg.IconIndex; 37 | item.IconLocation = $"{dlg.IconPath},{dlg.IconIndex}"; 38 | } 39 | } 40 | }; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/ITsiTextItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System.Windows.Forms; 5 | 6 | namespace ContextMenuManager.Controls.Interfaces 7 | { 8 | interface ITsiTextItem 9 | { 10 | string Text { get; set; } 11 | string ItemText { get; set; } 12 | ChangeTextMenuItem TsiChangeText { get; set; } 13 | } 14 | 15 | sealed class ChangeTextMenuItem : RToolStripMenuItem 16 | { 17 | public ChangeTextMenuItem(ITsiTextItem item) : base(AppString.Menu.ChangeText) 18 | { 19 | Click += (sender, e) => 20 | { 21 | string name = ChangeText(item.Text); 22 | if (name != null) item.ItemText = name; 23 | }; 24 | } 25 | 26 | private string ChangeText(string text) 27 | { 28 | using (InputDialog dlg = new InputDialog { Text = text, Title = AppString.Menu.ChangeText }) 29 | { 30 | if (dlg.ShowDialog() != DialogResult.OK) return null; 31 | if (dlg.Text.Length == 0) 32 | { 33 | AppMessageBox.Show(AppString.Message.TextCannotBeEmpty); 34 | return ChangeText(text); 35 | } 36 | else if (ResourceString.GetDirectString(dlg.Text).Length == 0) 37 | { 38 | AppMessageBox.Show(AppString.Message.StringParsingFailed); 39 | return ChangeText(text); 40 | } 41 | else return dlg.Text; 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/ResourceString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace BluePointLilac.Methods 6 | { 7 | public static class ResourceString 8 | { 9 | //MSDN文档: https://docs.microsoft.com/windows/win32/api/shlwapi/nf-shlwapi-shloadindirectstring 10 | //提取.pri文件资源: https://docs.microsoft.com/windows/uwp/app-resources/makepri-exe-command-options 11 | //.pri转储.xml资源列表: MakePri.exe dump /if [priPath] /of [xmlPath] 12 | 13 | [DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode, 14 | ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)] 15 | private static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, IntPtr ppvReserved); 16 | 17 | /// 获取格式为"@[filename],-[strID]"或"@{[packageName]?ms-resource://[resPath]}"的直接字符串 18 | /// 要转换的字符串 19 | /// resStr为Null时返回值为string.Empty; resStr首字符为@但解析失败时返回string.Empty 20 | /// [fileName]:文件路径; [strID]:字符串资源索引; [packageName]:UWP带版本号包名; [resPath]:pri资源路径 21 | public static string GetDirectString(string resStr) 22 | { 23 | StringBuilder outBuff = new StringBuilder(1024); 24 | SHLoadIndirectString(resStr, outBuff, 1024, IntPtr.Zero); 25 | return outBuff.ToString(); 26 | } 27 | 28 | public static readonly string OK = GetDirectString("@shell32.dll,-9752"); 29 | public static readonly string Cancel = GetDirectString("@shell32.dll,-9751"); 30 | } 31 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/SubItemsForm.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System.Drawing; 5 | using System.Windows.Forms; 6 | 7 | namespace ContextMenuManager.Controls 8 | { 9 | sealed class SubItemsForm : RForm 10 | { 11 | public SubItemsForm() 12 | { 13 | SuspendLayout(); 14 | StartPosition = FormStartPosition.CenterParent; 15 | ShowInTaskbar = MaximizeBox = MinimizeBox = false; 16 | MinimumSize = Size = new Size(646, 419).DpiZoom(); 17 | Controls.AddRange(new Control[] { listBox, statusBar }); 18 | statusBar.CanMoveForm(); 19 | this.AddEscapeButton(); 20 | ResumeLayout(); 21 | InitTheme(); 22 | } 23 | 24 | readonly MyListBox listBox = new MyListBox { Dock = DockStyle.Fill }; 25 | readonly MyStatusBar statusBar = new MyStatusBar(); 26 | 27 | public void AddList(MyList myList) 28 | { 29 | myList.Owner = listBox; 30 | myList.HoveredItemChanged += (sender, e) => 31 | { 32 | if (!AppConfig.ShowFilePath) return; 33 | MyListItem item = myList.HoveredItem; 34 | foreach (string prop in new[] { "ItemFilePath", "RegPath", "GroupPath" }) 35 | { 36 | string path = item.GetType().GetProperty(prop)?.GetValue(item, null)?.ToString(); 37 | if (!path.IsNullOrWhiteSpace()) { statusBar.Text = path; return; } 38 | } 39 | statusBar.Text = item.Text; 40 | }; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/ExplorerRestarter.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System; 5 | using System.Windows.Forms; 6 | 7 | namespace ContextMenuManager.Controls 8 | { 9 | sealed class ExplorerRestarter : MyListItem 10 | { 11 | public ExplorerRestarter() 12 | { 13 | Visible = false; 14 | DoubleBuffered = false; 15 | Dock = DockStyle.Bottom; 16 | Image = AppImage.Explorer; 17 | Text = AppString.Other.RestartExplorer; 18 | ToolTipBox.SetToolTip(BtnRestart, AppString.Tip.RestartExplorer); 19 | AddCtr(BtnRestart); 20 | this.CanMoveForm(); 21 | ShowHandler += () => Visible = true; 22 | HideHandler += () => Visible = false; 23 | BtnRestart.MouseDown += (sender, e) => 24 | { 25 | ExternalProgram.RestartExplorer(); 26 | Visible = false; 27 | }; 28 | } 29 | 30 | public new bool Visible 31 | { 32 | get => base.Visible; 33 | set 34 | { 35 | bool flag = base.Visible != value && Parent != null; 36 | base.Visible = value; 37 | if (flag) Parent.Height += value ? Height : -Height; 38 | } 39 | } 40 | 41 | private readonly PictureButton BtnRestart = new PictureButton(AppImage.RestartExplorer); 42 | 43 | private static Action ShowHandler; 44 | private static Action HideHandler; 45 | 46 | public static new void Show() => ShowHandler?.Invoke(); 47 | public static new void Hide() => HideHandler?.Invoke(); 48 | } 49 | } -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | tags: 7 | - '*' 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | permissions: 14 | contents: write 15 | 16 | runs-on: windows-latest 17 | 18 | env: 19 | Project_Path: ContextMenuManager/ContextMenuManager.csproj 20 | 21 | steps: 22 | 23 | # Checkout codes 24 | - name: Checkout 25 | uses: actions/checkout@v6 26 | 27 | # Install the .NET Core workload 28 | - name: Setup .NET 29 | uses: actions/setup-dotnet@v5 30 | with: 31 | dotnet-version: | 32 | 9.0.x 33 | 34 | # Restore dependencies 35 | - name: Restore dependencies 36 | run: dotnet restore ${{ env.Project_Path }} 37 | 38 | # Build the project 39 | - name: Build 40 | run: dotnet build ${{ env.Project_Path }} --configuration Release --no-restore 41 | 42 | # Get package version 43 | - name: Get Package Version 44 | run: | 45 | $version = [system.diagnostics.fileversioninfo]::getversioninfo("ContextMenuManager\bin\Release\net9.0-windows\ContextMenuManager.dll").fileversion 46 | echo "release_version=$version" | out-file -filepath $env:github_env -encoding utf-8 -append 47 | 48 | # Publish the project 49 | - name: Publish 50 | run: dotnet publish ${{ env.Project_Path }} -p:PublishProfile=Net9.0-AnyCPU.pubxml 51 | 52 | # Publish to GitHub releases 53 | - name: Publish 54 | uses: softprops/action-gh-release@v2 55 | with: 56 | files: | 57 | Output/ContextMenuManager* 58 | tag_name: "${{ env.release_version }}" 59 | generate_release_notes: true 60 | -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/WinOsVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BluePointLilac.Methods 4 | { 5 | // 判断Windows系统版本 6 | // https://docs.microsoft.com/windows/release-health/release-information 7 | public static class WinOsVersion 8 | { 9 | public static readonly Version Current = Environment.OSVersion.Version; 10 | public static readonly Version Win11 = new Version(10, 0, 22000); 11 | public static readonly Version Win10 = new Version(10, 0); 12 | public static readonly Version Win8_1 = new Version(6, 3); 13 | public static readonly Version Win8 = new Version(6, 2); 14 | public static readonly Version Win7 = new Version(6, 1); 15 | public static readonly Version Vista = new Version(6, 0); 16 | public static readonly Version XP = new Version(5, 1); 17 | 18 | public static readonly Version Win10_1507 = new Version(10, 0, 10240); 19 | public static readonly Version Win10_1511 = new Version(10, 0, 10586); 20 | public static readonly Version Win10_1607 = new Version(10, 0, 14393); 21 | public static readonly Version Win10_1703 = new Version(10, 0, 15063); 22 | public static readonly Version Win10_1709 = new Version(10, 0, 16299); 23 | public static readonly Version Win10_1803 = new Version(10, 0, 17134); 24 | public static readonly Version Win10_1809 = new Version(10, 0, 17763); 25 | public static readonly Version Win10_1903 = new Version(10, 0, 18362); 26 | public static readonly Version Win10_1909 = new Version(10, 0, 18363); 27 | public static readonly Version Win10_2004 = new Version(10, 0, 19041); 28 | public static readonly Version Win10_20H2 = new Version(10, 0, 19042); 29 | } 30 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request_en.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Share your suggestions for new features 3 | title: "[Feat]: " 4 | labels: [ "enhancement" ] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: "# ContextMenuManager Feature Request" 9 | - type: checkboxes 10 | id: feature_check 11 | attributes: 12 | label: Checklist 13 | description: "Before submitting, please confirm the following:" 14 | options: 15 | - label: "This feature has not already been requested (search [here](https://github.com/Jack251970/ContextMenuManager/issues?q=is%3Aissue))" 16 | required: true 17 | - label: "The latest version of ContextMenuManager does not include this feature" 18 | required: true 19 | - label: "I understand: general ideas (not specific features) should be posted in [Discussions](https://github.com/Jack251970/ContextMenuManager/discussions/new?category=%E6%83%B3%E6%B3%95)" 20 | required: true 21 | - type: input 22 | id: feature_summary 23 | attributes: 24 | label: Summary 25 | description: "Briefly describe the feature you'd like to add" 26 | placeholder: | 27 | 28 | - type: textarea 29 | id: feature_details 30 | attributes: 31 | label: Details 32 | description: "Describe why this feature is needed and what it should do" 33 | placeholder: | 34 | 35 | - type: markdown 36 | attributes: 37 | value: "### Thank you for supporting ContextMenuManager!" 38 | -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/ITsiAdministratorItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using ContextMenuManager.Methods; 3 | using System.IO; 4 | using System.Windows.Forms; 5 | 6 | namespace ContextMenuManager.Controls.Interfaces 7 | { 8 | interface ITsiAdministratorItem 9 | { 10 | ContextMenuStrip ContextMenuStrip { get; set; } 11 | RunAsAdministratorItem TsiAdministrator { get; set; } 12 | ShellLink ShellLink { get; } 13 | } 14 | 15 | sealed class RunAsAdministratorItem : RToolStripMenuItem 16 | { 17 | public RunAsAdministratorItem(ITsiAdministratorItem item) : base(AppString.Menu.RunAsAdministrator) 18 | { 19 | item.ContextMenuStrip.Opening += (sender, e) => 20 | { 21 | if (item.ShellLink == null) 22 | { 23 | Enabled = false; 24 | return; 25 | } 26 | string filePath = item.ShellLink.TargetPath; 27 | string extension = Path.GetExtension(filePath)?.ToLower(); 28 | switch (extension) 29 | { 30 | case ".exe": 31 | case ".bat": 32 | case ".cmd": 33 | Enabled = true; 34 | break; 35 | default: 36 | Enabled = false; 37 | break; 38 | } 39 | Checked = item.ShellLink.RunAsAdministrator; 40 | }; 41 | Click += (sender, e) => 42 | { 43 | item.ShellLink.RunAsAdministrator = !Checked; 44 | item.ShellLink.Save(); 45 | if (item is WinXItem) ExplorerRestarter.Show(); 46 | }; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/SearchableListBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace ContextMenuManager.Controls 7 | { 8 | public abstract class SearchableListBase : UserControl 9 | { 10 | public virtual void SearchItems(string searchText) 11 | { 12 | // 默认实现:搜索所有 MyListItem 控件 13 | var items = GetAllItems(); 14 | 15 | if (string.IsNullOrWhiteSpace(searchText)) 16 | { 17 | // 清空搜索,显示所有项 18 | foreach (var item in items) 19 | { 20 | item.Visible = true; 21 | if (item is BluePointLilac.Controls.MyListItem myItem) 22 | { 23 | myItem.HighlightText = null; 24 | } 25 | } 26 | return; 27 | } 28 | 29 | // 搜索所有列表项 30 | foreach (var item in items) 31 | { 32 | bool matches = item.Text.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0; 33 | item.Visible = matches; 34 | 35 | if (item is BluePointLilac.Controls.MyListItem myItem) 36 | { 37 | if (matches) 38 | { 39 | myItem.HighlightText = searchText; 40 | } 41 | else 42 | { 43 | myItem.HighlightText = null; 44 | } 45 | } 46 | } 47 | } 48 | 49 | public virtual IEnumerable GetAllItems() 50 | { 51 | // 默认实现:返回所有子控件 52 | return Controls.Cast(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/FileExtensionDialog.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace ContextMenuManager.Controls 8 | { 9 | sealed class FileExtensionDialog : SelectDialog 10 | { 11 | public string Extension 12 | { 13 | get => Selected.Trim(); 14 | set => Selected = value?.Trim(); 15 | } 16 | 17 | public FileExtensionDialog() 18 | { 19 | CanEdit = true; 20 | Title = AppString.Dialog.SelectExtension; 21 | Items = FileExtensionItems.ToArray(); 22 | } 23 | 24 | public static List FileExtensionItems 25 | { 26 | get 27 | { 28 | List items = new List(); 29 | using (var key = RegistryEx.GetRegistryKey(FileExtension.FILEEXTSPATH)) 30 | { 31 | if (key != null) 32 | { 33 | foreach (string keyName in key.GetSubKeyNames()) 34 | { 35 | if (keyName.StartsWith(".")) items.Add(keyName.Substring(1)); 36 | } 37 | } 38 | } 39 | return items; 40 | } 41 | } 42 | 43 | protected override bool RunDialog(IntPtr hwndOwner) 44 | { 45 | bool flag = base.RunDialog(hwndOwner); 46 | if (flag) 47 | { 48 | string extension = ObjectPath.RemoveIllegalChars(Extension); 49 | int index = extension.LastIndexOf('.'); 50 | if (index >= 0) Extension = extension.Substring(index); 51 | else Extension = $".{extension}"; 52 | } 53 | return flag; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/ControlExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Windows.Forms; 4 | 5 | namespace BluePointLilac.Methods 6 | { 7 | public static class ControlExtension 8 | { 9 | [DllImport("user32.dll")] 10 | private static extern int PostMessage(IntPtr hWnd, int Msg, int wParam, int lParam); 11 | 12 | [DllImport("user32.dll")] 13 | private static extern bool ReleaseCapture(); 14 | 15 | /// 使控件能够移动所属窗体 16 | /// 目标控件 17 | public static void CanMoveForm(this Control ctr) 18 | { 19 | const int WM_NCLBUTTONDOWN = 0xA1; 20 | const int HT_CAPTION = 0x2; 21 | ctr.MouseMove += (sender, e) => 22 | { 23 | if (e.Button != MouseButtons.Left) return; 24 | ReleaseCapture(); 25 | PostMessage(ctr.FindForm().Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0); 26 | }; 27 | } 28 | 29 | [DllImport("user32.dll")] 30 | private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int wndproc); 31 | 32 | [DllImport("user32.dll")] 33 | private static extern int GetWindowLong(IntPtr hWnd, int nIndex); 34 | 35 | /// 通过Win32API禁用/启用目标控件 36 | /// 控件被禁用时仍可更改字体颜色,不需要同时设置ctr.Enabled=false 37 | /// 目标控件 38 | /// 启用为true,禁用为false 39 | public static void SetEnabled(this Control ctr, bool enabled) 40 | { 41 | const int GWL_STYLE = -16; 42 | const int WS_DISABLED = 0x8000000; 43 | int value = GetWindowLong(ctr.Handle, GWL_STYLE); 44 | if (enabled) value &= ~WS_DISABLED; 45 | else value |= WS_DISABLED; 46 | SetWindowLong(ctr.Handle, GWL_STYLE, value); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 错误报告 2 | description: 报告 ContextMenuManager 出现的错误 3 | title: "[Bug]: " 4 | labels: [ "bug" ] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: "# ContextMenuManager 错误报告" 9 | - type: checkboxes 10 | id: bug_check 11 | attributes: 12 | label: 检查项 13 | description: 在报告前,请确认以下内容: 14 | options: 15 | - label: 此错误未被报告过(可以在[这里](https://github.com/Jack251970/ContextMenuManager/issues?q=is%3Aissue)进行搜索) 16 | required: true 17 | - label: 我已将 ContextMenuManager 更新到最新版本 18 | required: true 19 | - label: 我已知晓:任何不合规定的 issue 都有可能被直接关闭 20 | required: true 21 | - type: dropdown 22 | id: bug_os 23 | attributes: 24 | label: Windows 25 | description: 您使用的Windows版本 26 | options: 27 | - Windows 7 28 | - Windows 8.x 29 | - Windows 10 30 | - Windows 11 31 | default: 3 32 | validations: 33 | required: true 34 | - type: input 35 | id: bug_version 36 | attributes: 37 | label: 版本 38 | description: 您发现此错误使用的 ContextMenuManager 版本 39 | placeholder: ContextMenuManager版本:x.x.x.x 40 | validations: 41 | required: true 42 | - type: textarea 43 | id: bug_reproducer 44 | attributes: 45 | label: 复现流程 46 | description: 请简单描述如何触发这一个错误 47 | placeholder: | 48 | 1. xxx 49 | 2. xxx 50 | 3. xxx 51 | validations: 52 | required: true 53 | - type: textarea 54 | id: bug_logs 55 | attributes: 56 | label: 崩溃日志 57 | description: 把程序崩溃时出现的日志粘贴到文本框里 58 | - type: markdown 59 | attributes: 60 | value: "### 感谢您对 ContextMenuManager 的支持!" 61 | -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/HighDpi.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Windows; 3 | using System.Windows.Forms; 4 | using Point = System.Drawing.Point; 5 | using Size = System.Drawing.Size; 6 | 7 | namespace BluePointLilac.Methods 8 | { 9 | /// 处理不同DPI缩放比下的像素绘制和字体显示问题 10 | /// 使用此类需要添加引用 PresentationFramework 11 | /// 还应在配置清单App.manifest中启用DPI感知自动缩放 12 | /// Font为矢量类型,Point、Size、Rectangle、Padding等为像素类型。 13 | /// 在不同DPI缩放下,矢量类型等比缩放,像素类型保持不变,故会出现排版显示问题。 14 | /// 解决方案一:项目中所有用到的像素类型实例值都取与缩放比之积,矢量类型不变。 15 | /// 解决方案二:项目中所有用到的矢量类型实例都取与缩放比之商,像素类型不变 16 | public static class HighDpi 17 | { 18 | /// DPI缩放比 19 | public static readonly double DpiScale = Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth; 20 | 21 | public static Point DpiZoom(this Point point) => new Point(DpiZoom(point.X), DpiZoom(point.Y)); 22 | 23 | public static PointF DpiZoom(this PointF point) => new PointF(DpiZoom(point.X), DpiZoom(point.Y)); 24 | 25 | public static Size DpiZoom(this Size size) => new Size(DpiZoom(size.Width), DpiZoom(size.Height)); 26 | 27 | public static SizeF DpiZoom(this SizeF size) => new SizeF(DpiZoom(size.Width), DpiZoom(size.Height)); 28 | 29 | public static Rectangle DpiZoom(this Rectangle r) => new Rectangle(DpiZoom(r.Location), DpiZoom(r.Size)); 30 | 31 | public static RectangleF DpiZoom(this RectangleF r) => new RectangleF(DpiZoom(r.Location), DpiZoom(r.Size)); 32 | 33 | public static Padding DpiZoom(this Padding p) => new Padding(DpiZoom(p.Left), DpiZoom(p.Top), DpiZoom(p.Right), DpiZoom(p.Bottom)); 34 | 35 | public static Font DpiZoom(this Font font) => new Font(font.FontFamily, font.Size / DpiZoom(1F), font.Style); 36 | 37 | public static int DpiZoom(this int num) => (int)(num * DpiScale); 38 | 39 | public static float DpiZoom(this float num) => (float)(num * DpiScale); 40 | 41 | public static double DpiZoom(this double num) => num * DpiScale; 42 | } 43 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/ITsiRegExportItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using ContextMenuManager.Methods; 3 | using System; 4 | using System.IO; 5 | using System.Windows.Forms; 6 | 7 | namespace ContextMenuManager.Controls.Interfaces 8 | { 9 | interface ITsiRegExportItem 10 | { 11 | string Text { get; set; } 12 | string RegPath { get; } 13 | ContextMenuStrip ContextMenuStrip { get; set; } 14 | RegExportMenuItem TsiRegExport { get; set; } 15 | } 16 | 17 | sealed class RegExportMenuItem : RToolStripMenuItem 18 | { 19 | public RegExportMenuItem(ITsiRegExportItem item) : base(AppString.Menu.ExportRegistry) 20 | { 21 | item.ContextMenuStrip.Opening += (sender, e) => 22 | { 23 | using (var key = RegistryEx.GetRegistryKey(item.RegPath)) 24 | Visible = key != null; 25 | }; 26 | Click += (sender, e) => 27 | { 28 | using (SaveFileDialog dlg = new SaveFileDialog()) 29 | { 30 | string date = DateTime.Today.ToString("yyyy-MM-dd"); 31 | string time = DateTime.Now.ToString("HH-mm-ss"); 32 | string filePath = $@"{AppConfig.RegBackupDir}\{date}\{item.Text} - {time}.reg"; 33 | string dirPath = Path.GetDirectoryName(filePath); 34 | string fileName = Path.GetFileName(filePath); 35 | Directory.CreateDirectory(dirPath); 36 | dlg.FileName = fileName; 37 | dlg.InitialDirectory = dirPath; 38 | dlg.Filter = $"{AppString.Dialog.RegistryFile}|*.reg"; 39 | if (dlg.ShowDialog() == DialogResult.OK) 40 | { 41 | ExternalProgram.ExportRegistry(item.RegPath, dlg.FileName); 42 | } 43 | if (Directory.GetFileSystemEntries(dirPath).Length == 0) 44 | { 45 | Directory.Delete(dirPath); 46 | } 47 | } 48 | }; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/SwitchDicList.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System; 5 | using System.Drawing; 6 | using System.Windows.Forms; 7 | 8 | namespace ContextMenuManager.Controls 9 | { 10 | class SwitchDicList : MyList // 其他菜单 增强菜单 11 | { 12 | public bool UseUserDic { get; set; } 13 | 14 | public virtual void LoadItems() 15 | { 16 | AddSwitchItem(); 17 | } 18 | 19 | public void AddSwitchItem() 20 | { 21 | SwitchDicItem item = new SwitchDicItem { UseUserDic = UseUserDic }; 22 | item.UseDicChanged += () => 23 | { 24 | UseUserDic = item.UseUserDic; 25 | ClearItems(); 26 | LoadItems(); 27 | }; 28 | AddItem(item); 29 | } 30 | } 31 | 32 | sealed class SwitchDicItem : MyListItem 33 | { 34 | public SwitchDicItem() 35 | { 36 | Text = AppString.Other.SwitchDictionaries; 37 | AddCtr(cmbDic); 38 | cmbDic.AutosizeDropDownWidth(); 39 | cmbDic.Font = new Font(Font.FontFamily, Font.Size + 1F); 40 | cmbDic.Items.AddRange(new[] { AppString.Other.WebDictionaries, AppString.Other.UserDictionaries }); 41 | cmbDic.SelectionChangeCommitted += (sender, e) => 42 | { 43 | Focus(); 44 | UseUserDic = cmbDic.SelectedIndex == 1; 45 | }; 46 | } 47 | 48 | private bool? useUserDic = null; 49 | public bool UseUserDic 50 | { 51 | get => useUserDic == true; 52 | set 53 | { 54 | if (useUserDic == value) return; 55 | bool flag = useUserDic == null; 56 | useUserDic = value; 57 | Image = UseUserDic ? AppImage.User : AppImage.Web; 58 | cmbDic.SelectedIndex = value ? 1 : 0; 59 | if (!flag) UseDicChanged?.Invoke(); 60 | } 61 | } 62 | 63 | public Action UseDicChanged; 64 | 65 | readonly RComboBox cmbDic = new RComboBox 66 | { 67 | DropDownStyle = ComboBoxStyle.DropDownList, 68 | Width = 120.DpiZoom() 69 | }; 70 | } 71 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/IEList.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using Microsoft.Win32; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Windows.Forms; 8 | 9 | namespace ContextMenuManager.Controls 10 | { 11 | sealed class IEList : MyList // 其他规则 IE浏览器 12 | { 13 | public const string IEPath = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer"; 14 | 15 | public void LoadItems() 16 | { 17 | AddNewItem(); 18 | LoadIEItems(); 19 | } 20 | 21 | private void LoadIEItems() 22 | { 23 | List names = new List(); 24 | using (RegistryKey ieKey = RegistryEx.GetRegistryKey(IEPath)) 25 | { 26 | if (ieKey == null) return; 27 | foreach (string part in IEItem.MeParts) 28 | { 29 | using (RegistryKey meKey = ieKey.OpenSubKey(part)) 30 | { 31 | if (meKey == null) continue; 32 | foreach (string keyName in meKey.GetSubKeyNames()) 33 | { 34 | if (names.Contains(keyName, StringComparer.OrdinalIgnoreCase)) continue; 35 | using (RegistryKey key = meKey.OpenSubKey(keyName)) 36 | { 37 | if (!string.IsNullOrEmpty(key.GetValue("")?.ToString())) 38 | { 39 | AddItem(new IEItem(key.Name)); 40 | names.Add(keyName); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | 49 | private void AddNewItem() 50 | { 51 | NewItem newItem = new NewItem(); 52 | AddItem(newItem); 53 | newItem.AddNewItem += () => 54 | { 55 | using (NewIEDialog dlg = new NewIEDialog()) 56 | { 57 | if (dlg.ShowDialog() != DialogResult.OK) return; 58 | InsertItem(new IEItem(dlg.RegPath), 1); 59 | } 60 | }; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report_en.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a bug in ContextMenuManager 3 | title: "[Bug]: " 4 | labels: [ "bug" ] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: "# ContextMenuManager Bug Report" 9 | - type: checkboxes 10 | id: bug_check 11 | attributes: 12 | label: Checklist 13 | description: "Before submitting, please confirm the following:" 14 | options: 15 | - label: "This bug has not already been reported (search [here](https://github.com/Jack251970/ContextMenuManager/issues?q=is%3Aissue))" 16 | required: true 17 | - label: "I have updated ContextMenuManager to the latest version" 18 | required: true 19 | - label: "I understand that any issue not following the guidelines may be closed directly" 20 | required: true 21 | - type: dropdown 22 | id: bug_os 23 | attributes: 24 | label: Windows 25 | description: Your Windows version 26 | options: 27 | - Windows 7 28 | - Windows 8.x 29 | - Windows 10 30 | - Windows 11 31 | default: 3 32 | validations: 33 | required: true 34 | - type: input 35 | id: bug_version 36 | attributes: 37 | label: Version 38 | description: The ContextMenuManager version where you found this bug 39 | placeholder: "ContextMenuManager version: x.x.x.x" 40 | validations: 41 | required: true 42 | - type: textarea 43 | id: bug_reproducer 44 | attributes: 45 | label: Steps to Reproduce 46 | description: Please briefly describe how to trigger this bug 47 | placeholder: | 48 | 1. xxx 49 | 2. xxx 50 | 3. xxx 51 | validations: 52 | required: true 53 | - type: textarea 54 | id: bug_logs 55 | attributes: 56 | label: Crash logs 57 | description: Paste the log that appears when the program crashes into the text box 58 | - type: markdown 59 | attributes: 60 | value: "### Thank you for supporting ContextMenuManager!" 61 | -------------------------------------------------------------------------------- /ContextMenuManager/Controls/NewIEDialog.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using ContextMenuManager.Methods; 3 | using System; 4 | using System.IO; 5 | using System.Windows.Forms; 6 | 7 | namespace ContextMenuManager.Controls 8 | { 9 | sealed class NewIEDialog : CommonDialog 10 | { 11 | public string RegPath { get; private set; } 12 | public override void Reset() { } 13 | 14 | protected override bool RunDialog(IntPtr hwndOwner) 15 | { 16 | using (NewIEForm frm = new NewIEForm()) 17 | { 18 | frm.TopMost = true; 19 | bool flag = frm.ShowDialog() == DialogResult.OK; 20 | if (flag) RegPath = frm.RegPath; 21 | return flag; 22 | } 23 | } 24 | 25 | sealed class NewIEForm : NewItemForm 26 | { 27 | public string RegPath { get; set; } 28 | 29 | protected override void InitializeComponents() 30 | { 31 | base.InitializeComponents(); 32 | btnOK.Click += (sender, e) => 33 | { 34 | if (ItemText.IsNullOrWhiteSpace()) 35 | { 36 | AppMessageBox.Show(AppString.Message.TextCannotBeEmpty); 37 | return; 38 | } 39 | if (ItemCommand.IsNullOrWhiteSpace()) 40 | { 41 | AppMessageBox.Show(AppString.Message.CommandCannotBeEmpty); 42 | return; 43 | } 44 | AddNewItem(); 45 | DialogResult = DialogResult.OK; 46 | }; 47 | 48 | btnBrowse.Click += (sender, e) => 49 | { 50 | using (OpenFileDialog dlg = new OpenFileDialog()) 51 | { 52 | if (dlg.ShowDialog() != DialogResult.OK) return; 53 | ItemFilePath = dlg.FileName; 54 | ItemText = Path.GetFileNameWithoutExtension(dlg.FileName); 55 | } 56 | }; 57 | } 58 | 59 | private void AddNewItem() 60 | { 61 | RegPath = $@"{IEList.IEPath}\{IEItem.MeParts[0]}\{ItemText.Replace("\\", "")}"; 62 | Microsoft.Win32.Registry.SetValue(RegPath, "", ItemCommand); 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /ContextMenuManager.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 18 4 | VisualStudioVersion = 18.0.11018.127 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ContextMenuManager", "ContextMenuManager\ContextMenuManager.csproj", "{EF7E60E9-3565-42BA-AFB3-7FE73A1B011C}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CE0AD55A-5ABF-40A7-931E-FC04B0FFC6FA}" 9 | ProjectSection(SolutionItems) = preProject 10 | .gitattributes = .gitattributes 11 | .gitignore = .gitignore 12 | Screenshot\AppImage.png = Screenshot\AppImage.png 13 | .github\ISSUE_TEMPLATE\bug_report.yml = .github\ISSUE_TEMPLATE\bug_report.yml 14 | .github\ISSUE_TEMPLATE\bug_report_en.yml = .github\ISSUE_TEMPLATE\bug_report_en.yml 15 | .github\workflows\build.yml = .github\workflows\build.yml 16 | .github\dependabot.yml = .github\dependabot.yml 17 | Donate.md = Donate.md 18 | .github\ISSUE_TEMPLATE\feature_request.yml = .github\ISSUE_TEMPLATE\feature_request.yml 19 | .github\ISSUE_TEMPLATE\feature_request_en.yml = .github\ISSUE_TEMPLATE\feature_request_en.yml 20 | LICENSE = LICENSE 21 | Logo\Logo.png = Logo\Logo.png 22 | Logo\Logo2.png = Logo\Logo2.png 23 | .github\workflows\publish.yml = .github\workflows\publish.yml 24 | README-en.md = README-en.md 25 | README.md = README.md 26 | .github\workflows\remove-old-artifacts.yml = .github\workflows\remove-old-artifacts.yml 27 | Screenshot\Screenshot-en.png = Screenshot\Screenshot-en.png 28 | Screenshot\Screenshot.png = Screenshot\Screenshot.png 29 | EndProjectSection 30 | EndProject 31 | Global 32 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 33 | Debug|Any CPU = Debug|Any CPU 34 | Release|Any CPU = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 37 | {EF7E60E9-3565-42BA-AFB3-7FE73A1B011C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {EF7E60E9-3565-42BA-AFB3-7FE73A1B011C}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {EF7E60E9-3565-42BA-AFB3-7FE73A1B011C}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {EF7E60E9-3565-42BA-AFB3-7FE73A1B011C}.Release|Any CPU.Build.0 = Release|Any CPU 41 | EndGlobalSection 42 | GlobalSection(SolutionProperties) = preSolution 43 | HideSolutionNode = FALSE 44 | EndGlobalSection 45 | GlobalSection(ExtensibilityGlobals) = postSolution 46 | SolutionGuid = {A0E97C81-86B8-4360-8251-DF6FD4494EDA} 47 | EndGlobalSection 48 | EndGlobal 49 | -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Controls/ResizeLimitedForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace BluePointLilac.Controls 5 | { 6 | /// 限制水平、竖直方向调整大小的窗体 7 | public class ResizeLimitedForm : RForm 8 | { 9 | /// 水平方向可调整大小 10 | public bool HorizontalResizable { get; set; } = true; 11 | 12 | /// 竖直方向可调整大小 13 | public bool VerticalResizable { get; set; } = true; 14 | 15 | public ResizeLimitedForm() 16 | { 17 | InitTheme(); 18 | } 19 | 20 | protected override void WndProc(ref Message m) 21 | { 22 | base.WndProc(ref m); 23 | if (m.Msg == WM_NCHITTEST && WindowState == FormWindowState.Normal) 24 | { 25 | IntPtr hNowhere = new IntPtr((int)HitTest.Nowhere); 26 | switch ((HitTest)m.Result) 27 | { 28 | case HitTest.Top: 29 | case HitTest.Bottom: 30 | if (!VerticalResizable) m.Result = hNowhere; 31 | break; 32 | case HitTest.Left: 33 | case HitTest.Right: 34 | if (!HorizontalResizable) m.Result = hNowhere; 35 | break; 36 | case HitTest.TopLeft: 37 | case HitTest.TopRight: 38 | case HitTest.BottomLeft: 39 | case HitTest.BottomRight: 40 | if (!VerticalResizable || !HorizontalResizable) m.Result = hNowhere; 41 | break; 42 | } 43 | } 44 | } 45 | 46 | const int WM_NCHITTEST = 0x84;//光标移动或鼠标按下、释放时的消息 47 | /// 鼠标击中位置 48 | enum HitTest : int 49 | { 50 | Error = -2, 51 | Transparent = -1, 52 | Nowhere = 0, 53 | Client = 1, 54 | TitleBar = 2, 55 | SysMenu = 3, 56 | Size = 4, 57 | GrowBox = 5, 58 | Hscroll = 6, 59 | Vscroll = 7, 60 | MinButton = 8, 61 | MaxButton = 9, 62 | Left = 10, 63 | Right = 11, 64 | Top = 12, 65 | TopLeft = 13, 66 | TopRight = 14, 67 | Bottom = 15, 68 | BottomLeft = 16, 69 | BottomRight = 17, 70 | Border = 18, 71 | Close = 20, 72 | Help = 21 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /ContextMenuManager/Methods/UwpHelper.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using Microsoft.Win32; 3 | using System; 4 | using System.IO; 5 | 6 | namespace ContextMenuManager.Methods 7 | { 8 | static class UwpHelper 9 | { 10 | private const string PackageRegPath = @"HKEY_CLASSES_ROOT\PackagedCom\Package"; 11 | private const string PackagesRegPath = @"HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\AppModel\PackageRepository\Packages"; 12 | 13 | public static string GetPackageName(string uwpName) 14 | { 15 | if (string.IsNullOrEmpty(uwpName)) return null; 16 | using (RegistryKey packageKey = RegistryEx.GetRegistryKey(PackageRegPath)) 17 | { 18 | if (packageKey == null) return null; 19 | foreach (string packageName in packageKey.GetSubKeyNames()) 20 | { 21 | if (packageName.StartsWith(uwpName, StringComparison.OrdinalIgnoreCase)) 22 | { 23 | return packageName; 24 | } 25 | } 26 | } 27 | return null; 28 | } 29 | 30 | public static string GetRegPath(string uwpName, Guid guid) 31 | { 32 | string packageName = GetPackageName(uwpName); 33 | if (packageName == null) return null; 34 | else return $@"{PackageRegPath}\{packageName}\Class\{guid:B}"; 35 | } 36 | 37 | public static string GetFilePath(string uwpName, Guid guid) 38 | { 39 | string regPath = GetRegPath(uwpName, guid); 40 | if (regPath == null) return null; 41 | string packageName = GetPackageName(uwpName); 42 | using (RegistryKey pKey = RegistryEx.GetRegistryKey($@"{PackagesRegPath}\{packageName}")) 43 | { 44 | if (pKey == null) return null; 45 | string dirPath = pKey.GetValue("Path")?.ToString(); 46 | string dllPath = Registry.GetValue(regPath, "DllPath", null)?.ToString(); 47 | string filePath = $@"{dirPath}\{dllPath}"; 48 | if (File.Exists(filePath)) return filePath; 49 | string[] names = pKey.GetSubKeyNames(); 50 | if (names.Length == 1) 51 | { 52 | filePath = "shell:AppsFolder\\" + names[0]; 53 | return filePath; 54 | } 55 | if (Directory.Exists(dirPath)) return dirPath; 56 | return null; 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Controls/ReadOnlyTextBox.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using System; 3 | using System.Drawing; 4 | using System.Windows.Forms; 5 | 6 | namespace BluePointLilac.Controls 7 | { 8 | public sealed class ReadOnlyTextBox : TextBox 9 | { 10 | public ReadOnlyTextBox() 11 | { 12 | ReadOnly = true; 13 | Multiline = true; 14 | ShortcutsEnabled = false; 15 | ForeColor = MyMainForm.FormFore; 16 | BackColor = MyMainForm.FormBack; 17 | Font = SystemFonts.MenuFont; 18 | Font = new Font(Font.FontFamily, Font.Size + 1F); 19 | } 20 | 21 | const int WM_SETFOCUS = 0x0007; 22 | const int WM_KILLFOCUS = 0x0008; 23 | protected override void WndProc(ref Message m) 24 | { 25 | switch (m.Msg) 26 | { 27 | case WM_SETFOCUS: 28 | m.Msg = WM_KILLFOCUS; break; 29 | } 30 | base.WndProc(ref m); 31 | } 32 | 33 | private bool firstEnter = true; 34 | 35 | protected override void OnMouseEnter(EventArgs e) 36 | { 37 | base.OnMouseEnter(e); 38 | if (firstEnter) Focus(); 39 | firstEnter = false; 40 | } 41 | } 42 | 43 | public sealed class ReadOnlyRichTextBox : RichTextBox 44 | { 45 | public ReadOnlyRichTextBox() 46 | { 47 | ReadOnly = true; 48 | Dock = DockStyle.Fill; 49 | BorderStyle = BorderStyle.None; 50 | ForeColor = MyMainForm.FormFore; 51 | BackColor = MyMainForm.FormBack; 52 | Font = SystemFonts.MenuFont; 53 | Font = new Font(Font.FontFamily, Font.Size + 1F); 54 | } 55 | 56 | const int WM_SETFOCUS = 0x0007; 57 | const int WM_KILLFOCUS = 0x0008; 58 | 59 | protected override void WndProc(ref Message m) 60 | { 61 | switch (m.Msg) 62 | { 63 | case WM_SETFOCUS: 64 | m.Msg = WM_KILLFOCUS; break; 65 | } 66 | base.WndProc(ref m); 67 | } 68 | 69 | private bool firstEnter = true; 70 | 71 | protected override void OnMouseEnter(EventArgs e) 72 | { 73 | base.OnMouseEnter(e); 74 | if (firstEnter) Focus(); 75 | firstEnter = false; 76 | } 77 | 78 | protected override void OnLinkClicked(LinkClickedEventArgs e) 79 | { 80 | base.OnLinkClicked(e); 81 | ExternalProgram.OpenWebUrl(e.LinkText); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/Interfaces/ITsiDeleteItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System; 5 | using System.IO; 6 | using System.Windows.Forms; 7 | 8 | namespace ContextMenuManager.Controls.Interfaces 9 | { 10 | interface ITsiDeleteItem 11 | { 12 | DeleteMeMenuItem TsiDeleteMe { get; set; } 13 | void DeleteMe(); 14 | } 15 | 16 | interface ITsiRegDeleteItem : ITsiDeleteItem 17 | { 18 | string Text { get; } 19 | string RegPath { get; } 20 | } 21 | 22 | sealed class DeleteMeMenuItem : RToolStripMenuItem 23 | { 24 | public DeleteMeMenuItem(ITsiDeleteItem item) : base(item is RestoreItem ? AppString.Menu.DeleteBackup : AppString.Menu.Delete) 25 | { 26 | Click += (sender, e) => 27 | { 28 | if (item is ITsiRegDeleteItem regItem && AppConfig.AutoBackup) 29 | { 30 | if (AppMessageBox.Show(AppString.Message.DeleteButCanRestore, MessageBoxButtons.YesNo) != DialogResult.Yes) 31 | { 32 | return; 33 | } 34 | string date = DateTime.Today.ToString("yyyy-MM-dd"); 35 | string time = DateTime.Now.ToString("HH-mm-ss"); 36 | string filePath = $@"{AppConfig.RegBackupDir}\{date}\{regItem.Text} - {time}.reg"; 37 | Directory.CreateDirectory(Path.GetDirectoryName(filePath)); 38 | ExternalProgram.ExportRegistry(regItem.RegPath, filePath); 39 | } 40 | else if (AppMessageBox.Show(item is RestoreItem ? AppString.Message.ConfirmDeleteBackupPermanently : AppString.Message.ConfirmDeletePermanently, 41 | MessageBoxButtons.YesNo) != DialogResult.Yes) 42 | { 43 | return; 44 | } 45 | MyListItem listItem = (MyListItem)item; 46 | MyList list = (MyList)listItem.Parent; 47 | int index = list.GetItemIndex(listItem); 48 | try 49 | { 50 | item.DeleteMe(); 51 | } 52 | catch 53 | { 54 | AppMessageBox.Show(AppString.Message.AuthorityProtection); 55 | return; 56 | } 57 | list.Controls.Remove(listItem); 58 | list.Controls[index < list.Controls.Count ? index : (list.Controls.Count - 1)].Focus(); 59 | listItem.Dispose(); 60 | }; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/RestoreItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using ContextMenuManager.Controls.Interfaces; 3 | using ContextMenuManager.Methods; 4 | using System.IO; 5 | using System.Windows.Forms; 6 | 7 | namespace ContextMenuManager.Controls 8 | { 9 | interface ITsiRestoreFile 10 | { 11 | void RestoreItems(string restoreFile); 12 | } 13 | 14 | sealed class RestoreItem : MyListItem, IBtnShowMenuItem, ITsiFilePathItem, ITsiDeleteItem, ITsiRestoreItem 15 | { 16 | public RestoreItem(ITsiRestoreFile item, string filePath, string deviceName, string creatTime) 17 | { 18 | InitializeComponents(); 19 | restoreInterface = item; 20 | FilePath = filePath; 21 | Text = AppString.Other.RestoreItemText.Replace("%device", deviceName).Replace("%time", creatTime); 22 | Image = AppImage.BackupItem; 23 | } 24 | 25 | // 恢复函数接口对象 26 | private readonly ITsiRestoreFile restoreInterface; 27 | 28 | // 备份文件目录 29 | private string filePath; 30 | public string FilePath 31 | { 32 | get => filePath; 33 | set => filePath = value; 34 | } 35 | public string ItemFilePath { get { return filePath; } } 36 | 37 | public MenuButton BtnShowMenu { get; set; } 38 | public FilePropertiesMenuItem TsiFileProperties { get; set; } 39 | public FileLocationMenuItem TsiFileLocation { get; set; } 40 | public DeleteMeMenuItem TsiDeleteMe { get; set; } 41 | public RestoreMeMenuItem TsiRestoreMe { get; set; } 42 | 43 | readonly RToolStripMenuItem TsiDetails = new RToolStripMenuItem(AppString.Menu.Details); 44 | 45 | private void InitializeComponents() 46 | { 47 | BtnShowMenu = new MenuButton(this); 48 | TsiFileLocation = new FileLocationMenuItem(this); 49 | TsiFileProperties = new FilePropertiesMenuItem(this); 50 | TsiDeleteMe = new DeleteMeMenuItem(this); 51 | TsiRestoreMe = new RestoreMeMenuItem(this); 52 | 53 | // 设置菜单:详细信息;删除备份;恢复备份 54 | ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiDetails, new RToolStripSeparator(), 55 | TsiRestoreMe, new RToolStripSeparator(), TsiDeleteMe }); 56 | 57 | // 详细信息 58 | TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiFileProperties, TsiFileLocation }); 59 | } 60 | 61 | public void DeleteMe() 62 | { 63 | File.Delete(filePath); 64 | } 65 | 66 | public void RestoreMe() 67 | { 68 | restoreInterface.RestoreItems(filePath); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/TextBoxExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Windows.Forms; 3 | 4 | namespace BluePointLilac.Methods 5 | { 6 | public static class TextBoxExtension 7 | { 8 | /// TextBox仿RichTextBox按住Ctrl加鼠标滚轮放缩字体 9 | public static void CanResizeFont(this TextBox box) 10 | { 11 | box.MouseWheel += (sender, e) => 12 | { 13 | if (Control.ModifierKeys != Keys.Control) return; 14 | float size = box.Font.Size; 15 | if (size < 8F && e.Delta < 0) return; 16 | if (size > 40F && e.Delta > 0) return; 17 | box.Font = new Font(box.Font.FontFamily, size + (e.Delta > 0 ? 1F : -1F)); 18 | }; 19 | } 20 | 21 | /// TextBox在文字未超出边界时隐藏滚动条,超出时显示 22 | public static void SetAutoShowScroll(this TextBox box, ScrollBars scrollBars) 23 | { 24 | 25 | void SetScrollVisible() 26 | { 27 | Size szBox = box.ClientSize; 28 | Size szText = TextRenderer.MeasureText(box.Text, box.Font); 29 | if ((scrollBars | ScrollBars.Vertical) == ScrollBars.Vertical) 30 | { 31 | if (szText.Height > szBox.Height) 32 | { 33 | box.ScrollBars = scrollBars | ScrollBars.Vertical; 34 | } 35 | else 36 | { 37 | box.ScrollBars = scrollBars & ~ScrollBars.Vertical; 38 | } 39 | } 40 | if ((scrollBars | ScrollBars.Horizontal) == ScrollBars.Horizontal) 41 | { 42 | if (szText.Width > szBox.Width) 43 | { 44 | box.ScrollBars = scrollBars | ScrollBars.Horizontal; 45 | } 46 | else 47 | { 48 | box.ScrollBars = scrollBars & ~ScrollBars.Horizontal; 49 | } 50 | } 51 | } 52 | ; 53 | box.TextChanged += (sender, e) => SetScrollVisible(); 54 | box.FontChanged += (sender, e) => SetScrollVisible(); 55 | box.ClientSizeChanged += (sender, e) => SetScrollVisible(); 56 | } 57 | 58 | /// TextBox只读时可以使用Ctrl+A全选快捷键 59 | public static void CanSelectAllWhenReadOnly(this TextBox box) 60 | { 61 | box.KeyDown += (sender, e) => 62 | { 63 | if (box.ReadOnly && e.Control && e.KeyCode == Keys.A) box.SelectAll(); 64 | }; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Controls/UAWebClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Runtime.Serialization.Json; 5 | using System.Text; 6 | using System.Xml; 7 | 8 | namespace BluePointLilac.Controls 9 | { 10 | public sealed class UAWebClient : WebClient 11 | { 12 | static UAWebClient() 13 | { 14 | // 确保能在Win10上工作 15 | ServicePointManager.Expect100Continue = true; 16 | //此类主要为了解决访问Github的一些问题 17 | //请求被中止: 未能创建 SSL/TLS 安全通道; 基础连接已经关闭: 发送时发生错误,一般添加TLS12即可 18 | //TLS12------0xc00,TLS11------0x300,TLS------0xc0,SSL------0x30; 19 | ServicePointManager.SecurityProtocol |= /*SecurityProtocolType.Ssl3 | */SecurityProtocolType.Tls 20 | | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; 21 | } 22 | 23 | public UAWebClient() 24 | { 25 | //网络传输默认文本编码 UTF-8 26 | Encoding = Encoding.UTF8; 27 | //远程服务器返回错误: (403) 已禁止 28 | //浏览器 F12 console 输入 console.log(navigator.userAgent); 获取 User Agent 29 | Headers.Add("User-Agent", 30 | "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " + 31 | "Chrome/90.0.4430.212 Safari/537.36 Edg/90.0.818.66"); 32 | } 33 | 34 | protected override WebRequest GetWebRequest(Uri address) 35 | { 36 | WebRequest request = base.GetWebRequest(address); 37 | request.Timeout = 6 * 1000; 38 | return request; 39 | } 40 | 41 | /// 获取网页文本 42 | public string GetWebString(string url) 43 | { 44 | try 45 | { 46 | string str = DownloadString(url); 47 | str = str?.Replace("\n", Environment.NewLine);//换行符转换 48 | return str; 49 | } 50 | catch { return null; } 51 | } 52 | 53 | /// 将网络文本写入本地文件 54 | /// 本地文件路径 55 | /// 网络文件Raw路径 56 | public bool WebStringToFile(string filePath, string fileUrl) 57 | { 58 | string contents = GetWebString(fileUrl); 59 | bool flag = contents != null; 60 | if (flag) File.WriteAllText(filePath, contents, Encoding.Unicode); 61 | return flag; 62 | } 63 | 64 | /// 获取网页Json文本并加工为Xml 65 | public XmlDocument GetWebJsonToXml(string url) 66 | { 67 | try 68 | { 69 | byte[] bytes = DownloadData(url); 70 | using (XmlReader xReader = JsonReaderWriterFactory.CreateJsonReader(bytes, XmlDictionaryReaderQuotas.Max)) 71 | { 72 | XmlDocument doc = new XmlDocument(); 73 | doc.Load(xReader); 74 | return doc; 75 | } 76 | } 77 | catch { return null; } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/GuidBlockedList.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using Microsoft.Win32; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Windows.Forms; 9 | 10 | namespace ContextMenuManager.Controls 11 | { 12 | sealed class GuidBlockedList : MyList // 其他规则 GUID锁 13 | { 14 | public const string HKLMBLOCKED = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked"; 15 | public const string HKCUBLOCKED = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked"; 16 | public static readonly string[] BlockedPaths = { HKLMBLOCKED, HKCUBLOCKED }; 17 | 18 | public void LoadItems() 19 | { 20 | AddNewItem(); 21 | LoadBlockedItems(); 22 | } 23 | 24 | private void LoadBlockedItems() 25 | { 26 | List values = new List(); 27 | foreach (string path in BlockedPaths) 28 | { 29 | using (RegistryKey key = RegistryEx.GetRegistryKey(path)) 30 | { 31 | if (key == null) continue; 32 | foreach (string value in key.GetValueNames()) 33 | { 34 | if (values.Contains(value, StringComparer.OrdinalIgnoreCase)) continue; 35 | AddItem(new GuidBlockedItem(value)); 36 | values.Add(value); 37 | } 38 | } 39 | } 40 | } 41 | 42 | private void AddNewItem() 43 | { 44 | NewItem newItem = new NewItem(AppString.Other.AddGuidBlockedItem); 45 | AddItem(newItem); 46 | newItem.AddNewItem += () => 47 | { 48 | using (InputDialog dlg = new InputDialog { Title = AppString.Dialog.InputGuid }) 49 | { 50 | if (GuidEx.TryParse(Clipboard.GetText(), out Guid guid)) dlg.Text = guid.ToString(); 51 | if (dlg.ShowDialog() != DialogResult.OK) return; 52 | if (GuidEx.TryParse(dlg.Text, out guid)) 53 | { 54 | string value = guid.ToString("B"); 55 | Array.ForEach(BlockedPaths, path => Registry.SetValue(path, value, "")); 56 | for (int i = 1; i < Controls.Count; i++) 57 | { 58 | if (((GuidBlockedItem)Controls[i]).Guid.Equals(guid)) 59 | { 60 | AppMessageBox.Show(AppString.Message.HasBeenAdded); 61 | return; 62 | } 63 | } 64 | InsertItem(new GuidBlockedItem(value), 1); 65 | ExplorerRestarter.Show(); 66 | } 67 | else AppMessageBox.Show(AppString.Message.MalformedGuid); 68 | } 69 | }; 70 | } 71 | } 72 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/OpenWithList.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using Microsoft.Win32; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Windows.Forms; 9 | 10 | namespace ContextMenuManager.Controls 11 | { 12 | sealed class OpenWithList : MyList // 主页 打开方式 13 | { 14 | public void LoadItems() 15 | { 16 | LoadOpenWithItems(); 17 | SortItemByText(); 18 | AddNewItem(); 19 | //Win8及以上版本系统才有在应用商店中查找应用 20 | if (WinOsVersion.Current >= WinOsVersion.Win8) 21 | { 22 | VisibleRegRuleItem storeItem = new VisibleRegRuleItem(VisibleRegRuleItem.UseStoreOpenWith); 23 | InsertItem(storeItem, 1); 24 | } 25 | } 26 | 27 | private void LoadOpenWithItems() 28 | { 29 | using (RegistryKey root = Registry.ClassesRoot) 30 | using (RegistryKey appKey = root.OpenSubKey("Applications")) 31 | { 32 | foreach (string appName in appKey.GetSubKeyNames()) 33 | { 34 | if (!appName.Contains('.')) continue;//需要为有扩展名的文件名 35 | using (RegistryKey shellKey = appKey.OpenSubKey($@"{appName}\shell")) 36 | { 37 | if (shellKey == null) continue; 38 | 39 | List names = shellKey.GetSubKeyNames().ToList(); 40 | if (names.Contains("open", StringComparer.OrdinalIgnoreCase)) names.Insert(0, "open"); 41 | 42 | string keyName = names.Find(name => 43 | { 44 | using (RegistryKey cmdKey = shellKey.OpenSubKey(name)) 45 | return cmdKey.GetValue("NeverDefault") == null; 46 | }); 47 | if (keyName == null) continue; 48 | 49 | using (RegistryKey commandKey = shellKey.OpenSubKey($@"{keyName}\command")) 50 | { 51 | string command = commandKey?.GetValue("")?.ToString(); 52 | if (ObjectPath.ExtractFilePath(command) != null) 53 | { 54 | OpenWithItem item = new OpenWithItem(commandKey.Name); 55 | AddItem(item); 56 | } 57 | } 58 | } 59 | } 60 | } 61 | } 62 | 63 | private void AddNewItem() 64 | { 65 | NewItem newItem = new NewItem(); 66 | InsertItem(newItem, 0); 67 | newItem.AddNewItem += () => 68 | { 69 | using (NewOpenWithDialog dlg = new NewOpenWithDialog()) 70 | { 71 | if (dlg.ShowDialog() == DialogResult.OK) 72 | InsertItem(new OpenWithItem(dlg.RegPath), 2); 73 | } 74 | }; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/ImageExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using System.Drawing.Drawing2D; 3 | using System.Drawing.Imaging; 4 | 5 | namespace BluePointLilac.Methods 6 | { 7 | public static class ImageExtension 8 | { 9 | public static Image ToTransparent(this Image image, float opacity = 0.5F) 10 | { 11 | Bitmap bitmap = new Bitmap(image.Width, image.Height); 12 | using (Graphics g = Graphics.FromImage(bitmap)) 13 | using (ImageAttributes attributes = new ImageAttributes()) 14 | { 15 | ColorMatrix matrix = new ColorMatrix { Matrix33 = opacity }; 16 | attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); 17 | g.DrawImage(image, new Rectangle(0, 0, bitmap.Width, bitmap.Height), 18 | 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); 19 | } 20 | return bitmap; 21 | } 22 | 23 | public static Image ResizeImage(this Image image, int width, int height) 24 | { 25 | //return image.GetThumbnailImage(width, height, null, System.IntPtr.Zero);//质量稍微低一点 26 | if (image.Width == width && image.Height == height) return image; 27 | Bitmap destImage = new Bitmap(width, height); 28 | destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution); 29 | using (Graphics g = Graphics.FromImage(destImage)) 30 | { 31 | g.CompositingMode = CompositingMode.SourceCopy; 32 | g.InterpolationMode = InterpolationMode.HighQualityBicubic; 33 | g.CompositingQuality = CompositingQuality.HighQuality; 34 | g.PixelOffsetMode = PixelOffsetMode.HighQuality; 35 | g.SmoothingMode = SmoothingMode.HighQuality; 36 | 37 | using (ImageAttributes attributes = new ImageAttributes()) 38 | { 39 | attributes.SetWrapMode(WrapMode.TileFlipXY); 40 | g.DrawImage(image, new Rectangle(0, 0, width, height), 41 | 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes); 42 | } 43 | } 44 | return destImage; 45 | } 46 | 47 | public static Image ResizeImage(this Image image, double scale) 48 | { 49 | if (scale == 1) return image; 50 | int width = (int)(image.Width * scale); 51 | int height = (int)(image.Height * scale); 52 | return image.ResizeImage(width, height); 53 | } 54 | 55 | public static Image ResizeImage(this Image image, Size newSize) 56 | { 57 | if (newSize == image.Size) return image; 58 | return image.ResizeImage(newSize.Width, newSize.Height); 59 | } 60 | 61 | public static Image RotateImage(this Image image, RotateFlipType rotateType) 62 | { 63 | Bitmap bitmap = new Bitmap(image); 64 | bitmap.RotateFlip(rotateType); 65 | return bitmap; 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/FileExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace BluePointLilac.Methods 7 | { 8 | public static class FileExtension 9 | { 10 | [Flags] 11 | enum AssocF 12 | { 13 | Init_NoRemapCLSID = 0x1, 14 | Init_ByExeName = 0x2, 15 | Open_ByExeName = 0x2, 16 | Init_DefaultToStar = 0x4, 17 | Init_DefaultToFolder = 0x8, 18 | NoUserSettings = 0x10, 19 | NoTruncate = 0x20, 20 | Verify = 0x40, 21 | RemapRunDll = 0x80, 22 | NoFixUps = 0x100, 23 | IgnoreBaseClass = 0x200 24 | } 25 | 26 | public enum AssocStr 27 | { 28 | Command = 1, 29 | Executable, 30 | FriendlyDocName, 31 | FriendlyAppName, 32 | NoOpen, 33 | ShellNewValue, 34 | DDECommand, 35 | DDEIfExec, 36 | DDEApplication, 37 | DDETopic 38 | } 39 | 40 | [DllImport("shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)] 41 | private static extern uint AssocQueryString(AssocF flags, AssocStr str, string pszAssoc, string pszExtra, [Out] StringBuilder pszOut, ref uint pcchOut); 42 | 43 | public const string FILEEXTSPATH = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts"; 44 | private const string HKCRCLASSES = @"HKEY_CURRENT_USER\SOFTWARE\Classes"; 45 | private const string HKLMCLASSES = @"HKEY_LOCAL_MACHINE\SOFTWARE\Classes"; 46 | 47 | public static string GetExtentionInfo(AssocStr assocStr, string extension) 48 | { 49 | uint pcchOut = 0; 50 | AssocQueryString(AssocF.Verify, assocStr, extension, null, null, ref pcchOut); 51 | StringBuilder pszOut = new StringBuilder((int)pcchOut); 52 | AssocQueryString(AssocF.Verify, assocStr, extension, null, pszOut, ref pcchOut); 53 | return pszOut.ToString(); 54 | } 55 | 56 | public static string GetOpenMode(string extension) 57 | { 58 | if (string.IsNullOrEmpty(extension)) return null; 59 | string mode; 60 | bool CheckMode() 61 | { 62 | if (mode.IsNullOrWhiteSpace()) return false; 63 | if (mode.Length > 255) return false; 64 | if (mode.ToLower().StartsWith(@"applications\")) return false; 65 | using (RegistryKey root = Registry.ClassesRoot) 66 | using (RegistryKey key = root.OpenSubKey(mode)) 67 | { 68 | return key != null; 69 | } 70 | } 71 | mode = Registry.GetValue($@"{FILEEXTSPATH}\{extension}\UserChoice", "ProgId", null)?.ToString(); 72 | if (CheckMode()) return mode; 73 | mode = Registry.GetValue($@"{HKLMCLASSES}\{extension}", "", null)?.ToString(); 74 | if (CheckMode()) return mode; 75 | mode = Registry.GetValue($@"{HKCRCLASSES}\{extension}", "", null)?.ToString(); 76 | if (CheckMode()) return mode; 77 | return null; 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/RForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Runtime.InteropServices; 4 | using System.Windows.Forms; 5 | 6 | namespace BluePointLilac.Controls 7 | { 8 | /// 9 | /// Edited from: https://github.com/seerge/g-helper 10 | /// 11 | public class RForm : Form 12 | { 13 | protected override void OnLoad(EventArgs e) 14 | { 15 | StartPosition = FormStartPosition.CenterScreen; 16 | base.OnLoad(e); 17 | } 18 | 19 | public static Color ButtonMain => MyMainForm.ButtonMain; 20 | public static Color ButtonSecond => MyMainForm.ButtonSecond; 21 | 22 | public static Color FormBack => MyMainForm.FormBack; 23 | public static Color FormFore => MyMainForm.FormFore; 24 | public static Color FormBorder => MyMainForm.FormBorder; 25 | 26 | [DllImport("UXTheme.dll", SetLastError = true, EntryPoint = "#138")] 27 | public static extern bool CheckSystemDarkModeStatus(); 28 | 29 | [DllImport("DwmApi")] //System.Runtime.InteropServices 30 | private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, int[] attrValue, int attrSize); 31 | 32 | public bool darkTheme = false; 33 | protected override CreateParams CreateParams 34 | { 35 | get 36 | { 37 | var parms = base.CreateParams; 38 | parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN 39 | parms.ClassStyle &= ~0x00020000; 40 | return parms; 41 | } 42 | } 43 | 44 | public bool InitTheme() 45 | { 46 | bool newDarkTheme = MyMainForm.IsDarkTheme(); 47 | bool changed = darkTheme != newDarkTheme; 48 | darkTheme = newDarkTheme; 49 | 50 | if (changed) 51 | { 52 | DwmSetWindowAttribute(Handle, 20, new[] { darkTheme ? 1 : 0 }, 4); 53 | Adjust(); 54 | Invalidate(); 55 | } 56 | 57 | return changed; 58 | } 59 | 60 | protected void ApplyDarkModeToDataGridView(DataGridView dgv) 61 | { 62 | // Background color 63 | dgv.BackgroundColor = MyMainForm.FormBack; 64 | dgv.DefaultCellStyle.BackColor = MyMainForm.FormBack; 65 | dgv.DefaultCellStyle.ForeColor = MyMainForm.FormFore; 66 | 67 | // Grid color 68 | dgv.GridColor = Color.DimGray; 69 | 70 | // Header style 71 | dgv.ColumnHeadersDefaultCellStyle.BackColor = MyMainForm.FormBack; 72 | dgv.ColumnHeadersDefaultCellStyle.ForeColor = MyMainForm.FormFore; 73 | dgv.ColumnHeadersDefaultCellStyle.SelectionBackColor = MyMainForm.MainColor; 74 | dgv.ColumnHeadersDefaultCellStyle.SelectionForeColor = MyMainForm.FormFore; 75 | dgv.EnableHeadersVisualStyles = false; // Ensure custom header styles apply 76 | 77 | // Row styles 78 | dgv.RowsDefaultCellStyle.BackColor = MyMainForm.FormBack; 79 | dgv.RowsDefaultCellStyle.ForeColor = MyMainForm.FormFore; 80 | dgv.AlternatingRowsDefaultCellStyle.BackColor = MyMainForm.FormBack; 81 | 82 | // Selection color 83 | dgv.DefaultCellStyle.SelectionBackColor = MyMainForm.MainColor; 84 | dgv.DefaultCellStyle.SelectionForeColor = MyMainForm.FormFore; 85 | } 86 | 87 | private void Adjust() 88 | { 89 | BackColor = FormBack; 90 | ForeColor = FormFore; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Controls/InputDialog.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using System; 3 | using System.Drawing; 4 | using System.Windows.Forms; 5 | 6 | namespace BluePointLilac.Controls 7 | { 8 | public sealed class InputDialog : CommonDialog 9 | { 10 | /// 输入对话框标题 11 | public string Title { get; set; } = Application.ProductName; 12 | /// 输入对话框文本框文本 13 | public string Text { get; set; } 14 | public Size Size { get; set; } 15 | 16 | public override void Reset() { } 17 | 18 | protected override bool RunDialog(IntPtr hwndOwner) 19 | { 20 | using (InputBox frm = new InputBox()) 21 | { 22 | frm.Text = Title; 23 | frm.InputedText = Text; 24 | frm.Size = Size; 25 | if (Control.FromHandle(hwndOwner) is Form owner) frm.TopMost = true; 26 | bool flag = frm.ShowDialog() == DialogResult.OK; 27 | Text = flag ? frm.InputedText : null; 28 | return flag; 29 | } 30 | } 31 | 32 | sealed class InputBox : RForm 33 | { 34 | public InputBox() 35 | { 36 | AcceptButton = btnOK; 37 | CancelButton = btnCancel; 38 | Font = SystemFonts.MessageBoxFont; 39 | SizeGripStyle = SizeGripStyle.Hide; 40 | StartPosition = FormStartPosition.CenterParent; 41 | MaximizeBox = MinimizeBox = ShowIcon = ShowInTaskbar = false; 42 | Controls.AddRange(new Control[] { txtInput, btnOK, btnCancel }); 43 | txtInput.Font = new Font(txtInput.Font.FontFamily, txtInput.Font.Size + 2F); 44 | txtInput.CanResizeFont(); 45 | InitializeComponents(); 46 | InitTheme(); 47 | } 48 | 49 | public string InputedText 50 | { 51 | get => txtInput.Text; 52 | set => txtInput.Text = value; 53 | } 54 | 55 | readonly TextBox txtInput = new TextBox 56 | { 57 | Font = SystemFonts.MenuFont, 58 | ScrollBars = ScrollBars.Vertical, 59 | Multiline = true 60 | }; 61 | readonly Button btnOK = new Button 62 | { 63 | Anchor = AnchorStyles.Bottom | AnchorStyles.Right, 64 | DialogResult = DialogResult.OK, 65 | Text = ResourceString.OK, 66 | AutoSize = true 67 | }; 68 | readonly Button btnCancel = new Button 69 | { 70 | Anchor = AnchorStyles.Bottom | AnchorStyles.Right, 71 | DialogResult = DialogResult.Cancel, 72 | Text = ResourceString.Cancel, 73 | AutoSize = true 74 | }; 75 | 76 | private void InitializeComponents() 77 | { 78 | SuspendLayout(); 79 | int a = 20.DpiZoom(); 80 | txtInput.Location = new Point(a, a); 81 | txtInput.Size = new Size(340, 24).DpiZoom(); 82 | ClientSize = new Size(txtInput.Width + a * 2, txtInput.Height + btnOK.Height + a * 3); 83 | btnCancel.Top = btnOK.Top = txtInput.Bottom + a; 84 | btnCancel.Left = txtInput.Right - btnCancel.Width; 85 | btnOK.Left = btnCancel.Left - btnOK.Width - a; 86 | ResumeLayout(); 87 | MinimumSize = Size; 88 | Resize += (sender, e) => 89 | { 90 | txtInput.Width = ClientSize.Width - 2 * a; 91 | txtInput.Height = btnCancel.Top - 2 * a; 92 | }; 93 | } 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/SingleInstance.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Windows.Forms; 8 | 9 | namespace BluePointLilac.Methods 10 | { 11 | public static class SingleInstance 12 | { 13 | [DllImport("user32.dll")] 14 | private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow); 15 | 16 | [DllImport("user32.dll")] 17 | private static extern bool SetForegroundWindow(IntPtr hWnd); 18 | 19 | /// 判断单实例程序是否正在运行 20 | /// 若正在运行激活窗口 21 | public static bool IsRunning() 22 | { 23 | using (Process current = Process.GetCurrentProcess()) 24 | { 25 | foreach (Process process in Process.GetProcessesByName(current.ProcessName)) 26 | { 27 | using (process) 28 | { 29 | if (process.Id == current.Id) continue; 30 | if (process.MainModule.FileName == current.MainModule.FileName) 31 | { 32 | const int SW_RESTORE = 9; 33 | ShowWindowAsync(process.MainWindowHandle, SW_RESTORE); 34 | SetForegroundWindow(process.MainWindowHandle); 35 | return true; 36 | } 37 | } 38 | } 39 | return false; 40 | } 41 | } 42 | 43 | /// 重启单实例程序 44 | /// 重启程序时传入参数 45 | /// 用于更新程序的新版本文件路径,为null则为普通重启 46 | public static void Restart(string[] args = null, string updatePath = null) 47 | { 48 | string appPath = Application.ExecutablePath; 49 | string command = appPath; 50 | if (args != null && args.Length > 0) command += "," + string.Join(" ", args); 51 | List contents = new List(); 52 | //vbs命令逐行执行不等待,故加些代码确定上一条命令执行是否完成 53 | contents.AddRange(new[] 54 | { 55 | "On Error Resume Next", 56 | "WScript.Sleep 1000",//等待程序结束 57 | "Dim wsh, fso", 58 | "Set wsh = CreateObject(\"WScript.Shell\")", 59 | "Set fso = CreateObject(\"Scripting.FileSystemObject\")", 60 | }); 61 | 62 | if (File.Exists(updatePath)) 63 | { 64 | contents.AddRange(new[] 65 | { 66 | $"fso.DeleteFile \"{appPath}\"", 67 | $"Do While fso.FileExists(\"{appPath}\")", 68 | "WScript.Sleep 100", 69 | "Loop",//确定文件删除完成 70 | $"fso.MoveFile \"{updatePath}\",\"{appPath}\"",//更新文件 71 | $"Do While fso.FileExists(\"{updatePath}\")",//确定文件已被移动 72 | "WScript.Sleep 100", 73 | $"Loop", 74 | }); 75 | } 76 | contents.AddRange(new[] 77 | { 78 | $"wsh.Run \"{command}\"", 79 | "fso.DeleteFile(WScript.ScriptFullName)",//vbs自删命令 80 | "Set wsh = Nothing", 81 | "Set fso = Nothing", 82 | }); 83 | 84 | string vbsPath = Path.GetTempPath() + Guid.NewGuid() + ".vbs"; 85 | File.WriteAllLines(vbsPath, contents.ToArray(), Encoding.Unicode); 86 | using (Process process = new Process()) 87 | { 88 | process.StartInfo.FileName = "wscript.exe"; 89 | process.StartInfo.Arguments = vbsPath; 90 | process.Start(); 91 | } 92 | Application.Exit(); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **简体中文** | **[English](README-en.md)** 2 | 3 |
4 | 5 |
6 |

ContextMenuManager

7 |

一个纯粹的Windows右键菜单管理器,支持 Windows 7 ~ 11

8 | 9 |
10 | 11 | ![GitHub Release](https://img.shields.io/github/v/release/Jack251970/ContextMenuManager?label=版本) 12 | ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/Jack251970/ContextMenuManager/total?label=下载量) 13 | [![Stars](https://img.shields.io/github/stars/Jack251970/ContextMenuManager?style=flat&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZD0iTTggLjI1YS43NS43NSAwIDAgMSAuNjczLjQxOGwxLjg4MiAzLjgxNSA0LjIxLjYxMmEuNzUuNzUgMCAwIDEgLjQxNiAxLjI3OWwtMy4wNDYgMi45Ny43MTkgNC4xOTJhLjc1MS43NTEgMCAwIDEtMS4wODguNzkxTDggMTIuMzQ3bC0zLjc2NiAxLjk4YS43NS43NSAwIDAgMS0xLjA4OC0uNzlsLjcyLTQuMTk0TC44MTggNi4zNzRhLjc1Ljc1IDAgMCAxIC40MTYtMS4yOGw0LjIxLS42MTFMNy4zMjcuNjY4QS43NS43NSAwIDAgMSA4IC4yNVoiIGZpbGw9IiNlYWM1NGYiLz48L3N2Zz4=&logoSize=auto&label=收藏)](https://github.com/Jack251970/ContextMenuManager/) 14 | ![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/Jack251970/ContextMenuManager?label="问题") 15 | 16 |
17 | 18 | --- 19 | 20 | ## 💕 特别感谢 21 | * 本项目基于 [https://github.com/BluePointLilac/ContextMenuManager](https://github.com/BluePointLilac/ContextMenuManager) 进行开发,感谢作者 [蓝点lilac](https://github.com/BluePointLilac)! 22 | * 感谢由 [澜芸](https://github.com/LanYun2022) 制作的的新图标! 23 | 24 | ## 🚀 程序下载 25 | * [最新版本][Latest]
26 | [GitHub Releases][GitHub Releases]
27 | [Gitee Releases][Gitee Releases]
28 | * 下载说明:在Assets文件列表里面下载zip(建议)或者直接下载exe 29 | * .NET版本说明:程序基于.NET9,适用于Win11、10、8.1、8、7
30 | 如果您的设备不包含.NET9,则需要安装对应版本的[.NET SDK] 31 | 32 | ## ⭐ 主要功能 33 | * 启用或禁用文件、文件夹、新建、发送到、打开方式、自定义文件格式、IE浏览器、WinX等右键菜单项目并对右键菜单进行备份和恢复 34 | * 对上述场景右键菜单项目进行修改名称、修改图标、导航注册表位置、导航文件位置、永久删除等操作 35 | * 对上述场景右键菜单自定义添加项目,自定义菜单命令 36 | 37 | ## 🖥️ 兼容性 38 | * 适用于 Windows 7、8、8.1、10、11 39 | * 适用于 64位、32位 的系统 40 | * 适用于高分屏,最佳显示缩放比为150% 41 | * 程序支持国际化多语言显示,欢迎为此程序制作语言字典 42 | 43 | ## 🖼️ 运行截图 44 | 45 | 46 | 47 | 运行截图 48 | 49 | 50 | ## 🔣 资源引用 51 | 52 | 53 | 54 | 资源引用 55 | 56 | 57 | ## 🪧 温馨提示 58 | * 程序需要对大量的注册表项和文件进行读写删改操作,这些行为比较敏感,可能会被 Windows Defender 等误报为病毒,如发生此情况请自行添加进白名单。 59 | 60 | * 一些特殊菜单项(Shell 扩展、加密文件等)可能会受到其他因素影响导致不会直接显示在右键菜单中,但是按照程序使用的通用规则在此程序中仍会显示为启用,这是正常的现象。 61 | 62 | * 每个右键管理程序禁用菜单方法可能不同,建议不要同时使用多个右键菜单管理程序。 63 | 64 | * 大部分程序使用简单暴力的备份删除法,此程序尽可能使用了系统提供的键值进行隐藏以便于之后的恢复。 65 | 66 | * 通过其他程序禁用的菜单项目。请先使用对应程序还原,不然可能无法在此程序中看到它。 67 | 68 | * 此程序不用于清理未卸载干净的程序,但是可以帮助你快速定位菜单项相关注册表位置和文件位置,你可以根据相关内容进行你的操作。如果你是一个电脑小白,建议只使用启用/禁用功能。 69 | 70 | ## 🏆 贡献者 71 | 感谢我们的贡献者们! 72 | 73 | [![ContextMenuManager Contributors](https://contrib.rocks/image?repo=Jack251970/ContextMenuManager)](https://github.com/Jack251970/ContextMenuManager/graphs/contributors) 74 | 75 | 由 [contrib.rocks](https://contrib.rocks) 生成。 76 | 77 | [Latest]: https://github.com/Jack251970/ContextMenuManager/releases/latest 78 | [GitHub Releases]: https://github.com/Jack251970/ContextMenuManager/releases 79 | [Gitee Releases]: https://gitee.com/Jack251970/ContextMenuManager/releases 80 | [.NET SDK]: https://dotnet.microsoft.com/zh-cn/download/dotnet/9.0 81 | 82 | ## ❤️ 感谢 83 | 84 | 要是你觉得这插件好用,就请我喝杯咖啡支持一下吧! 85 | 86 | [](https://ko-fi.com/jackye) 87 | -------------------------------------------------------------------------------- /ContextMenuManager/Controls/NewOpenWithDialog.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using ContextMenuManager.Methods; 3 | using System; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Windows.Forms; 7 | 8 | namespace ContextMenuManager.Controls 9 | { 10 | sealed class NewOpenWithDialog : CommonDialog 11 | { 12 | public string RegPath { get; private set; } 13 | public override void Reset() { } 14 | 15 | protected override bool RunDialog(IntPtr hwndOwner) 16 | { 17 | using (NewOpenWithForm frm = new NewOpenWithForm()) 18 | { 19 | frm.TopMost = true; 20 | bool flag = frm.ShowDialog() == DialogResult.OK; 21 | if (flag) RegPath = frm.RegPath; 22 | return flag; 23 | } 24 | } 25 | 26 | sealed class NewOpenWithForm : NewItemForm 27 | { 28 | public string RegPath { get; private set; } 29 | 30 | private string FilePath; 31 | private string FileName => Path.GetFileName(FilePath); 32 | private string AppRegPath => $@"{RegistryEx.CLASSES_ROOT}\Applications\{FileName}"; 33 | private string CommandPath => $@"{AppRegPath}\shell\open\command"; 34 | 35 | protected override void InitializeComponents() 36 | { 37 | base.InitializeComponents(); 38 | btnBrowse.Click += (sender, e) => BrowseFile(); 39 | btnOK.Click += (sender, e) => 40 | { 41 | if (string.IsNullOrEmpty(ItemText)) 42 | { 43 | AppMessageBox.Show(AppString.Message.TextCannotBeEmpty); 44 | return; 45 | } 46 | if (ItemCommand.IsNullOrWhiteSpace()) 47 | { 48 | AppMessageBox.Show(AppString.Message.CommandCannotBeEmpty); 49 | return; 50 | } 51 | FilePath = ObjectPath.ExtractFilePath(base.ItemFilePath); 52 | using (var key = RegistryEx.GetRegistryKey(CommandPath)) 53 | { 54 | string path = ObjectPath.ExtractFilePath(key?.GetValue("")?.ToString()); 55 | string name = Path.GetFileName(path); 56 | if (FilePath != null && FilePath.Equals(path, StringComparison.OrdinalIgnoreCase)) 57 | { 58 | AppMessageBox.Show(AppString.Message.HasBeenAdded); 59 | return; 60 | } 61 | if (FileName == null || FileName.Equals(name, StringComparison.OrdinalIgnoreCase)) 62 | { 63 | AppMessageBox.Show(AppString.Message.UnsupportedFilename); 64 | return; 65 | } 66 | } 67 | AddNewItem(); 68 | DialogResult = DialogResult.OK; 69 | }; 70 | } 71 | 72 | private void BrowseFile() 73 | { 74 | using (OpenFileDialog dlg = new OpenFileDialog()) 75 | { 76 | dlg.Filter = $"{AppString.Dialog.Program}|*.exe"; 77 | if (dlg.ShowDialog() == DialogResult.OK) 78 | { 79 | base.ItemFilePath = dlg.FileName; 80 | Arguments = "\"%1\""; 81 | ItemText = FileVersionInfo.GetVersionInfo(dlg.FileName).FileDescription; 82 | } 83 | } 84 | } 85 | 86 | private void AddNewItem() 87 | { 88 | using (var key = RegistryEx.GetRegistryKey(AppRegPath, true, true)) 89 | { 90 | key.SetValue("FriendlyAppName", ItemText); 91 | } 92 | using (var cmdKey = RegistryEx.GetRegistryKey(CommandPath, true, true)) 93 | { 94 | cmdKey.SetValue("", ItemCommand); 95 | RegPath = cmdKey.Name; 96 | } 97 | } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/SendToList.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System; 5 | using System.IO; 6 | using System.Windows.Forms; 7 | 8 | namespace ContextMenuManager.Controls 9 | { 10 | sealed class SendToList : MyList // 主页 发送到 11 | { 12 | public static readonly string SendToPath = Environment.ExpandEnvironmentVariables(@"%AppData%\Microsoft\Windows\SendTo"); 13 | public static readonly string DefaultSendToPath = Environment.ExpandEnvironmentVariables(@"%SystemDrive%\Users\Default\AppData\Roaming\Microsoft\Windows\SendTo"); 14 | 15 | public void LoadItems() 16 | { 17 | foreach (string path in Directory.GetFileSystemEntries(SendToPath)) 18 | { 19 | if (Path.GetFileName(path).ToLower() == "desktop.ini") continue; 20 | AddItem(new SendToItem(path)); 21 | } 22 | SortItemByText(); 23 | AddNewItem(); 24 | AddDirItem(); // 发送到右键菜单Ink文件的文件夹存放位置 25 | AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.SendToDrive)); 26 | AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.DeferBuildSendTo)); 27 | } 28 | 29 | private void AddNewItem() 30 | { 31 | NewItem newItem = new NewItem(); 32 | InsertItem(newItem, 0); 33 | newItem.AddNewItem += () => 34 | { 35 | using (NewLnkFileDialog dlg = new NewLnkFileDialog()) 36 | { 37 | dlg.FileFilter = $"{AppString.Dialog.Program}|*.exe;*.bat;*.cmd;*.vbs;*.vbe;*.js;*.jse;*.wsf"; 38 | if (dlg.ShowDialog() != DialogResult.OK) return; 39 | string lnkPath = $@"{SendToPath}\{ObjectPath.RemoveIllegalChars(dlg.ItemText)}.lnk"; 40 | lnkPath = ObjectPath.GetNewPathWithIndex(lnkPath, ObjectPath.PathType.File); 41 | using (ShellLink shellLink = new ShellLink(lnkPath)) 42 | { 43 | shellLink.TargetPath = dlg.ItemFilePath; 44 | shellLink.WorkingDirectory = Path.GetDirectoryName(dlg.ItemFilePath); 45 | shellLink.Arguments = dlg.Arguments; 46 | shellLink.Save(); 47 | } 48 | DesktopIni.SetLocalizedFileNames(lnkPath, dlg.ItemText); 49 | InsertItem(new SendToItem(lnkPath), 2); 50 | } 51 | }; 52 | } 53 | 54 | private void AddDirItem() 55 | { 56 | MyListItem item = new MyListItem 57 | { 58 | Text = Path.GetFileNameWithoutExtension(SendToPath), 59 | Image = ResourceIcon.GetFolderIcon(SendToPath).ToBitmap() 60 | }; 61 | PictureButton btnPath = new PictureButton(AppImage.Open); 62 | ToolTipBox.SetToolTip(btnPath, AppString.Menu.FileLocation); 63 | btnPath.MouseDown += (sender, e) => ExternalProgram.OpenDirectory(SendToPath); 64 | item.AddCtr(btnPath); 65 | InsertItem(item, 1); 66 | item.ContextMenuStrip = new ContextMenuStrip(); 67 | var tsiRestoreDefault = new RToolStripMenuItem(AppString.Menu.RestoreDefault); 68 | item.ContextMenuStrip.Items.Add(tsiRestoreDefault); 69 | tsiRestoreDefault.Enabled = Directory.Exists(DefaultSendToPath); 70 | tsiRestoreDefault.Click += (sender, e) => 71 | { 72 | if (AppMessageBox.Show(AppString.Message.RestoreDefault, MessageBoxButtons.OKCancel) == DialogResult.OK) 73 | { 74 | File.SetAttributes(SendToPath, FileAttributes.Normal); 75 | Directory.Delete(SendToPath, true); 76 | Directory.CreateDirectory(SendToPath); 77 | File.SetAttributes(SendToPath, File.GetAttributes(DefaultSendToPath)); 78 | foreach (string srcPath in Directory.GetFiles(DefaultSendToPath)) 79 | { 80 | string dstPath = $@"{SendToPath}\{Path.GetFileName(srcPath)}"; 81 | File.Copy(srcPath, dstPath); 82 | } 83 | ClearItems(); 84 | LoadItems(); 85 | } 86 | }; 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/UwpModeItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Controls.Interfaces; 4 | using ContextMenuManager.Methods; 5 | using Microsoft.Win32; 6 | using System; 7 | using System.Windows.Forms; 8 | 9 | namespace ContextMenuManager.Controls 10 | { 11 | sealed class UwpModeItem : MyListItem, IChkVisibleItem, ITsiRegPathItem, ITsiFilePathItem, 12 | IBtnShowMenuItem, ITsiWebSearchItem, ITsiRegExportItem, ITsiRegDeleteItem, ITsiGuidItem 13 | { 14 | public UwpModeItem(string uwpName, Guid guid) 15 | { 16 | Guid = guid; 17 | UwpName = uwpName; 18 | InitializeComponents(); 19 | Visible = UwpHelper.GetPackageName(uwpName) != null; 20 | Image = GuidInfo.GetImage(guid); 21 | Text = ItemText; 22 | } 23 | 24 | public Guid Guid { get; set; } 25 | public string UwpName { get; set; } 26 | 27 | public bool ItemVisible // 是否显示于右键菜单中 28 | { 29 | get 30 | { 31 | foreach (string path in GuidBlockedList.BlockedPaths) 32 | { 33 | using (RegistryKey key = RegistryEx.GetRegistryKey(path)) 34 | { 35 | if (key == null) continue; 36 | if (key.GetValue(Guid.ToString("B")) != null) return false; 37 | } 38 | } 39 | return true; 40 | } 41 | set 42 | { 43 | foreach (string path in GuidBlockedList.BlockedPaths) 44 | { 45 | if (value) 46 | { 47 | RegistryEx.DeleteValue(path, Guid.ToString("B")); 48 | } 49 | else 50 | { 51 | Registry.SetValue(path, Guid.ToString("B"), ""); 52 | } 53 | } 54 | ExplorerRestarter.Show(); 55 | } 56 | } 57 | 58 | public string ItemText => GuidInfo.GetText(Guid); 59 | public string RegPath => UwpHelper.GetRegPath(UwpName, Guid); 60 | public string ItemFilePath => UwpHelper.GetFilePath(UwpName, Guid); 61 | 62 | public string SearchText => Text; 63 | public string ValueName => "DllPath"; 64 | public MenuButton BtnShowMenu { get; set; } 65 | public VisibleCheckBox ChkVisible { get; set; } 66 | public DetailedEditButton BtnDetailedEdit { get; set; } 67 | public RegLocationMenuItem TsiRegLocation { get; set; } 68 | public FileLocationMenuItem TsiFileLocation { get; set; } 69 | public FilePropertiesMenuItem TsiFileProperties { get; set; } 70 | public WebSearchMenuItem TsiSearch { get; set; } 71 | public DeleteMeMenuItem TsiDeleteMe { get; set; } 72 | public RegExportMenuItem TsiRegExport { get; set; } 73 | public HandleGuidMenuItem TsiHandleGuid { get; set; } 74 | 75 | readonly RToolStripMenuItem TsiDetails = new RToolStripMenuItem(AppString.Menu.Details); 76 | 77 | private void InitializeComponents() 78 | { 79 | BtnShowMenu = new MenuButton(this); 80 | ChkVisible = new VisibleCheckBox(this); 81 | BtnDetailedEdit = new DetailedEditButton(this); 82 | TsiSearch = new WebSearchMenuItem(this); 83 | TsiFileLocation = new FileLocationMenuItem(this); 84 | TsiFileProperties = new FilePropertiesMenuItem(this); 85 | TsiRegLocation = new RegLocationMenuItem(this); 86 | TsiDeleteMe = new DeleteMeMenuItem(this); 87 | TsiRegExport = new RegExportMenuItem(this); 88 | TsiHandleGuid = new HandleGuidMenuItem(this); 89 | 90 | ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiHandleGuid, 91 | new RToolStripSeparator(), TsiDetails, new RToolStripSeparator(), TsiDeleteMe }); 92 | TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new RToolStripSeparator(), 93 | TsiFileProperties, TsiFileLocation, TsiRegLocation, TsiRegExport }); 94 | } 95 | 96 | public void DeleteMe() 97 | { 98 | RegistryEx.DeleteKeyTree(RegPath); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/GuidBlockedItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Controls.Interfaces; 4 | using ContextMenuManager.Methods; 5 | using System; 6 | using System.Linq; 7 | using System.Windows.Forms; 8 | 9 | namespace ContextMenuManager.Controls 10 | { 11 | class GuidBlockedItem : MyListItem, IBtnShowMenuItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiGuidItem, ITsiRegPathItem 12 | { 13 | public GuidBlockedItem(string value) 14 | { 15 | InitializeComponents(); 16 | Value = value; 17 | if (GuidEx.TryParse(value, out Guid guid)) 18 | { 19 | Guid = guid; 20 | Image = GuidInfo.GetImage(guid); 21 | ItemFilePath = GuidInfo.GetFilePath(Guid); 22 | } 23 | else 24 | { 25 | Guid = Guid.Empty; 26 | Image = AppImage.SystemFile; 27 | } 28 | Text = ItemText; 29 | } 30 | 31 | public string Value { get; set; } 32 | public Guid Guid { get; set; } 33 | public string SearchText => Value; 34 | public string ValueName => Value; 35 | public string RegPath 36 | { 37 | get 38 | { 39 | foreach (string path in GuidBlockedList.BlockedPaths) 40 | { 41 | using (var key = RegistryEx.GetRegistryKey(path)) 42 | { 43 | if (key == null) continue; 44 | if (key.GetValueNames().Contains(Value, StringComparer.OrdinalIgnoreCase)) return path; 45 | } 46 | } 47 | return null; 48 | } 49 | } 50 | 51 | public string ItemText 52 | { 53 | get 54 | { 55 | string text; 56 | if (GuidEx.TryParse(Value, out Guid guid)) text = GuidInfo.GetText(guid); 57 | else text = AppString.Message.MalformedGuid; 58 | text += "\n" + Value; 59 | return text; 60 | } 61 | } 62 | 63 | public string ItemFilePath { get; set; } 64 | public MenuButton BtnShowMenu { get; set; } 65 | public DetailedEditButton BtnDetailedEdit { get; set; } 66 | public WebSearchMenuItem TsiSearch { get; set; } 67 | public FileLocationMenuItem TsiFileLocation { get; set; } 68 | public FilePropertiesMenuItem TsiFileProperties { get; set; } 69 | public HandleGuidMenuItem TsiHandleGuid { get; set; } 70 | public RegLocationMenuItem TsiRegLocation { get; set; } 71 | 72 | readonly RToolStripMenuItem TsiDetails = new RToolStripMenuItem(AppString.Menu.Details); 73 | readonly RToolStripMenuItem TsiDelete = new RToolStripMenuItem(AppString.Menu.Delete); 74 | 75 | private void InitializeComponents() 76 | { 77 | BtnShowMenu = new MenuButton(this); 78 | BtnDetailedEdit = new DetailedEditButton(this); 79 | TsiSearch = new WebSearchMenuItem(this); 80 | TsiFileProperties = new FilePropertiesMenuItem(this); 81 | TsiFileLocation = new FileLocationMenuItem(this); 82 | TsiRegLocation = new RegLocationMenuItem(this); 83 | TsiHandleGuid = new HandleGuidMenuItem(this); 84 | 85 | ContextMenuStrip.Items.AddRange(new ToolStripItem[] {TsiHandleGuid, 86 | new RToolStripSeparator(), TsiDetails, new RToolStripSeparator(), TsiDelete }); 87 | TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, 88 | new RToolStripSeparator(), TsiFileProperties, TsiFileLocation, TsiRegLocation}); 89 | 90 | TsiDelete.Click += (sender, e) => DeleteMe(); 91 | } 92 | 93 | public void DeleteMe() 94 | { 95 | if (AppMessageBox.Show(AppString.Message.ConfirmDelete, MessageBoxButtons.YesNo) != DialogResult.Yes) return; 96 | Array.ForEach(GuidBlockedList.BlockedPaths, path => RegistryEx.DeleteValue(path, Value)); 97 | if (!Guid.Equals(Guid.Empty)) ExplorerRestarter.Show(); 98 | int index = Parent.Controls.GetChildIndex(this); 99 | index -= (index < Parent.Controls.Count - 1) ? 0 : 1; 100 | Parent.Controls[index].Focus(); 101 | Dispose(); 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/NewItemForm.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System; 5 | using System.Drawing; 6 | using System.Windows.Forms; 7 | 8 | namespace ContextMenuManager.Controls 9 | { 10 | class NewItemForm : ResizeLimitedForm 11 | { 12 | public NewItemForm() 13 | { 14 | AcceptButton = btnOK; 15 | CancelButton = btnCancel; 16 | Text = AppString.Other.NewItem; 17 | Font = SystemFonts.MenuFont; 18 | MaximizeBox = MinimizeBox = false; 19 | ShowIcon = ShowInTaskbar = false; 20 | StartPosition = FormStartPosition.CenterParent; 21 | SizeGripStyle = SizeGripStyle.Hide; 22 | VerticalResizable = false; 23 | InitializeComponents(); 24 | } 25 | 26 | public string ItemText { get => txtText.Text; set => txtText.Text = value; } 27 | public string ItemFilePath { get => txtFilePath.Text; set => txtFilePath.Text = value; } 28 | public string Arguments { get => txtArguments.Text; set => txtArguments.Text = value; } 29 | public string ItemCommand 30 | { 31 | get 32 | { 33 | string filePath = ItemFilePath; 34 | string arguments = Arguments; 35 | if (arguments.IsNullOrWhiteSpace()) return filePath; 36 | if (filePath.IsNullOrWhiteSpace()) return arguments; 37 | if (filePath.Contains(" ")) filePath = $"\"{filePath}\""; 38 | if (!arguments.Contains("\"")) arguments = $"\"{arguments}\""; 39 | return $"{filePath} {arguments}"; 40 | } 41 | } 42 | 43 | protected readonly Label lblText = new Label 44 | { 45 | Text = AppString.Dialog.ItemText, 46 | AutoSize = true 47 | }; 48 | protected readonly Label lblCommand = new Label 49 | { 50 | Text = AppString.Dialog.ItemCommand, 51 | AutoSize = true 52 | }; 53 | protected readonly Label lblArguments = new Label 54 | { 55 | Text = AppString.Dialog.CommandArguments, 56 | AutoSize = true 57 | }; 58 | protected readonly TextBox txtText = new TextBox(); 59 | protected readonly TextBox txtFilePath = new TextBox(); 60 | protected readonly TextBox txtArguments = new TextBox(); 61 | protected readonly Button btnBrowse = new Button 62 | { 63 | Text = AppString.Dialog.Browse, 64 | AutoSize = true 65 | }; 66 | protected readonly Button btnOK = new Button 67 | { 68 | Text = ResourceString.OK, 69 | AutoSize = true 70 | }; 71 | protected readonly Button btnCancel = new Button 72 | { 73 | DialogResult = DialogResult.Cancel, 74 | Text = ResourceString.Cancel, 75 | AutoSize = true 76 | }; 77 | 78 | protected virtual void InitializeComponents() 79 | { 80 | Controls.AddRange(new Control[] { lblText, lblCommand, lblArguments, 81 | txtText, txtFilePath, txtArguments, btnBrowse, btnOK, btnCancel }); 82 | int a = 20.DpiZoom(); 83 | btnBrowse.Anchor = btnOK.Anchor = btnCancel.Anchor = AnchorStyles.Right | AnchorStyles.Top; 84 | txtText.Top = lblText.Top = lblText.Left = lblCommand.Left = lblArguments.Left = a; 85 | btnBrowse.Top = txtFilePath.Top = lblCommand.Top = txtText.Bottom + a; 86 | lblArguments.Top = txtArguments.Top = txtFilePath.Bottom + a; 87 | btnOK.Top = btnCancel.Top = txtArguments.Bottom + a; 88 | btnCancel.Left = btnBrowse.Left = ClientSize.Width - btnCancel.Width - a; 89 | btnOK.Left = btnCancel.Left - btnOK.Width - a; 90 | int b = Math.Max(Math.Max(lblText.Width, lblCommand.Width), lblArguments.Width) + btnBrowse.Width + 4 * a; 91 | ClientSize = new Size(320.DpiZoom() + b, btnOK.Bottom + a); 92 | MinimumSize = Size; 93 | Resize += (sender, e) => 94 | { 95 | txtText.Width = txtFilePath.Width = txtArguments.Width = ClientSize.Width - b; 96 | txtText.Left = txtFilePath.Left = txtArguments.Left = btnBrowse.Left - txtFilePath.Width - a; 97 | }; 98 | OnResize(null); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/EncodingType.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace BluePointLilac.Methods 7 | { 8 | /* 获取文本文件编码类型 9 | * 代码参考:https://www.cnblogs.com/guyun/p/4262587.html (Napoléon)*/ 10 | public static class EncodingType 11 | { 12 | /// 各种带BOM的编码BOM值 13 | private static readonly Dictionary EncodingBomBytes = new Dictionary 14 | { 15 | { new byte[] { 0xEF, 0xBB, 0xBF }, Encoding.UTF8 }, //UTF-8 EF BB BF 16 | { new byte[] { 0xFF, 0xFE, 0x00, 0x00 }, Encoding.UTF32 }, //UTF-32LE FF FE 00 00 17 | { new byte[] { 0xFF, 0xFE }, Encoding.Unicode }, //UTF-16LE FF FE 18 | { new byte[] { 0xFE, 0xFF }, Encoding.BigEndianUnicode }, //UTF-16BE FE FF 19 | { new byte[] { 0x2B, 0x2F, 0x76 }, Encoding.UTF7 }, //UTF-7 2B 2F 76 20 | { new byte[] { 0x00, 0x00, 0xFE, 0xFF }, new UTF32Encoding(true, true) }, //UTF-32BE 00 00 FE FF 21 | }; 22 | 23 | /// 获取给定的文件的编码类型 24 | /// 文件路径 25 | /// 文件的编码类型 26 | public static Encoding GetType(string filePath) 27 | { 28 | byte[] fs = File.ReadAllBytes(filePath); 29 | foreach (var kv in EncodingBomBytes) 30 | { 31 | if (fs.Length < kv.Key.Length) continue; 32 | int i = -1; 33 | bool flag = kv.Key.All(s => { i++; return s == fs[i]; }); 34 | if (flag) return kv.Value; 35 | } 36 | if (IsUTF8Bytes(fs)) return Encoding.UTF8; //不带BOM的UTF-8 37 | return Encoding.Default; 38 | } 39 | 40 | /// 判断是否是不带 BOM 的 UTF8 格式 41 | /// 42 | private static bool IsUTF8Bytes(byte[] bytes) 43 | { 44 | int count = 1; //计算当前正分析的字符应还有的字节数 45 | for (int i = 0; i < bytes.Length; i++) 46 | { 47 | byte curByte = bytes[i];//当前分析的字节. 48 | if (count == 1) 49 | { 50 | if (curByte >= 0x80) 51 | { 52 | //计算当前字符所占的字节数 53 | //修复了EncodingType内造成en语言下debug错误的问题(算术溢出) 54 | int byteCount = 7; 55 | if (curByte < 0xC0) 56 | { 57 | byteCount = 0; 58 | } 59 | else if (curByte < 0xE0) 60 | { 61 | byteCount = 1; 62 | } 63 | else if (curByte < 0xF0) 64 | { 65 | byteCount = 2; 66 | } 67 | else if (curByte < 0xF8) 68 | { 69 | byteCount = 3; 70 | } 71 | else if (curByte < 0xFC) 72 | { 73 | byteCount = 4; 74 | } 75 | else if (curByte < 0xFE) 76 | { 77 | byteCount = 5; 78 | } 79 | else if (curByte < 0xFF) 80 | { 81 | byteCount = 6; 82 | } 83 | count += byteCount; 84 | /*while (((curByte <<= 1) & 0x80) != 0) 85 | { 86 | count++; 87 | }*/ 88 | //标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X 89 | if (count == 1 || count > 6) return false; 90 | } 91 | } 92 | else 93 | { 94 | //若是UTF-8 此时第一位必须为1 95 | if ((curByte & 0xC0) != 0x80) return false; 96 | else count--; 97 | } 98 | } 99 | //if(count > 1) throw new Exception("非预期的byte格式"); 100 | return true; 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Controls/DownloadDialog.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using System; 3 | using System.Diagnostics; 4 | using System.Drawing; 5 | using System.IO; 6 | using System.Windows.Forms; 7 | 8 | namespace BluePointLilac.Controls 9 | { 10 | sealed class DownloadDialog : CommonDialog 11 | { 12 | public string Text { get; set; } 13 | public string Url { get; set; } 14 | public string FilePath { get; set; } 15 | public override void Reset() { } 16 | 17 | protected override bool RunDialog(IntPtr hwndOwner) 18 | { 19 | using (Process process = Process.GetCurrentProcess()) 20 | using (DownloadForm frm = new DownloadForm()) 21 | { 22 | frm.Url = Url; 23 | frm.Text = Text; 24 | frm.FilePath = FilePath; 25 | return frm.ShowDialog() == DialogResult.OK; 26 | } 27 | } 28 | 29 | sealed class DownloadForm : RForm 30 | { 31 | public DownloadForm() 32 | { 33 | SuspendLayout(); 34 | Font = SystemFonts.MessageBoxFont; 35 | FormBorderStyle = FormBorderStyle.FixedSingle; 36 | MinimizeBox = MaximizeBox = ShowInTaskbar = false; 37 | Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath); 38 | Controls.AddRange(new Control[] { pgbDownload, btnCancel }); 39 | Load += (sender, e) => DownloadFile(Url, FilePath); 40 | InitializeComponents(); 41 | ResumeLayout(); 42 | InitTheme(); 43 | } 44 | 45 | readonly ProgressBar pgbDownload = new ProgressBar 46 | { 47 | Width = 200.DpiZoom(), 48 | Maximum = 100 49 | }; 50 | readonly Button btnCancel = new Button 51 | { 52 | DialogResult = DialogResult.Cancel, 53 | Text = ResourceString.Cancel, 54 | AutoSize = true 55 | }; 56 | 57 | public string Url { get; set; } 58 | public string FilePath { get; set; } 59 | 60 | private void InitializeComponents() 61 | { 62 | int a = 20.DpiZoom(); 63 | pgbDownload.Left = pgbDownload.Top = btnCancel.Top = a; 64 | pgbDownload.Height = btnCancel.Height; 65 | btnCancel.Left = pgbDownload.Right + a; 66 | ClientSize = new Size(btnCancel.Right + a, btnCancel.Bottom + a); 67 | } 68 | 69 | private void DownloadFile(string url, string filePath) 70 | { 71 | try 72 | { 73 | using (UAWebClient client = new UAWebClient()) 74 | { 75 | client.DownloadProgressChanged += (sender, e) => 76 | { 77 | int value = e.ProgressPercentage; 78 | Text = $"Downloading: {value}%"; 79 | pgbDownload.Value = value; 80 | if (DialogResult == DialogResult.Cancel) 81 | { 82 | client.CancelAsync(); 83 | File.Delete(FilePath); 84 | } 85 | }; 86 | client.DownloadFileCompleted += (sender, e) => 87 | { 88 | DialogResult = DialogResult.OK; 89 | }; 90 | client.DownloadFileAsync(new Uri(url), filePath); 91 | } 92 | } 93 | catch (Exception e) 94 | { 95 | MessageBox.Show(e.Message, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning); 96 | DialogResult = DialogResult.Cancel; 97 | } 98 | } 99 | 100 | protected override void OnLoad(EventArgs e) 101 | { 102 | if (Owner == null && Form.ActiveForm != this) Owner = Form.ActiveForm; 103 | if (Owner == null) StartPosition = FormStartPosition.CenterScreen; 104 | else 105 | { 106 | TopMost = true; 107 | StartPosition = FormStartPosition.CenterParent; 108 | } 109 | base.OnLoad(e); 110 | } 111 | } 112 | } 113 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Controls/PictureButton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Drawing.Imaging; 4 | using System.Windows.Forms; 5 | 6 | namespace BluePointLilac.Controls 7 | { 8 | public class PictureButton : PictureBox 9 | { 10 | private Timer animationTimer; 11 | private float currentOpacity = 0f; 12 | private float targetOpacity = 0f; 13 | private const float ANIMATION_SPEED = 0.1f; 14 | 15 | public PictureButton(Image image) 16 | { 17 | BaseImage = image; 18 | SizeMode = PictureBoxSizeMode.AutoSize; 19 | Cursor = Cursors.Hand; 20 | 21 | // 初始化动画计时器 22 | animationTimer = new Timer(); 23 | animationTimer.Interval = 16; // ~60 FPS 24 | animationTimer.Tick += AnimationTimer_Tick; 25 | } 26 | 27 | private Image baseImage; 28 | public Image BaseImage 29 | { 30 | get => baseImage; 31 | set 32 | { 33 | baseImage = value; 34 | // 初始状态为禁用效果 35 | Image = CreateDisabledImage(value); 36 | } 37 | } 38 | 39 | protected override void OnMouseEnter(EventArgs e) 40 | { 41 | base.OnMouseEnter(e); 42 | targetOpacity = 1f; // 目标为完全不透明 43 | animationTimer.Start(); 44 | } 45 | 46 | protected override void OnMouseLeave(EventArgs e) 47 | { 48 | base.OnMouseLeave(e); 49 | targetOpacity = 0f; // 目标为完全透明(禁用效果) 50 | animationTimer.Start(); 51 | } 52 | 53 | protected override void OnMouseDown(MouseEventArgs e) 54 | { 55 | if (e.Button == MouseButtons.Left) base.OnMouseDown(e); 56 | } 57 | 58 | private void AnimationTimer_Tick(object sender, EventArgs e) 59 | { 60 | // 逐步接近目标不透明度 61 | if (currentOpacity < targetOpacity) 62 | { 63 | currentOpacity += ANIMATION_SPEED; 64 | if (currentOpacity > targetOpacity) currentOpacity = targetOpacity; 65 | } 66 | else if (currentOpacity > targetOpacity) 67 | { 68 | currentOpacity -= ANIMATION_SPEED; 69 | if (currentOpacity < targetOpacity) currentOpacity = targetOpacity; 70 | } 71 | else 72 | { 73 | animationTimer.Stop(); 74 | return; 75 | } 76 | 77 | // 创建混合图像 78 | Image normalImage = BaseImage; 79 | Image disabledImage = CreateDisabledImage(BaseImage); 80 | 81 | // 创建一个临时位图来绘制混合效果 82 | Bitmap mixedImage = new Bitmap(normalImage.Width, normalImage.Height); 83 | using (Graphics g = Graphics.FromImage(mixedImage)) 84 | { 85 | // 先绘制禁用效果的图像 86 | g.DrawImage(disabledImage, 0, 0); 87 | 88 | // 然后根据当前不透明度绘制正常图像 89 | float opacity = Math.Max(0, Math.Min(1, currentOpacity)); 90 | ColorMatrix matrix = new ColorMatrix(); 91 | matrix.Matrix33 = opacity; // 设置透明度 92 | ImageAttributes attributes = new ImageAttributes(); 93 | attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); 94 | 95 | g.DrawImage( 96 | normalImage, 97 | new Rectangle(0, 0, normalImage.Width, normalImage.Height), 98 | 0, 0, normalImage.Width, normalImage.Height, 99 | GraphicsUnit.Pixel, 100 | attributes 101 | ); 102 | } 103 | 104 | // 更新显示的图像 105 | if (Image != null && Image != disabledImage && Image != baseImage) 106 | Image.Dispose(); 107 | 108 | Image = mixedImage; 109 | 110 | // 清理资源 111 | disabledImage.Dispose(); 112 | } 113 | 114 | private Image CreateDisabledImage(Image image) 115 | { 116 | return ToolStripRenderer.CreateDisabledImage(image); 117 | } 118 | 119 | // 添加资源清理 120 | protected override void Dispose(bool disposing) 121 | { 122 | if (disposing) 123 | { 124 | animationTimer?.Stop(); 125 | animationTimer?.Dispose(); 126 | } 127 | base.Dispose(disposing); 128 | } 129 | } 130 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Controls/SelectDialog.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Methods; 2 | using System; 3 | using System.Drawing; 4 | using System.Windows.Forms; 5 | 6 | namespace BluePointLilac.Controls 7 | { 8 | public class SelectDialog : CommonDialog 9 | { 10 | public string Title { get; set; } 11 | public string Selected { get; set; } 12 | public int SelectedIndex { get; set; } 13 | public string[] Items { get; set; } 14 | public bool CanEdit { get; set; } 15 | 16 | public override void Reset() { } 17 | 18 | protected override bool RunDialog(IntPtr hwndOwner) 19 | { 20 | using (SelectForm frm = new SelectForm()) 21 | { 22 | frm.Text = Title; 23 | frm.Items = Items; 24 | if (Selected != null) frm.Selected = Selected; 25 | else frm.SelectedIndex = SelectedIndex; 26 | frm.CanEdit = CanEdit; 27 | if (Control.FromHandle(hwndOwner) is Form owner) frm.TopMost = true; 28 | bool flag = frm.ShowDialog() == DialogResult.OK; 29 | if (flag) 30 | { 31 | Selected = frm.Selected; 32 | SelectedIndex = frm.SelectedIndex; 33 | } 34 | return flag; 35 | } 36 | } 37 | 38 | sealed class SelectForm : RForm 39 | { 40 | public SelectForm() 41 | { 42 | SuspendLayout(); 43 | AcceptButton = btnOK; 44 | CancelButton = btnCancel; 45 | Font = SystemFonts.MenuFont; 46 | ShowIcon = ShowInTaskbar = false; 47 | MaximizeBox = MinimizeBox = false; 48 | FormBorderStyle = FormBorderStyle.FixedSingle; 49 | StartPosition = FormStartPosition.CenterParent; 50 | InitializeComponents(); 51 | ResumeLayout(); 52 | InitTheme(); 53 | } 54 | 55 | public string Selected 56 | { 57 | get => cmbItems.Text; 58 | set => cmbItems.Text = value; 59 | } 60 | 61 | public string[] Items 62 | { 63 | get 64 | { 65 | string[] value = new string[cmbItems.Items.Count]; 66 | cmbItems.Items.CopyTo(value, 0); 67 | return value; 68 | } 69 | set 70 | { 71 | cmbItems.Items.Clear(); 72 | cmbItems.Items.AddRange(value); 73 | } 74 | } 75 | 76 | public bool CanEdit 77 | { 78 | get => cmbItems.DropDownStyle == ComboBoxStyle.DropDown; 79 | set => cmbItems.DropDownStyle = value ? ComboBoxStyle.DropDown : ComboBoxStyle.DropDownList; 80 | } 81 | 82 | public int SelectedIndex 83 | { 84 | get => cmbItems.SelectedIndex; 85 | set => cmbItems.SelectedIndex = value; 86 | } 87 | 88 | readonly Button btnOK = new Button 89 | { 90 | DialogResult = DialogResult.OK, 91 | Text = ResourceString.OK, 92 | AutoSize = true 93 | }; 94 | readonly Button btnCancel = new Button 95 | { 96 | DialogResult = DialogResult.Cancel, 97 | Text = ResourceString.Cancel, 98 | AutoSize = true 99 | }; 100 | readonly RComboBox cmbItems = new RComboBox 101 | { 102 | AutoCompleteMode = AutoCompleteMode.SuggestAppend, 103 | AutoCompleteSource = AutoCompleteSource.ListItems, 104 | DropDownHeight = 294.DpiZoom(), 105 | ImeMode = ImeMode.Disable 106 | }; 107 | 108 | private void InitializeComponents() 109 | { 110 | Controls.AddRange(new Control[] { cmbItems, btnOK, btnCancel }); 111 | int a = 20.DpiZoom(); 112 | cmbItems.Left = a; 113 | cmbItems.Width = 85.DpiZoom(); 114 | cmbItems.Top = btnOK.Top = btnCancel.Top = a; 115 | btnOK.Left = cmbItems.Right + a; 116 | btnCancel.Left = btnOK.Right + a; 117 | ClientSize = new Size(btnCancel.Right + a, btnCancel.Bottom + a); 118 | cmbItems.AutosizeDropDownWidth(); 119 | } 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/IEItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Controls.Interfaces; 4 | using ContextMenuManager.Methods; 5 | using Microsoft.Win32; 6 | using System; 7 | using System.Drawing; 8 | using System.Windows.Forms; 9 | 10 | namespace ContextMenuManager.Controls 11 | { 12 | sealed class IEItem : MyListItem, ITsiRegPathItem, ITsiFilePathItem, ITsiRegDeleteItem, ITsiCommandItem, 13 | ITsiWebSearchItem, ITsiTextItem, ITsiRegExportItem, IBtnShowMenuItem, IChkVisibleItem 14 | { 15 | public static readonly string[] MeParts = { "MenuExt", "-MenuExt" }; 16 | 17 | public IEItem(string regPath) 18 | { 19 | InitializeComponents(); 20 | RegPath = regPath; 21 | } 22 | 23 | private string regPath; 24 | public string RegPath 25 | { 26 | get => regPath; 27 | set 28 | { 29 | regPath = value; 30 | Text = ItemText; 31 | Image = ItemImage; 32 | } 33 | } 34 | public string ValueName => null; 35 | private string KeyName => RegistryEx.GetKeyName(RegPath); 36 | private string BackupPath => $@"{IEList.IEPath}\{(ItemVisible ? MeParts[1] : MeParts[0])}\{KeyName}"; 37 | private string MeKeyName => RegistryEx.GetKeyName(RegistryEx.GetParentPath(RegPath)); 38 | 39 | public string ItemText 40 | { 41 | get => RegistryEx.GetKeyName(RegPath); 42 | set 43 | { 44 | string newPath = $@"{RegistryEx.GetParentPath(RegPath)}\{value.Replace("\\", "")}"; 45 | string defaultValue = Registry.GetValue(newPath, "", null)?.ToString(); 46 | if (!defaultValue.IsNullOrWhiteSpace()) 47 | { 48 | AppMessageBox.Show(AppString.Message.HasBeenAdded); 49 | } 50 | else 51 | { 52 | RegistryEx.MoveTo(RegPath, newPath); 53 | RegPath = newPath; 54 | } 55 | } 56 | } 57 | 58 | public bool ItemVisible 59 | { 60 | get => MeKeyName.Equals(MeParts[0], StringComparison.OrdinalIgnoreCase); 61 | set 62 | { 63 | RegistryEx.MoveTo(RegPath, BackupPath); 64 | RegPath = BackupPath; 65 | } 66 | } 67 | 68 | public string ItemCommand 69 | { 70 | get => Registry.GetValue(RegPath, "", null)?.ToString(); 71 | set 72 | { 73 | Registry.SetValue(RegPath, "", value); 74 | Image = ItemImage; 75 | } 76 | } 77 | 78 | public string SearchText => $@"{AppString.SideBar.IEMenu} {Text}"; 79 | public string ItemFilePath => ObjectPath.ExtractFilePath(ItemCommand); 80 | private Icon ItemIcon => ResourceIcon.GetIcon(ItemFilePath) ?? ResourceIcon.GetExtensionIcon(ItemFilePath); 81 | private Image ItemImage => ItemIcon?.ToBitmap() ?? AppImage.NotFound; 82 | 83 | public MenuButton BtnShowMenu { get; set; } 84 | public VisibleCheckBox ChkVisible { get; set; } 85 | public WebSearchMenuItem TsiSearch { get; set; } 86 | public ChangeTextMenuItem TsiChangeText { get; set; } 87 | public ChangeCommandMenuItem TsiChangeCommand { get; set; } 88 | public FileLocationMenuItem TsiFileLocation { get; set; } 89 | public FilePropertiesMenuItem TsiFileProperties { get; set; } 90 | public RegLocationMenuItem TsiRegLocation { get; set; } 91 | public RegExportMenuItem TsiRegExport { get; set; } 92 | public DeleteMeMenuItem TsiDeleteMe { get; set; } 93 | readonly RToolStripMenuItem TsiDetails = new RToolStripMenuItem(AppString.Menu.Details); 94 | 95 | private void InitializeComponents() 96 | { 97 | BtnShowMenu = new MenuButton(this); 98 | ChkVisible = new VisibleCheckBox(this); 99 | TsiChangeText = new ChangeTextMenuItem(this); 100 | TsiChangeCommand = new ChangeCommandMenuItem(this); 101 | TsiSearch = new WebSearchMenuItem(this); 102 | TsiFileLocation = new FileLocationMenuItem(this); 103 | TsiFileProperties = new FilePropertiesMenuItem(this); 104 | TsiRegLocation = new RegLocationMenuItem(this); 105 | TsiRegExport = new RegExportMenuItem(this); 106 | TsiDeleteMe = new DeleteMeMenuItem(this); 107 | 108 | ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText, 109 | new RToolStripSeparator(), TsiDetails, new RToolStripSeparator(), TsiDeleteMe }); 110 | 111 | TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new RToolStripSeparator(), 112 | TsiChangeCommand, TsiFileProperties, TsiFileLocation, TsiRegLocation, TsiRegExport}); 113 | } 114 | 115 | public void DeleteMe() 116 | { 117 | RegistryEx.DeleteKeyTree(RegPath); 118 | RegistryEx.DeleteKeyTree(BackupPath); 119 | } 120 | } 121 | } -------------------------------------------------------------------------------- /ContextMenuManager/Controls/FoldSubItem.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Controls.Interfaces; 4 | using ContextMenuManager.Methods; 5 | using System; 6 | using System.Drawing; 7 | using System.IO; 8 | using System.Windows.Forms; 9 | using static ContextMenuManager.Methods.ObjectPath; 10 | 11 | namespace ContextMenuManager.Controls 12 | { 13 | class FoldSubItem : MyListItem 14 | { 15 | public FoldGroupItem FoldGroupItem { get; set; } 16 | 17 | public void Indent() 18 | { 19 | int w = 40.DpiZoom(); 20 | Controls["Image"].Left += w; 21 | Controls["Text"].Left += w; 22 | } 23 | } 24 | 25 | class FoldGroupItem : MyListItem, IBtnShowMenuItem 26 | { 27 | private bool isFold; 28 | public bool IsFold 29 | { 30 | get => isFold; 31 | set 32 | { 33 | if (isFold == value) return; 34 | isFold = value; 35 | FoldMe(value); 36 | } 37 | } 38 | 39 | public string GroupPath { get; set; } 40 | public PathType PathType { get; set; } 41 | 42 | public MenuButton BtnShowMenu { get; set; } 43 | readonly PictureButton btnFold; 44 | readonly PictureButton btnOpenPath; 45 | readonly RToolStripMenuItem tsiFoldAll = new RToolStripMenuItem(AppString.Menu.FoldAll); 46 | readonly RToolStripMenuItem tsiUnfoldAll = new RToolStripMenuItem(AppString.Menu.UnfoldAll); 47 | 48 | public FoldGroupItem(string groupPath, PathType pathType) 49 | { 50 | btnFold = new PictureButton(AppImage.Up); 51 | BtnShowMenu = new MenuButton(this); 52 | btnOpenPath = new PictureButton(AppImage.Open); 53 | 54 | if (pathType == PathType.File || pathType == PathType.Directory) 55 | { 56 | groupPath = Environment.ExpandEnvironmentVariables(groupPath); 57 | } 58 | string tip = null; 59 | Action openPath = null; 60 | switch (pathType) 61 | { 62 | case PathType.File: 63 | tip = AppString.Menu.FileLocation; 64 | Text = Path.GetFileNameWithoutExtension(groupPath); 65 | Image = ResourceIcon.GetExtensionIcon(groupPath).ToBitmap(); 66 | openPath = () => ExternalProgram.JumpExplorer(groupPath, AppConfig.OpenMoreExplorer); 67 | break; 68 | case PathType.Directory: 69 | tip = AppString.Menu.FileLocation; 70 | Text = Path.GetFileNameWithoutExtension(groupPath); 71 | Image = ResourceIcon.GetFolderIcon(groupPath).ToBitmap(); 72 | openPath = () => ExternalProgram.OpenDirectory(groupPath); 73 | break; 74 | case PathType.Registry: 75 | tip = AppString.Menu.RegistryLocation; 76 | openPath = () => ExternalProgram.JumpRegEdit(groupPath, null, AppConfig.OpenMoreRegedit); 77 | break; 78 | } 79 | PathType = pathType; 80 | GroupPath = groupPath; 81 | Font = new Font(Font, FontStyle.Bold); 82 | AddCtrs(new[] { btnFold, btnOpenPath }); 83 | ContextMenuStrip.Items.AddRange(new[] { tsiFoldAll, tsiUnfoldAll }); 84 | MouseDown += (sender, e) => 85 | { 86 | if (e.Button == MouseButtons.Left) Fold(); 87 | }; 88 | btnFold.MouseDown += (sender, e) => 89 | { 90 | Fold(); 91 | btnFold.Image = btnFold.BaseImage; 92 | }; 93 | tsiFoldAll.Click += (sender, e) => FoldAll(true); 94 | tsiUnfoldAll.Click += (sender, e) => FoldAll(false); 95 | btnOpenPath.MouseDown += (sender, e) => openPath.Invoke(); 96 | ToolTipBox.SetToolTip(btnOpenPath, tip); 97 | } 98 | 99 | public void SetVisibleWithSubItemCount() 100 | { 101 | foreach (Control ctr in Parent.Controls) 102 | { 103 | if (ctr is FoldSubItem item && item.FoldGroupItem == this) 104 | { 105 | Visible = true; 106 | return; 107 | } 108 | } 109 | Visible = false; 110 | } 111 | 112 | private void Fold() 113 | { 114 | Parent.SuspendLayout(); 115 | IsFold = !IsFold; 116 | Parent.ResumeLayout(); 117 | } 118 | 119 | private void FoldMe(bool isFold) 120 | { 121 | btnFold.BaseImage = isFold ? AppImage.Down : AppImage.Up; 122 | foreach (Control ctr in Parent?.Controls) 123 | { 124 | if (ctr is FoldSubItem item && item.FoldGroupItem == this) ctr.Visible = !isFold; 125 | } 126 | } 127 | 128 | private void FoldAll(bool isFold) 129 | { 130 | Parent.SuspendLayout(); 131 | foreach (Control ctr in Parent.Controls) 132 | { 133 | if (ctr is FoldGroupItem groupItem) groupItem.IsFold = isFold; 134 | } 135 | Parent.ResumeLayout(); 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /README-en.md: -------------------------------------------------------------------------------- 1 | **[简体中文](README.md)** | **English** 2 | 3 |
4 | 5 |
6 |

ContextMenuManager

7 |

A program to manage the Windows right-click context menu with support of Windows 7 - 11

8 | 9 |
10 | 11 | ![GitHub Release](https://img.shields.io/github/v/release/Jack251970/ContextMenuManager?label=Version) 12 | ![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/Jack251970/ContextMenuManager/total?label=Downloads) 13 | [![Stars](https://img.shields.io/github/stars/Jack251970/ContextMenuManager?style=flat&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZD0iTTggLjI1YS43NS43NSAwIDAgMSAuNjczLjQxOGwxLjg4MiAzLjgxNSA0LjIxLjYxMmEuNzUuNzUgMCAwIDEgLjQxNiAxLjI3OWwtMy4wNDYgMi45Ny43MTkgNC4xOTJhLjc1MS43NTEgMCAwIDEtMS4wODguNzkxTDggMTIuMzQ3bC0zLjc2NiAxLjk4YS43NS43NSAwIDAgMS0xLjA4OC0uNzlsLjcyLTQuMTk0TC44MTggNi4zNzRhLjc1Ljc1IDAgMCAxIC40MTYtMS4yOGw0LjIxLS42MTFMNy4zMjcuNjY4QS43NS43NSAwIDAgMSA4IC4yNVoiIGZpbGw9IiNlYWM1NGYiLz48L3N2Zz4=&logoSize=auto&label=Stars)](https://github.com/Jack251970/ContextMenuManager/) 14 | ![GitHub Issues or Pull Requests](https://img.shields.io/github/issues/Jack251970/ContextMenuManager?label=Issues) 15 | 16 |
17 | 18 | --- 19 | 20 | ## 💕 Thanks 21 | * This project is based on [https://github.com/BluePointLilac/ContextMenuManager](https://github.com/BluePointLilac/ContextMenuManager), thanks to [蓝点lilac](https://github.com/BluePointLilac)! 22 | * Thanks to [澜芸](https://github.com/LanYun2022) for new icons! 23 | 24 | ## 🚀 Download 25 | * [Latest Version][Latest]
26 | [Github Releases][GitHub Releases]
27 | [Gitee Releases][Gitee Releases]
28 | * Note: Download the zip file (recommended) or the exe file in the Assets list 29 | * About .NET versions: This application is based on the the .Net 9, and it is compatible with Windows 7, 8, 8.1, 10, 11.
30 | If your device does not include .Net 9, you may need to install the appropriate [.NET SDK]. 31 | 32 | ## ⭐ Features 33 | * Enable and disable context menu options for files, folders, submenus (e.g. open, send to), Internet Explorer, and Win+X, etc., and backup and restore right-click menus 34 | * Modify menu names and icons 35 | * Delete context menu entries 36 | * Navigate menus in the registry or File Explorer 37 | * Add custom menu items and commands 38 | 39 | ## 🖥️ Compatibility 40 | * Windows 7, 8, 8.1, 10, 11 41 | * 32 and 64 bit operating systems 42 | * Support for display scaling, optimal at 150% DPI 43 | * Localization support: contributions are welcome 44 | 45 | ## 🖼️ Screenshots 46 | 47 | 48 | 49 | Screenshots 50 | 51 | 52 | ## 🔣 Resources 53 | 54 | 55 | 56 | Resources 57 | 58 | 59 | ## 🪧 Notices 60 | * The program needs to read, write and delete a lot of registry entries and files, these behaviours are sensitive and may be mistakenly reported as viruses by Windows Defender, etc. If this happens, please add it to the whitelist by yourself. 61 | 62 | * Some special menu items (Shell extensions, file encryption, etc.) may not be displayed in the context menu, but will still show as enabled within the program; this is normal. 63 | 64 | * Different context menu manager programs may use different methods for disabling menu options. Using multiple managers at the same time is not recommended. 65 | 66 | * While other programs may use destructive methods, this program utilizes the registry keys provided by the system to hide menu items when possible. 67 | 68 | * If you have used other context menu managers in the past, use that program to restore the menu items before using this one in order to avoid any potential issues. 69 | 70 | * This program is not designed to perform clean uninstalls; however, it can help you find the registry and file locations of menu items so that they can be modified. If you are not familiar with such operations, it is recommended you use the enable/disable functions only. 71 | 72 | ## 🏆 Contributors 73 | Thanks to our amazing contributors! 74 | 75 | [![ContextMenuManager Contributors](https://contrib.rocks/image?repo=Jack251970/ContextMenuManager)](https://github.com/Jack251970/ContextMenuManager/graphs/contributors) 76 | 77 | Made with [contrib.rocks](https://contrib.rocks). 78 | 79 | [Latest]: https://github.com/Jack251970/ContextMenuManager/releases/latest 80 | [GitHub Releases]: https://github.com/Jack251970/ContextMenuManager/releases 81 | [Gitee Releases]: https://gitee.com/Jack251970/ContextMenuManager/releases 82 | [.NET SDK]: https://dotnet.microsoft.com/en-us/download/dotnet/9.0 83 | 84 | ## ❤️ Thank You 85 | 86 | If you are enjoying this plugin, then please support my work and enthusiasm by buying me a coffee on 87 | [https://ko-fi/jackye](https://ko-fi.com/jackye). 88 | 89 | [](https://ko-fi.com/jackye) 90 | -------------------------------------------------------------------------------- /ContextMenuManager/Controls/DictionariesBox.cs: -------------------------------------------------------------------------------- 1 | using BluePointLilac.Controls; 2 | using BluePointLilac.Methods; 3 | using ContextMenuManager.Methods; 4 | using System; 5 | using System.Drawing; 6 | using System.IO; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | 10 | namespace ContextMenuManager.Controls 11 | { 12 | sealed class DictionariesBox : TabControl 13 | { 14 | public DictionariesBox() 15 | { 16 | SuspendLayout(); 17 | Dock = DockStyle.Fill; 18 | Controls.AddRange(pages); 19 | ForeColor = MyMainForm.FormFore; 20 | BackColor = MyMainForm.FormBack; 21 | Font = SystemFonts.MenuFont; 22 | Font = new Font(Font.FontFamily, Font.Size + 1F); 23 | cms.Items.AddRange(items); 24 | for (int i = 0; i < 6; i++) 25 | { 26 | boxs[i] = new ReadOnlyRichTextBox { Parent = pages[i] }; 27 | if (i > 0) boxs[i].ContextMenuStrip = cms; 28 | } 29 | items[0].Click += (sender, e) => ExternalProgram.OpenNotepadWithText(GetInitialText()); 30 | items[2].Click += (sender, e) => SaveFile(); 31 | boxs[0].Controls.Add(btnOpenDir); 32 | btnOpenDir.Top = boxs[0].Height - btnOpenDir.Height; 33 | ToolTipBox.SetToolTip(btnOpenDir, AppString.Menu.FileLocation); 34 | btnOpenDir.MouseDown += (sender, e) => ExternalProgram.OpenDirectory(AppConfig.DicsDir); 35 | SelectedIndexChanged += (sender, e) => LoadText(); 36 | VisibleChanged += (sender, e) => this.SetEnabled(Visible); 37 | ResumeLayout(); 38 | } 39 | 40 | readonly TabPage[] pages = 41 | { 42 | new TabPage(AppString.Other.DictionaryDescription), 43 | new TabPage(AppString.SideBar.AppLanguage), 44 | new TabPage(AppString.Other.GuidInfosDictionary), 45 | new TabPage(AppString.SideBar.DetailedEdit), 46 | new TabPage(AppString.SideBar.EnhanceMenu), 47 | new TabPage(AppString.Other.UwpMode) 48 | }; 49 | readonly ReadOnlyRichTextBox[] boxs = new ReadOnlyRichTextBox[6]; 50 | readonly PictureButton btnOpenDir = new PictureButton(AppImage.Open) 51 | { 52 | Anchor = AnchorStyles.Left | AnchorStyles.Bottom, 53 | Left = 0 54 | }; 55 | readonly ContextMenuStrip cms = new ContextMenuStrip(); 56 | readonly ToolStripItem[] items = 57 | { 58 | new RToolStripMenuItem(AppString.Menu.Edit), 59 | new RToolStripSeparator(), 60 | new RToolStripMenuItem(AppString.Menu.Save) 61 | }; 62 | 63 | private void SaveFile() 64 | { 65 | using (SaveFileDialog dlg = new SaveFileDialog()) 66 | { 67 | string dirPath = AppConfig.UserDicsDir; 68 | switch (SelectedIndex) 69 | { 70 | case 1: 71 | dirPath = AppConfig.LangsDir; 72 | dlg.FileName = AppConfig.ZH_CNINI; 73 | break; 74 | case 2: 75 | dlg.FileName = AppConfig.GUIDINFOSDICINI; 76 | break; 77 | case 3: 78 | dlg.FileName = AppConfig.DETAILEDEDITDICXML; 79 | break; 80 | case 4: 81 | dlg.FileName = AppConfig.ENHANCEMENUSICXML; 82 | break; 83 | case 5: 84 | dlg.FileName = AppConfig.UWPMODEITEMSDICXML; 85 | break; 86 | } 87 | dlg.Filter = $"{dlg.FileName}|*{Path.GetExtension(dlg.FileName)}"; 88 | Directory.CreateDirectory(dirPath); 89 | dlg.InitialDirectory = dirPath; 90 | if (dlg.ShowDialog() == DialogResult.OK) 91 | { 92 | File.WriteAllText(dlg.FileName, GetInitialText(), Encoding.Unicode); 93 | } 94 | } 95 | } 96 | 97 | private string GetInitialText() 98 | { 99 | switch (SelectedIndex) 100 | { 101 | case 0: 102 | return AppString.Other.Dictionaries; 103 | case 1: 104 | return Properties.Resources.AppLanguageDic; 105 | case 2: 106 | return Properties.Resources.GuidInfosDic; 107 | case 3: 108 | return Properties.Resources.DetailedEditDic; 109 | case 4: 110 | return Properties.Resources.EnhanceMenusDic; 111 | case 5: 112 | return Properties.Resources.UwpModeItemsDic; 113 | default: 114 | return string.Empty; 115 | } 116 | } 117 | 118 | public void LoadText() 119 | { 120 | int index = SelectedIndex; 121 | if (boxs[index].Text.Length > 0) return; 122 | Action action = null; 123 | switch (index) 124 | { 125 | case 0: 126 | case 1: 127 | case 2: 128 | action = boxs[index].LoadIni; break; 129 | case 3: 130 | case 4: 131 | case 5: 132 | action = boxs[index].LoadXml; break; 133 | } 134 | BeginInvoke(action, new[] { GetInitialText() }); 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | 263 | # Output 264 | Output/ 265 | -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Controls/LoadingDialog.Designer.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http.HttpResults; 2 | using System; 3 | using System.ComponentModel; 4 | using System.Drawing; 5 | using System.Drawing.Drawing2D; 6 | using System.Windows.Forms; 7 | 8 | namespace BluePointLilac.Controls 9 | { 10 | sealed partial class LoadingDialog 11 | { 12 | /// 13 | /// Required designer variable. 14 | /// 15 | private IContainer components = null; 16 | 17 | /// 18 | /// Clean up any resources being used. 19 | /// 20 | /// true if managed resources should be disposed; otherwise, false. 21 | protected override void Dispose(bool disposing) 22 | { 23 | if (disposing && (components != null)) 24 | { 25 | components.Dispose(); 26 | } 27 | base.Dispose(disposing); 28 | } 29 | 30 | #region Windows Form Designer generated code 31 | 32 | /// 33 | /// Required method for Designer support - do not modify 34 | /// the contents of this method with the code editor. 35 | /// 36 | private void InitializeComponent() 37 | { 38 | progressBar = new NewProgressBar(); 39 | panel1 = new Panel(); 40 | panel1.SuspendLayout(); 41 | SuspendLayout(); 42 | // 43 | // progressBar 44 | // 45 | progressBar.Dock = DockStyle.Top; 46 | progressBar.Location = new System.Drawing.Point(10, 12); 47 | progressBar.Margin = new Padding(5, 6, 5, 6); 48 | progressBar.Name = "progressBar"; 49 | progressBar.Size = new System.Drawing.Size(560, 40); 50 | progressBar.Style = ProgressBarStyle.Blocks; 51 | progressBar.TabIndex = 0; 52 | progressBar.Value = 0; 53 | progressBar.Maximum = 100; 54 | progressBar.Minimum = 0; 55 | // 56 | // panel1 57 | // 58 | panel1.AutoSize = true; 59 | panel1.AutoSizeMode = AutoSizeMode.GrowAndShrink; 60 | panel1.BorderStyle = BorderStyle.FixedSingle; 61 | panel1.Controls.Add(progressBar); 62 | panel1.Location = new System.Drawing.Point(0, 0); 63 | panel1.Margin = new Padding(0); 64 | panel1.MinimumSize = new System.Drawing.Size(582, 19); 65 | panel1.Name = "panel1"; 66 | panel1.Padding = new Padding(10, 12, 10, 12); 67 | panel1.Size = new System.Drawing.Size(582, 66); 68 | panel1.TabIndex = 2; 69 | panel1.Resize += panel1_Resize; 70 | // 71 | // LoadingDialog 72 | // 73 | AutoScaleDimensions = new System.Drawing.SizeF(10F, 25F); 74 | AutoScaleMode = AutoScaleMode.Font; 75 | AutoSizeMode = AutoSizeMode.GrowAndShrink; 76 | BackColor = System.Drawing.SystemColors.Control; 77 | ClientSize = new System.Drawing.Size(581, 64); 78 | ControlBox = false; 79 | Controls.Add(panel1); 80 | FormBorderStyle = FormBorderStyle.None; 81 | Margin = new Padding(5, 6, 5, 6); 82 | MaximizeBox = false; 83 | MinimizeBox = false; 84 | Name = "LoadingDialog"; 85 | ShowIcon = false; 86 | ShowInTaskbar = false; 87 | SizeGripStyle = SizeGripStyle.Hide; 88 | StartPosition = FormStartPosition.CenterParent; 89 | panel1.ResumeLayout(false); 90 | ResumeLayout(false); 91 | PerformLayout(); 92 | 93 | } 94 | 95 | #endregion 96 | private Panel panel1; 97 | private NewProgressBar progressBar; 98 | } 99 | 100 | public class NewProgressBar : ProgressBar 101 | { 102 | private Color foreColor = MyMainForm.MainColor; 103 | private Color backColor = MyMainForm.ButtonMain; 104 | 105 | public NewProgressBar() 106 | { 107 | this.SetStyle(ControlStyles.UserPaint, true); 108 | } 109 | 110 | protected override void OnPaintBackground(PaintEventArgs pevent) 111 | { 112 | // None... Helps control the flicker. 113 | } 114 | protected override void OnPaint(PaintEventArgs e) 115 | { 116 | const int inset = 2; // A single inset value to control teh sizing of the inner rect. 117 | 118 | using (Image offscreenImage = new Bitmap(this.Width, this.Height)) 119 | { 120 | using (Graphics offscreen = Graphics.FromImage(offscreenImage)) 121 | { 122 | Rectangle rect = new Rectangle(0, 0, this.Width, this.Height); 123 | 124 | if (ProgressBarRenderer.IsSupported) 125 | ProgressBarRenderer.DrawHorizontalBar(offscreen, rect); 126 | 127 | rect.Inflate(new Size(-inset, -inset)); // Deflate inner rect. 128 | rect.Width = (int)(rect.Width * ((double)this.Value / this.Maximum)); 129 | if (rect.Width == 0) rect.Width = 1; // Can't draw rec with width of 0. 130 | 131 | /*LinearGradientBrush brush = new LinearGradientBrush(rect, backColor, foreColor, LinearGradientMode.Vertical);*/ 132 | using (SolidBrush brush = new SolidBrush(foreColor)) 133 | offscreen.FillRectangle(brush, inset, inset, rect.Width, rect.Height); 134 | 135 | e.Graphics.DrawImage(offscreenImage, 0, 0); 136 | } 137 | } 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /ContextMenuManager/BluePointLilac.Methods/ElevatedFileDroper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Drawing; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Windows.Forms; 7 | 8 | namespace BluePointLilac.Methods 9 | { 10 | /// 代码用途:管理员UAC进程窗口拖放文件 11 | /// 代码来源1:https://zhuanlan.zhihu.com/p/48735364 12 | /// 代码来源2:https://github.com/volschin/sdimager/blob/master/ElevatedDragDropManager.cs 13 | /// 代码作者:雨少主(知乎)、volschin(Github)、蓝点lilac(转载、修改) 14 | /// 调用方法:var droper = new ElevatedFileDroper(control); 15 | /// droper.DragDrop += (sender, e) => MessageBox.Show(droper.DropFilePaths[0]); 16 | /// 备注:此类只能生效一个实例,不能将control.AllowDrop设为true,droper.DragDrop与control.DragDrop不共存 17 | 18 | public sealed class ElevatedFileDroper : IMessageFilter 19 | { 20 | [DllImport("user32.dll", SetLastError = true)] 21 | [return: MarshalAs(UnmanagedType.Bool)] 22 | private static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd, uint message, ChangeFilterAction action, in ChangeFilterStruct pChangeFilterStruct); 23 | 24 | [DllImport("user32.dll", SetLastError = true)] 25 | [return: MarshalAs(UnmanagedType.Bool)] 26 | private static extern bool ChangeWindowMessageFilter(uint msg, ChangeWindowMessageFilterFlags flags); 27 | 28 | [DllImport("shell32.dll", SetLastError = false)] 29 | private static extern void DragAcceptFiles(IntPtr hWnd, bool fAccept); 30 | 31 | [DllImport("shell32.dll", SetLastError = false, CharSet = CharSet.Unicode)] 32 | private static extern uint DragQueryFile(IntPtr hWnd, uint iFile, StringBuilder lpszFile, int cch); 33 | 34 | [DllImport("shell32.dll", SetLastError = false)] 35 | private static extern bool DragQueryPoint(IntPtr hDrop, out Point lppt); 36 | 37 | [DllImport("shell32.dll", SetLastError = false)] 38 | private static extern void DragFinish(IntPtr hDrop); 39 | 40 | [StructLayout(LayoutKind.Sequential)] 41 | struct ChangeFilterStruct 42 | { 43 | public uint CbSize; 44 | public ChangeFilterStatu ExtStatus; 45 | } 46 | 47 | enum ChangeWindowMessageFilterFlags : uint 48 | { 49 | MSGFLT_ADD = 1, 50 | MSGFLT_REMOVE = 2 51 | } 52 | 53 | enum ChangeFilterAction : uint 54 | { 55 | MSGFLT_RESET, 56 | MSGFLT_ALLOW, 57 | MSGFLT_DISALLOW 58 | } 59 | 60 | enum ChangeFilterStatu : uint 61 | { 62 | MSGFLTINFO_NONE, 63 | MSGFLTINFO_ALREADYALLOWED_FORWND, 64 | MSGFLTINFO_ALREADYDISALLOWED_FORWND, 65 | MSGFLTINFO_ALLOWED_HIGHER 66 | } 67 | 68 | const uint WM_COPYGLOBALDATA = 0x0049; 69 | const uint WM_COPYDATA = 0x004A; 70 | const uint WM_DROPFILES = 0x0233; 71 | 72 | public event EventHandler DragDrop; 73 | public string[] DropFilePaths { get; private set; } 74 | public Point DropPoint { get; private set; } 75 | 76 | public ElevatedFileDroper(Control ctr) 77 | { 78 | ctr.AllowDrop = false; 79 | DragAcceptFiles(ctr.Handle, true); 80 | Application.AddMessageFilter(this); 81 | ctr.Disposed += (sender, e) => Application.RemoveMessageFilter(this); 82 | 83 | if (ctr is Form frm) 84 | { 85 | double opacity = frm.Opacity; 86 | frm.Paint += (sender, e) => 87 | { 88 | if (frm.Opacity != opacity) 89 | { 90 | //窗体透明度变化时需要重新注册接受文件拖拽标识符 91 | DragAcceptFiles(ctr.Handle, true); 92 | opacity = frm.Opacity; 93 | } 94 | }; 95 | } 96 | 97 | Version ver = Environment.OSVersion.Version; 98 | bool isVistaOrHigher = ver >= new Version(6, 0); 99 | bool isWin7OrHigher = ver >= new Version(6, 1); 100 | var status = new ChangeFilterStruct { CbSize = 8 }; 101 | if (isVistaOrHigher) 102 | { 103 | foreach (uint msg in new[] { WM_DROPFILES, WM_COPYGLOBALDATA, WM_COPYDATA }) 104 | { 105 | bool error = false; 106 | if (isWin7OrHigher) 107 | { 108 | error = !ChangeWindowMessageFilterEx(ctr.Handle, msg, ChangeFilterAction.MSGFLT_ALLOW, in status); 109 | } 110 | else 111 | { 112 | error = !ChangeWindowMessageFilter(msg, ChangeWindowMessageFilterFlags.MSGFLT_ADD); 113 | } 114 | if (error) throw new Win32Exception(Marshal.GetLastWin32Error()); 115 | } 116 | } 117 | } 118 | 119 | public bool PreFilterMessage(ref Message m) 120 | { 121 | if (m.Msg != WM_DROPFILES) return false; 122 | IntPtr handle = m.WParam; 123 | uint fileCount = DragQueryFile(handle, uint.MaxValue, null, 0); 124 | string[] filePaths = new string[fileCount]; 125 | for (uint i = 0; i < fileCount; i++) 126 | { 127 | StringBuilder sb = new StringBuilder(260); 128 | uint result = DragQueryFile(handle, i, sb, sb.Capacity); 129 | if (result > 0) filePaths[i] = sb.ToString(); 130 | } 131 | DragQueryPoint(handle, out Point point); 132 | DragFinish(handle); 133 | DropPoint = point; 134 | DropFilePaths = filePaths; 135 | DragDrop?.Invoke(this, null); 136 | return true; 137 | } 138 | } 139 | } --------------------------------------------------------------------------------