Pārlūkot izejas kodu

添加项目文件。

蓝点lilac 5 gadi atpakaļ
revīzija
2d1bf10caa
100 mainītis faili ar 9229 papildinājumiem un 0 dzēšanām
  1. 63 0
      .gitattributes
  2. 261 0
      .gitignore
  3. 25 0
      ContextMenuManager.sln
  4. 10 0
      ContextMenuManager/App.config
  5. 73 0
      ContextMenuManager/AppImage.cs
  6. 240 0
      ContextMenuManager/AppString.cs
  7. 28 0
      ContextMenuManager/BulePointLilac.Controls/IconDialog.cs
  8. 96 0
      ContextMenuManager/BulePointLilac.Controls/InputDialog.cs
  9. 25 0
      ContextMenuManager/BulePointLilac.Controls/MyCheckBox.cs
  10. 282 0
      ContextMenuManager/BulePointLilac.Controls/MyListBox.cs
  11. 36 0
      ContextMenuManager/BulePointLilac.Controls/MyMainForm.cs
  12. 201 0
      ContextMenuManager/BulePointLilac.Controls/MySideBar.cs
  13. 46 0
      ContextMenuManager/BulePointLilac.Controls/MyStatusBar.cs
  14. 111 0
      ContextMenuManager/BulePointLilac.Controls/MyToolBar.cs
  15. 68 0
      ContextMenuManager/BulePointLilac.Controls/MyToolTip.cs
  16. 41 0
      ContextMenuManager/BulePointLilac.Controls/PictureButton.cs
  17. 74 0
      ContextMenuManager/BulePointLilac.Controls/ResizbleForm.cs
  18. 48 0
      ContextMenuManager/BulePointLilac.Methods/ControlExtension.cs
  19. 72 0
      ContextMenuManager/BulePointLilac.Methods/EncodingType.cs
  20. 29 0
      ContextMenuManager/BulePointLilac.Methods/HighDpi.cs
  21. 64 0
      ContextMenuManager/BulePointLilac.Methods/ImageExtension.cs
  22. 78 0
      ContextMenuManager/BulePointLilac.Methods/IniFileHelper.cs
  23. 85 0
      ContextMenuManager/BulePointLilac.Methods/IniReader.cs
  24. 14 0
      ContextMenuManager/BulePointLilac.Methods/MessageBoxEx.cs
  25. 147 0
      ContextMenuManager/BulePointLilac.Methods/ObjectPath.cs
  26. 56 0
      ContextMenuManager/BulePointLilac.Methods/PropertiesDialog.cs
  27. 226 0
      ContextMenuManager/BulePointLilac.Methods/RegTrustedInstaller.cs
  28. 110 0
      ContextMenuManager/BulePointLilac.Methods/RegistryEx.cs
  29. 173 0
      ContextMenuManager/BulePointLilac.Methods/ResourceIcon.cs
  30. 22 0
      ContextMenuManager/BulePointLilac.Methods/ResourceString.cs
  31. 488 0
      ContextMenuManager/BulePointLilac.Methods/RichTextBoxExtension.cs
  32. 375 0
      ContextMenuManager/ContextMenuManager.csproj
  33. 272 0
      ContextMenuManager/Controls/AboutApp.cs
  34. 12 0
      ContextMenuManager/Controls/AddCommonButton.cs
  35. 63 0
      ContextMenuManager/Controls/ExplorerRestarter.cs
  36. 137 0
      ContextMenuManager/Controls/FileExtensionDialog.cs
  37. 65 0
      ContextMenuManager/Controls/GuidBlockedItem.cs
  38. 67 0
      ContextMenuManager/Controls/GuidBlockedList.cs
  39. 26 0
      ContextMenuManager/Controls/Interfaces/IBtnDeleteItem.cs
  40. 19 0
      ContextMenuManager/Controls/Interfaces/IBtnMoveUpDownItem.cs
  41. 21 0
      ContextMenuManager/Controls/Interfaces/IBtnOpenPathItem.cs
  42. 21 0
      ContextMenuManager/Controls/Interfaces/IBtnShowMenuItem.cs
  43. 27 0
      ContextMenuManager/Controls/Interfaces/IChkVisibleItem.cs
  44. 72 0
      ContextMenuManager/Controls/Interfaces/IFoldGroupItem.cs
  45. 38 0
      ContextMenuManager/Controls/Interfaces/ITsiCommandItem.cs
  46. 24 0
      ContextMenuManager/Controls/Interfaces/ITsiDeleteItem.cs
  47. 41 0
      ContextMenuManager/Controls/Interfaces/ITsiFilePathItem.cs
  48. 48 0
      ContextMenuManager/Controls/Interfaces/ITsiIconItem.cs
  49. 27 0
      ContextMenuManager/Controls/Interfaces/ITsiRegPathItem.cs
  50. 46 0
      ContextMenuManager/Controls/Interfaces/ITsiTextItem.cs
  51. 28 0
      ContextMenuManager/Controls/Interfaces/ITsiWebSearchItem.cs
  52. 19 0
      ContextMenuManager/Controls/NewItem.cs
  53. 81 0
      ContextMenuManager/Controls/NewItemForm.cs
  54. 87 0
      ContextMenuManager/Controls/NewOpenWithDialog.cs
  55. 116 0
      ContextMenuManager/Controls/NewSendToDialog.cs
  56. 138 0
      ContextMenuManager/Controls/NewShellDialog.cs
  57. 124 0
      ContextMenuManager/Controls/OpenWithItem.cs
  58. 68 0
      ContextMenuManager/Controls/OpenWithList.cs
  59. 209 0
      ContextMenuManager/Controls/RegRuleItem.cs
  60. 65 0
      ContextMenuManager/Controls/SelectItemsForm.cs
  61. 159 0
      ContextMenuManager/Controls/SendToItem.cs
  62. 56 0
      ContextMenuManager/Controls/SendToList.cs
  63. 227 0
      ContextMenuManager/Controls/ShellCommonDialog.cs
  64. 130 0
      ContextMenuManager/Controls/ShellExItem.cs
  65. 379 0
      ContextMenuManager/Controls/ShellItem.cs
  66. 279 0
      ContextMenuManager/Controls/ShellList.cs
  67. 213 0
      ContextMenuManager/Controls/ShellNewItem.cs
  68. 77 0
      ContextMenuManager/Controls/ShellNewList.cs
  69. 129 0
      ContextMenuManager/Controls/ShellStoreDialog.cs
  70. 432 0
      ContextMenuManager/Controls/ShellSubMenuDialog.cs
  71. 57 0
      ContextMenuManager/Controls/SubMenuModeForm.cs
  72. 100 0
      ContextMenuManager/Controls/ThirdRulesList.cs
  73. 121 0
      ContextMenuManager/Controls/WinXItem.cs
  74. 52 0
      ContextMenuManager/Controls/WinXList.cs
  75. 190 0
      ContextMenuManager/GuidInfo.cs
  76. 287 0
      ContextMenuManager/MainForm.cs
  77. 72 0
      ContextMenuManager/Program.cs
  78. 25 0
      ContextMenuManager/Properties/App.manifest
  79. BIN
      ContextMenuManager/Properties/AppIcon.ico
  80. 15 0
      ContextMenuManager/Properties/AssemblyInfo.cs
  81. 405 0
      ContextMenuManager/Properties/Resources.Designer.cs
  82. 196 0
      ContextMenuManager/Properties/Resources.resx
  83. BIN
      ContextMenuManager/Properties/Resources/Images/About.png
  84. BIN
      ContextMenuManager/Properties/Resources/Images/Add.png
  85. BIN
      ContextMenuManager/Properties/Resources/Images/AddCommon.png
  86. BIN
      ContextMenuManager/Properties/Resources/Images/AddExisting.png
  87. BIN
      ContextMenuManager/Properties/Resources/Images/AddSeparator.png
  88. BIN
      ContextMenuManager/Properties/Resources/Images/CustomType.png
  89. BIN
      ContextMenuManager/Properties/Resources/Images/Delete.png
  90. BIN
      ContextMenuManager/Properties/Resources/Images/Donate.png
  91. BIN
      ContextMenuManager/Properties/Resources/Images/Home.png
  92. BIN
      ContextMenuManager/Properties/Resources/Images/MicrosoftStore.png
  93. BIN
      ContextMenuManager/Properties/Resources/Images/NewItem.png
  94. BIN
      ContextMenuManager/Properties/Resources/Images/Open.png
  95. BIN
      ContextMenuManager/Properties/Resources/Images/SeparatorItem.png
  96. BIN
      ContextMenuManager/Properties/Resources/Images/Setting.png
  97. BIN
      ContextMenuManager/Properties/Resources/Images/Star.png
  98. BIN
      ContextMenuManager/Properties/Resources/Images/SubItems.png
  99. BIN
      ContextMenuManager/Properties/Resources/Images/TurnOff.png
  100. BIN
      ContextMenuManager/Properties/Resources/Images/TurnOn.png

+ 63 - 0
.gitattributes

@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs     diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following 
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln       merge=binary
+#*.csproj    merge=binary
+#*.vbproj    merge=binary
+#*.vcxproj   merge=binary
+#*.vcproj    merge=binary
+#*.dbproj    merge=binary
+#*.fsproj    merge=binary
+#*.lsproj    merge=binary
+#*.wixproj   merge=binary
+#*.modelproj merge=binary
+#*.sqlproj   merge=binary
+#*.wwaproj   merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg   binary
+#*.png   binary
+#*.gif   binary
+
+###############################################################################
+# diff behavior for common document formats
+# 
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the 
+# entries below.
+###############################################################################
+#*.doc   diff=astextplain
+#*.DOC   diff=astextplain
+#*.docx  diff=astextplain
+#*.DOCX  diff=astextplain
+#*.dot   diff=astextplain
+#*.DOT   diff=astextplain
+#*.pdf   diff=astextplain
+#*.PDF   diff=astextplain
+#*.rtf   diff=astextplain
+#*.RTF   diff=astextplain

+ 261 - 0
.gitignore

@@ -0,0 +1,261 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+#*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc

+ 25 - 0
ContextMenuManager.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29911.84
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ContextMenuManager", "ContextMenuManager\ContextMenuManager.csproj", "{EF7E60E9-3565-42BA-AFB3-7FE73A1B011C}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{EF7E60E9-3565-42BA-AFB3-7FE73A1B011C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EF7E60E9-3565-42BA-AFB3-7FE73A1B011C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EF7E60E9-3565-42BA-AFB3-7FE73A1B011C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{EF7E60E9-3565-42BA-AFB3-7FE73A1B011C}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {A0E97C81-86B8-4360-8251-DF6FD4494EDA}
+	EndGlobalSection
+EndGlobal

+ 10 - 0
ContextMenuManager/App.config

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <startup>
+    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/>
+  </startup>
+  <runtime>
+    <legacyCorruptedStateExceptionsPolicy enabled="true"/>
+    <EnableWindowsFormsHighDpiAutoResizing enabled="true"/>
+  </runtime>
+</configuration>

+ 73 - 0
ContextMenuManager/AppImage.cs

@@ -0,0 +1,73 @@
+using BulePointLilac.Methods;
+using ContextMenuManager.Properties;
+using System.Drawing;
+
+namespace ContextMenuManager
+{
+    public static class AppImage
+    {
+        private static readonly double Scale = HighDpi.DpiScale / 1.5;
+        ///<summary>主页图标</summary>
+        public static readonly Image Home = Resources.Home.ResizeImage(Scale);
+        ///<summary>文件类型图标</summary>
+        public static readonly Image Type = Resources.Type.ResizeImage(Scale);
+        ///<summary>五角星图标</summary>
+        public static readonly Image Star = Resources.Star.ResizeImage(Scale);
+        ///<summary>关于问号图标</summary>
+        public static readonly Image About = Resources.About.ResizeImage(Scale);
+        ///<summary>设置按钮图标</summary>
+        public static readonly Image Setting = Resources.Setting.ResizeImage(Scale);
+        ///<summary>开关打开状态图片</summary>
+        public static readonly Image TurnOn = Resources.TurnOn.ResizeImage(Scale);
+        ///<summary>开关关闭状态图片</summary>
+        public static readonly Image TurnOff = Resources.TurnOff.ResizeImage(Scale);
+        ///<summary>编辑子项图标</summary>
+        public static readonly Image SubItems = Resources.SubItems.ResizeImage(Scale);
+        ///<summary>删除图标</summary>
+        public static readonly Image Delete = Resources.Delete.ResizeImage(Scale);
+        ///<summary>添加图标</summary>
+        public static readonly Image AddNewItem = Resources.Add.ResizeImage(Scale);
+        ///<summary>添加常用菜单项图标</summary>
+        public static readonly Image AddCommon = Resources.AddCommon.ResizeImage(Scale);
+        ///<summary>添加已有项目图标</summary>
+        public static readonly Image AddExisting = Resources.AddExisting.ResizeImage(Scale);
+        ///<summary>添加分割线图标</summary>
+        public static readonly Image AddSeparator = Resources.AddSeparator.ResizeImage(Scale);
+        ///<summary>打开图标</summary>
+        public static readonly Image Open = Resources.Open.ResizeImage(Scale);
+        ///<summary>上图标</summary>
+        public static readonly Image Up = Resources.Up.ResizeImage(Scale);
+        ///<summary>下图标</summary>
+        public static readonly Image Down = Up.RotateImage(RotateFlipType.Rotate180FlipNone);
+        ///<summary>新建项目图标</summary>
+        public static readonly Image NewItem = Resources.NewItem.ResizeImage(Scale);
+        ///<summary>分隔线图标</summary>
+        public static readonly Image Separator = Resources.SeparatorItem.ResizeImage(Scale);
+        ///<summary>自定义类型图标</summary>
+        public static readonly Image CustomType = Resources.CustomType.ResizeImage(Scale);
+        ///<summary>所有文件类型图标</summary>
+        public static readonly Image Types = Resources.Types.ResizeImage(Scale);
+        ///<summary>Microsoft Store图标</summary>
+        public static readonly Image MicrosoftStore = Resources.MicrosoftStore.ResizeImage(Scale);
+        ///<summary>dll文件默认图标</summary>
+        public static readonly Image DllDefaultIcon = ResourceIcon.GetExtensionIcon(".dll").ToBitmap();
+        ///<summary>资源不存在图标</summary>
+        public static readonly Image NotFound = ResourceIcon.GetIcon("imageres.dll", 2).ToBitmap();
+        ///<summary>管理员小盾牌</summary>
+        public static readonly Image Shield = ResourceIcon.GetIcon("imageres.dll", 73).ToBitmap();
+        ///<summary>资源管理器图标</summary>
+        public static readonly Image Explorer = ResourceIcon.GetIcon("imageres.dll", 203).ToBitmap();
+        ///<summary>刷新图标</summary>
+        public static readonly Image Refresh = ResourceIcon.GetIcon("shell32.dll", 238).ToBitmap();
+        ///<summary>自定义文件夹图标</summary>
+        public static readonly Image CustomFolder = ResourceIcon.GetIcon("imageres.dll", 306).ToBitmap();
+        ///<summary>网络驱动器图标</summary>
+        public static readonly Image NetworkDrive = ResourceIcon.GetIcon("imageres.dll", 28).ToBitmap();
+        ///<summary>回收站属性图标</summary>
+        public static readonly Image RecycleBinProperties = ResourceIcon.GetIcon("imageres.dll", -5350).ToBitmap();
+        ///<summary>磁盘图标</summary>
+        public static readonly Image Drive = ResourceIcon.GetIcon("imageres.dll", 27).ToBitmap();
+        ///<summary>发送到图标</summary>
+        public static readonly Image SendTo = ResourceIcon.GetIcon("imageres.dll", 176).ToBitmap();
+    }
+}

+ 240 - 0
ContextMenuManager/AppString.cs

@@ -0,0 +1,240 @@
+using BulePointLilac.Methods;
+using System.Text;
+
+namespace ContextMenuManager
+{
+    public static class AppString
+    {
+        public static IniReader UserLanguage = new IniReader(Program.LanguageFilePath);
+        private static readonly IniReader DefaultLanguage = new IniReader(new StringBuilder(Properties.Resources.AppLanguageDic));
+
+        private static string GetValue(string section, string key)
+        {
+            string value = UserLanguage?.GetValue(section, key);
+            if(string.IsNullOrEmpty(value)) value = DefaultLanguage.GetValue(section, key);
+            return value.Replace("\\n", "\n");
+        }
+
+        private static string GetGeneralValue(string key) => GetValue("General", key);
+        private static string GetToolBarValue(string key) => GetValue("ToolBar", key);
+        private static string GetSideBarValue(string key) => GetValue("SideBar", key);
+        private static string GetStatusBarValue(string key) => GetValue("StatusBar", key);
+        private static string GetMessageBoxValue(string key) => GetValue("MessageBox", key);
+        private static string GetMenuValue(string key) => GetValue("Menu", key);
+        private static string GetTextValue(string key) => GetValue("Text", key);
+        private static string GetTipValue(string key) => GetValue("Tip", key);
+
+        #region 常规
+        public static string General_Language = GetGeneralValue("Language");
+        public static string General_AppName = GetGeneralValue("AppName");
+        #endregion
+        #region 工具栏
+        public static string ToolBar_Home = GetToolBarValue("Home");
+        public static string ToolBar_Type = GetToolBarValue("Type");
+        public static string ToolBar_Rule = GetToolBarValue("Rule");
+        public static string ToolBar_About = GetToolBarValue("About");
+        #endregion
+        #region 侧边栏
+        public static string SideBar_File = GetSideBarValue("File");
+        public static string SideBar_Folder = GetSideBarValue("Folder");
+        public static string SideBar_Directory = GetSideBarValue("Directory");
+        public static string SideBar_Background = GetSideBarValue("Background");
+        public static string SideBar_Desktop = GetSideBarValue("Desktop");
+        public static string SideBar_Drive = GetSideBarValue("Drive");
+        public static string SideBar_AllObjects = GetSideBarValue("AllObjects");
+        public static string SideBar_Computer = GetSideBarValue("Computer");
+        public static string SideBar_RecycleBin = GetSideBarValue("RecycleBin");
+        public static string SideBar_Library = GetSideBarValue("Library");
+        public static string SideBar_New = GetSideBarValue("New");
+        public static string SideBar_SendTo = GetSideBarValue("SendTo");
+        public static string SideBar_OpenWith = GetSideBarValue("OpenWith");
+        public static string SideBar_WinX = GetSideBarValue("WinX");
+
+        public static string SideBar_LnkFile = GetSideBarValue("LnkFile");
+        public static string SideBar_ExeFile = GetSideBarValue("ExeFile");
+        public static string SideBar_TextFile = GetSideBarValue("TextFile");
+        public static string SideBar_ImageFile = GetSideBarValue("ImageFile");
+        public static string SideBar_VideoFile = GetSideBarValue("VideoFile");
+        public static string SideBar_AudioFile = GetSideBarValue("AudioFile");
+        public static string SideBar_ImageDirectory = GetSideBarValue("ImageDirectory");
+        public static string SideBar_VideoDirectory = GetSideBarValue("VideoDirectory");
+        public static string SideBar_AudioDirectory = GetSideBarValue("AudioDirectory");
+        public static string SideBar_UnknownType = GetSideBarValue("UnknownType");
+        public static string SideBar_CustomType = GetSideBarValue("CustomType");
+
+        public static string SideBar_GuidBlocked = GetSideBarValue("GuidBlocked");
+        public static string SideBar_ThirdRules = GetSideBarValue("ThirdRules");
+
+        public static string SideBar_AboutApp = GetSideBarValue("AboutApp");
+        public static string SideBar_Dictionaries = GetSideBarValue("Dictionaries");
+        public static string SideBar_AppLanguage = GetSideBarValue("AppLanguage");
+        public static string SideBar_Donate = GetSideBarValue("Donate");
+        #endregion
+        #region 状态栏
+        public static string StatusBar_File = GetStatusBarValue("File");
+        public static string StatusBar_Folder = GetStatusBarValue("Folder");
+        public static string StatusBar_Directory = GetStatusBarValue("Directory");
+        public static string StatusBar_Background = GetStatusBarValue("Background");
+        public static string StatusBar_Desktop = GetStatusBarValue("Desktop");
+        public static string StatusBar_Drive = GetStatusBarValue("Drive");
+        public static string StatusBar_AllObjects = GetStatusBarValue("AllObjects");
+        public static string StatusBar_Computer = GetStatusBarValue("Computer");
+        public static string StatusBar_RecycleBin = GetStatusBarValue("RecycleBin");
+        public static string StatusBar_Library = GetStatusBarValue("Library");
+        public static string StatusBar_New = GetStatusBarValue("New");
+        public static string StatusBar_SendTo = GetStatusBarValue("SendTo");
+        public static string StatusBar_OpenWith = GetStatusBarValue("OpenWith");
+        public static string StatusBar_WinX = GetStatusBarValue("WinX");
+
+        public static string StatusBar_LnkFile = GetStatusBarValue("LnkFile");
+        public static string StatusBar_ExeFile = GetStatusBarValue("ExeFile");
+        public static string StatusBar_TextFile = GetStatusBarValue("TextFile");
+        public static string StatusBar_ImageFile = GetStatusBarValue("ImageFile");
+        public static string StatusBar_VideoFile = GetStatusBarValue("VideoFile");
+        public static string StatusBar_AudioFile = GetStatusBarValue("AudioFile");
+        public static string StatusBar_ImageDirectory = GetStatusBarValue("ImageDirectory");
+        public static string StatusBar_VideoDirectory = GetStatusBarValue("VideoDirectory");
+        public static string StatusBar_AudioDirectory = GetStatusBarValue("AudioDirectory");
+        public static string StatusBar_UnknownType = GetStatusBarValue("UnknownType");
+        public static string StatusBar_CustomType = GetStatusBarValue("CustomType");
+
+        public static string StatusBar_GuidBlocked = GetStatusBarValue("GuidBlocked");
+        public static string StatusBar_ThirdRules = GetStatusBarValue("ThirdRules");
+        #endregion
+        #region 菜单
+        public static string Menu_ChangeText = GetMenuValue("ChangeText");
+        public static string Menu_ItemIcon = GetMenuValue("ItemIcon");
+        public static string Menu_ChangeIcon = GetMenuValue("ChangeIcon");
+        public static string Menu_AddIcon = GetMenuValue("AddIcon");
+        public static string Menu_DeleteIcon = GetMenuValue("DeleteIcon");
+        public static string Menu_ItemPosition = GetMenuValue("ItemPosition");
+        public static string Menu_SetDefault = GetMenuValue("SetDefault");
+        public static string Menu_SetTop = GetMenuValue("SetTop");
+        public static string Menu_SetBottom = GetMenuValue("SetBottom");
+        public static string Menu_OtherAttributes = GetMenuValue("OtherAttributes");
+        public static string Menu_OnlyWithShift = GetMenuValue("OnlyWithShift");
+        public static string Menu_OnlyInExplorer = GetMenuValue("OnlyInExplorer");
+        public static string Menu_NoWorkingDirectory = GetMenuValue("NoWorkingDirectory");
+        public static string Menu_ShowSeparator = GetMenuValue("ShowSeparator");
+        public static string Menu_Details = GetMenuValue("Details");
+        public static string Menu_WebSearch = GetMenuValue("WebSearch");
+        public static string Menu_ChangeCommand = GetMenuValue("ChangeCommand");
+        public static string Menu_FileProperties = GetMenuValue("FileProperties");
+        public static string Menu_FileLocation = GetMenuValue("FileLocation");
+        public static string Menu_RegistryLocation = GetMenuValue("RegistryLocation");
+        public static string Menu_Delete = GetMenuValue("Delete");
+        public static string Menu_DeleteReference = GetMenuValue("DeleteReference");
+        public static string Menu_CopyGuid = GetMenuValue("CopyGuid");
+        public static string Menu_InitialData = GetMenuValue("InitialData");
+        #endregion
+        #region 消息框
+        public static string MessageBox_TextCannotBeEmpty = GetMessageBoxValue("TextCannotBeEmpty");
+        public static string MessageBox_CommandCannotBeEmpty = GetMessageBoxValue("CommandCannotBeEmpty");
+        public static string MessageBox_StringParsingFailed = GetMessageBoxValue("StringParsingFailed");
+        public static string MessageBox_TextLengthCannotExceed80 = GetMessageBoxValue("TextLengthCannotExceed80");
+        public static string MessageBox_ConfirmDeletePermanently = GetMessageBoxValue("ConfirmDeletePermanently");
+        public static string MessageBox_ConfirmDeleteReference = GetMessageBoxValue("ConfirmDeleteReference");
+        public static string MessageBox_ConfirmDelete = GetMessageBoxValue("ConfirmDelete");
+        public static string MessageBox_ConfirmDeleteReferenced = GetMessageBoxValue("ConfirmDeleteReferenced");
+        public static string MessageBox_CannotAddNewItem = GetMessageBoxValue("CannotAddNewItem");
+        public static string MessageBox_UnsupportedFilename = GetMessageBoxValue("UnsupportedFilename");
+        public static string MessageBox_UnsupportedExtension = GetMessageBoxValue("UnsupportedExtension");
+        public static string MessageBox_CannotChangePath = GetMessageBoxValue("CannotChangePath");
+        public static string MessageBox_CopiedToClipboard = GetMessageBoxValue("CopiedToClipboard");
+        public static string MessageBox_UnknownGuid = GetMessageBoxValue("UnknownGuid");
+        public static string MessageBox_HasBeenAdded = GetMessageBoxValue("HasBeenAdded");
+        public static string MessageBox_EditInitialData = GetMessageBoxValue("EditInitialData");
+        public static string MessageBox_PromptIsOpenItem = GetMessageBoxValue("PromptIsOpenItem");
+        public static string MessageBox_RestartApp = GetMessageBoxValue("RestartApp");
+        public static string MessageBox_UpdateApp = GetMessageBoxValue("UpdateApp");
+        #endregion
+        #region 其他文本
+        public static string Text_ItemName = GetTextValue("ItemName");
+        public static string Text_ItemCommand = GetTextValue("ItemCommand");
+
+        public static string Text_Single = GetTextValue("Single");
+        public static string Text_Multi = GetTextValue("Multi");
+
+        public static string Text_EditSubItems = GetTextValue("EditSubItems");
+        public static string Text_Separator = GetTextValue("Separator");
+        public static string Text_InvalidItem = GetTextValue("InvalidItem");
+        public static string Text_CheckReference = GetTextValue("CheckReference");
+        public static string Text_CheckCommon = GetTextValue("CheckCommon");
+        public static string Text_InputGuid = GetTextValue("InputGuid");
+
+        public static string Text_Explore = GetTextValue("Explore");
+        public static string Text_CustomFolder = GetTextValue("CustomFolder");
+        public static string Text_BuildSendtoMenu = GetTextValue("BuildSendtoMenu");
+        public static string Text_UseStoreOpenWith = GetTextValue("UseStoreOpenWith");
+        public static string Text_RestartExplorer = GetTextValue("RestartExplorer");
+
+        public static string Text_NewItem = GetTextValue("NewItem");
+        public static string Text_NewShellItem = GetTextValue("NewShellItem");
+        public static string Text_NewSendToItem = GetTextValue("NewSendToItem");
+        public static string Text_NewOpenWithItem = GetTextValue("NewOpenWithItem");
+        public static string Text_NewGuidBlockedItem = GetTextValue("NewGuidBlockedItem");
+
+        public static string Text_SelectExtension = GetTextValue("SelectExtension");
+        public static string Text_CurrentExtension = GetTextValue("CurrentExtension");
+
+        public static string Text_DictionaryDescription = GetTextValue("DictionaryDescription");
+        public static string Text_LanguageDictionary = GetTextValue("LanguageDictionary");
+        public static string Text_GuidInfosDictionary = GetTextValue("GuidInfosDictionary");
+        public static string Text_ThridRulesDictionary = GetTextValue("ThridRulesDictionary");
+        public static string Text_CommonItemsDictionary = GetTextValue("CommonItemsDictionary");
+        public static string Text_Translators = GetTextValue("Translators");
+        public static string Text_OtherLanguages = GetTextValue("OtherLanguages");
+        public static string Text_SelectSubMenuMode = GetTextValue("SelectSubMenuMode");
+        public static string Text_AboutApp = GetTextValue("AboutApp");
+        public static string Text_Dictionaries = GetTextValue("Dictionaries");
+        public static string Text_Donate = GetTextValue("Donate");
+        #endregion
+        #region 提示
+        public static string Tip_RestartExplorer = GetTipValue("RestartExplorer");
+        public static string Tip_CustomFolder = GetTipValue("CustomFolder");
+        public static string Tip_SendToDrive = GetTipValue("SendToDrive");
+        public static string Tip_BuildSendtoMenu = GetTipValue("BuildSendtoMenu");
+        public static string Tip_UseStoreOpenWith = GetTipValue("UseStoreOpenWith");
+        public static string Tip_EditSubItems = GetTipValue("EditSubItems");
+        public static string Tip_InvalidItem = GetTipValue("InvalidItem");
+        public static string Tip_AddSeparator = GetTipValue("AddSeparator");
+        public static string Tip_Separator = GetTipValue("Separator");
+        public static string Tip_AddExistingItems = GetTipValue("AddExistingItems");
+        public static string Tip_AddCommonItems = GetTipValue("AddCommonItems");
+        #endregion
+        #region 国际化字符串
+        /// <summary>确定</summary>
+        public static readonly string Ok = ResourceString.GetDirectString("@shell32.dll,-9752");
+        /// <summary>取消</summary>
+        public static readonly string Cancel = ResourceString.GetDirectString("@shell32.dll,-9751");
+        /// <summary>浏览</summary>
+        public static readonly string Browse = ResourceString.GetDirectString("@shell32.dll,-9015");
+        /// <summary>打开</summary>
+        public static readonly string Open = ResourceString.GetDirectString("@shell32.dll,-12850");
+        /// <summary>编辑</summary>
+        public static readonly string Edit = ResourceString.GetDirectString("@shell32.dll,-37398");
+        /// <summary>打印</summary>
+        public static readonly string Print = ResourceString.GetDirectString("@shell32.dll,-31250");
+        /// <summary>搜索</summary>
+        public static readonly string Find = ResourceString.GetDirectString("@shell32.dll,-9031");
+        /// <summary>播放</summary>
+        public static readonly string Play = ResourceString.GetDirectString("@shell32.dll,-31283");
+        /// <summary>以管理员身份运行</summary>
+        public static readonly string Runas = ResourceString.GetDirectString("@shell32.dll,-37417");
+        /// <summary>保存</summary>
+        public static readonly string Save = ResourceString.GetDirectString("@shell32.dll,-38243");
+        /// <summary>程序</summary>
+        public static readonly string Programs = ResourceString.GetDirectString("@shell32.dll,-21782");
+        /// <summary>可移动磁盘</summary>
+        public static readonly string RemovableDrive = ResourceString.GetDirectString("@shell32.dll,-9309");
+        /// <summary>映射网络驱动器</summary>
+        public static readonly string MapNetworkDrive = ResourceString.GetDirectString("@shell32.dll,-31300");
+        /// <summary>断开网络驱动器的连接</summary>
+        public static readonly string DisconnectNetworkDrive = ResourceString.GetDirectString("@shell32.dll,-31304");
+        /// <summary>回收站属性</summary>
+        public static readonly string RecycleBinProperties = ResourceString.GetDirectString("@shell32.dll,-31338");
+        ///<summary>文件或文件夹不存在</summary>
+        public static readonly string FileOrFolderNotExists = ResourceString.GetDirectString("@shell32.dll,-4132");
+        #endregion
+    }
+}

+ 28 - 0
ContextMenuManager/BulePointLilac.Controls/IconDialog.cs

@@ -0,0 +1,28 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Controls
+{
+    public sealed class IconDialog : CommonDialog
+    {
+        const int MAXLENGTH = 260;//文件名最大长度
+        private int iconIndex;
+        public int IconIndex { get => iconIndex; set => iconIndex = value; }
+        public string IconPath { get; set; }
+
+        protected override bool RunDialog(IntPtr hwndOwner)
+        {
+            StringBuilder sb = new StringBuilder(IconPath, MAXLENGTH);
+            bool flag = PickIconDlg(hwndOwner, sb, MAXLENGTH, ref iconIndex);
+            IconPath = flag ? sb.ToString() : null;
+            return flag;
+        }
+
+        public override void Reset() { }
+
+        [DllImport("shell32.dll", CharSet = CharSet.Unicode, EntryPoint = "#62", SetLastError = true)]
+        private static extern bool PickIconDlg(IntPtr hWnd, StringBuilder pszFileName, int cchFileNameMax, ref int pnIconIndex);
+    }
+}

+ 96 - 0
ContextMenuManager/BulePointLilac.Controls/InputDialog.cs

@@ -0,0 +1,96 @@
+using BulePointLilac.Methods;
+using ContextMenuManager;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Controls
+{
+    public sealed class InputDialog : CommonDialog
+    {
+        /// <summary>输入对话框标题</summary>
+        public string Title { get; set; } = AppString.General_AppName;
+        /// <summary>输入对话框文本框文本</summary>
+        public string Text { get; set; }
+
+        private static Size LastSize = new Size();
+        public override void Reset() { }
+
+        protected override bool RunDialog(IntPtr hwndOwner)
+        {
+            using(InputBox frm = new InputBox { Text = Title, Size = LastSize })
+            {
+                frm.TxtInput.Text = this.Text;
+                bool flag = frm.ShowDialog() == DialogResult.OK;
+                this.Text = flag ? frm.TxtInput.Text : null;
+                LastSize = frm.Size;
+                return flag;
+            }
+        }
+
+        sealed class InputBox : Form
+        {
+            public InputBox()
+            {
+                this.AcceptButton = BtnOk;
+                this.CancelButton = BtnCancel;
+                this.Font = SystemFonts.MessageBoxFont;
+                this.SizeGripStyle = SizeGripStyle.Hide;
+                this.StartPosition = FormStartPosition.CenterParent;
+                this.MinimumSize = this.Size = new Size(400, 150).DpiZoom();
+                this.MaximizeBox = MinimizeBox = ShowIcon = ShowInTaskbar = false;
+                this.Controls.AddRange(new Control[] { TxtInput, BtnOk, BtnCancel });
+                TxtInput.MouseWheel += ResizeFont;
+                InitializeComponents();
+            }
+
+            public readonly TextBox TxtInput = new TextBox
+            {
+                Font = new Font(SystemFonts.MenuFont.FontFamily, 11F),
+                ScrollBars = ScrollBars.Vertical,
+                Multiline = true
+            };
+            readonly Button BtnOk = new Button
+            {
+                Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
+                DialogResult = DialogResult.OK,
+                Text = AppString.Ok,
+                AutoSize = true
+            };
+            readonly Button BtnCancel = new Button
+            {
+                Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
+                DialogResult = DialogResult.Cancel,
+                Text = AppString.Cancel,
+                AutoSize = true
+            };
+
+            protected override void OnResize(EventArgs e)
+            {
+                base.OnResize(e);
+                TxtInput.Width = BtnCancel.Right - TxtInput.Left;
+                TxtInput.Height = BtnCancel.Top - 2 * TxtInput.Top;
+            }
+
+            private void InitializeComponents()
+            {
+                int a = 20.DpiZoom();
+                TxtInput.Location = new Point(a, a);
+                BtnCancel.Top = BtnOk.Top = this.ClientSize.Height - BtnOk.Height - a;
+                BtnCancel.Left = this.ClientSize.Width - BtnCancel.Width - a;
+                BtnOk.Left = BtnCancel.Left - BtnOk.Width - a;
+                this.OnResize(null);
+            }
+
+            /// <summary>缩放文本框字体</summary>
+            private void ResizeFont(object sender, MouseEventArgs e)
+            {
+                if(ModifierKeys != Keys.Control) return;
+                float size = TxtInput.Font.Size;
+                if(size < 8F && e.Delta < 0) return;
+                if(size > 40F && e.Delta > 0) return;
+                TxtInput.Font = new Font(TxtInput.Font.FontFamily, size + (e.Delta > 0 ? 1 : -1));
+            }
+        }
+    }
+}

+ 25 - 0
ContextMenuManager/BulePointLilac.Controls/MyCheckBox.cs

@@ -0,0 +1,25 @@
+using ContextMenuManager;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Controls
+{
+    public class MyCheckBox : PictureBox
+    {
+        private bool _Checked;
+        public bool Checked
+        {
+            get => _Checked;
+            set
+            {
+                _Checked = value;
+                Image = value ? AppImage.TurnOn : AppImage.TurnOff;
+            }
+        }
+
+        public MyCheckBox()
+        {
+            this.Cursor = Cursors.Hand;
+            this.SizeMode = PictureBoxSizeMode.AutoSize;
+        }
+    }
+}

+ 282 - 0
ContextMenuManager/BulePointLilac.Controls/MyListBox.cs

@@ -0,0 +1,282 @@
+using BulePointLilac.Methods;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Controls
+{
+    public class MyListBox : Panel
+    {
+        public MyListBox()
+        {
+            this.AutoScroll = true;
+            this.BackColor = Color.FromArgb(250, 250, 250);
+        }
+
+        protected override void OnMouseWheel(MouseEventArgs e)
+        {
+            //使滚动幅度与MyListItem的高度相配合,防止滚动过快导致来不及重绘界面变花
+            base.OnMouseWheel(new MouseEventArgs(e.Button, e.Clicks, e.X, e.Y, Math.Sign(e.Delta) * 50.DpiZoom()));
+        }
+    }
+
+    public class MyList : FlowLayoutPanel
+    {
+
+        public MyListBox Owner
+        {
+            get => (MyListBox)this.Parent;
+            set => this.Parent = value;
+        }
+
+        public MyList(MyListBox owner) : this()
+        {
+            this.Owner = owner;
+        }
+
+        public MyList()
+        {
+            this.AutoSize = true;
+            this.Dock = DockStyle.Top;
+            this.DoubleBuffered = true;
+            this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
+        }
+
+        private MyListItem hoveredItem;
+        public MyListItem HoveredItem
+        {
+            get => hoveredItem;
+            set
+            {
+                if(hoveredItem == value) return;
+                if(hoveredItem != null)
+                {
+                    hoveredItem.ForeColor = Color.FromArgb(90, 90, 90);
+                    //hoveredItem.BackColor = Color.FromArgb(250, 250, 250);
+                }
+                hoveredItem = value;
+                value.ForeColor = Color.FromArgb(0, 138, 217);
+                //value.BackColor = Color.FromArgb(200, 230, 250);
+                value.Focus();
+            }
+        }
+
+        public void AddItem(MyListItem item)
+        {
+            item.Parent = this;
+            item.Width = Owner.Width - item.Margin.Horizontal;
+            Owner.Resize += (sender, e) => item.Width = Owner.Width - item.Margin.Horizontal;
+            this.MouseWheel += (sender, e) => item.ContextMenuStrip?.Close();
+            item.MouseEnter += (sender, e) => HoveredItem = item;
+        }
+
+        public void AddItems(MyListItem[] items)
+        {
+            Array.ForEach(items, item => AddItem(item));
+        }
+
+        public void AddItems(List<MyListItem> items)
+        {
+            items.ForEach(item => AddItem(item));
+        }
+
+        public void SetItemIndex(MyListItem item, int newIndex)
+        {
+            this.Controls.SetChildIndex(item, newIndex);
+        }
+
+        public int GetItemIndex(MyListItem item)
+        {
+            return this.Controls.GetChildIndex(item);
+        }
+
+        public void InsertItem(MyListItem item, int index)
+        {
+            if(item == null) return;
+            this.AddItem(item);
+            this.SetItemIndex(item, index);
+        }
+
+        public void ClearItems()
+        {
+            foreach(Control control in Controls) control.Dispose();
+            this.Controls.Clear();
+        }
+
+        public void SortItemByText()
+        {
+            List<MyListItem> items = new List<MyListItem>();
+            foreach(MyListItem item in this.Controls) items.Add(item);
+            this.Controls.Clear();
+            items.Sort(new TextComparer());
+            items.ForEach(item => this.AddItem(item));
+        }
+
+        public class TextComparer : IComparer<MyListItem>
+        {
+            public int Compare(MyListItem x, MyListItem y)
+            {
+                string[] strs = { x.Text, y.Text };
+                Array.Sort(strs);
+                if(strs[0] == x.Text) return -1;
+                else return 1;
+            }
+        }
+    }
+
+    public class MyListItem : Panel
+    {
+        public MyListItem()
+        {
+            this.HasImage = true;
+            this.DoubleBuffered = true;
+            this.Height = 50.DpiZoom();
+            this.Margin = new Padding(0);
+            this.ForeColor = Color.FromArgb(80, 80, 80);
+            this.BackColor = Color.FromArgb(250, 250, 250);
+            this.BackgroundImageLayout = ImageLayout.Stretch;
+            this.Font = SystemFonts.IconTitleFont;
+            this.Controls.AddRange(new Control[] { lblSeparator, flpControls, lblText, picImage });
+            flpControls.MouseEnter += (sender, e) => this.OnMouseEnter(null);
+            flpControls.MouseDown += (sender, e) => this.OnMouseEnter(null);
+            flpControls.Left = this.ClientSize.Width;
+            lblText.SetEnabled(false);
+            CenterControl(lblText);
+            CenterControl(picImage);
+        }
+
+        public Image Image
+        {
+            get => picImage.Image;
+            set
+            {
+                picImage.Image = value;
+                picImage.Visible = value != null;
+            }
+        }
+        public new string Text
+        {
+            get => lblText.Text;
+            set => lblText.Text = value;
+        }
+        public new Font Font
+        {
+            get => lblText.Font;
+            set => lblText.Font = value;
+        }
+        public new Color ForeColor
+        {
+            get => lblText.ForeColor;
+            set => lblText.ForeColor = value;
+        }
+
+        private bool hasImage;
+        public bool HasImage
+        {
+            get => hasImage;
+            set
+            {
+                hasImage = value;
+                picImage.Visible = value;
+                lblText.Left = value ? 60.DpiZoom() : 20.DpiZoom();
+            }
+        }
+
+        public int CtrCount { get => flpControls.Controls.Count; }
+
+        private readonly Label lblText = new Label
+        {
+            AutoSize = true
+        };
+        private readonly PictureBox picImage = new PictureBox
+        {
+            SizeMode = PictureBoxSizeMode.AutoSize,
+            Left = 20.DpiZoom(),
+            Enabled = false,
+        };
+        private readonly FlowLayoutPanel flpControls = new FlowLayoutPanel
+        {
+            Anchor = AnchorStyles.Top | AnchorStyles.Right,
+            FlowDirection = FlowDirection.RightToLeft,
+            AutoSizeMode = AutoSizeMode.GrowAndShrink,
+            AutoSize = true,
+            Top = 0
+        };
+        private readonly Label lblSeparator = new Label
+        {
+            BackColor = Color.FromArgb(200, 200, 200),
+            Dock = DockStyle.Bottom,
+            Height = 1
+        };
+
+        protected override void OnMouseDown(MouseEventArgs e)
+        {
+            base.OnMouseDown(e); OnMouseEnter(null);
+        }
+
+        protected override void OnResize(EventArgs e)
+        {
+            base.OnResize(e); flpControls.Height = this.Height;
+        }
+
+        private void CenterControl(Control ctr)
+        {
+            void reSize(Control c)
+            {
+                if(c.Parent == null) return;
+                int top = (c.Parent.Height - c.Height) / 2;
+                if(c.Parent == this) c.Top = top;
+                else if(c.Parent == flpControls)
+                {
+                    c.Margin = new Padding(0, top, c.Margin.Right, top);
+                }
+            }
+            ctr.Parent.Resize += (sender, e) => reSize(ctr);
+            ctr.Resize += (sender, e) => reSize(ctr);
+            reSize(ctr);
+        }
+
+        public void AddCtr(Control ctr)
+        {
+            int space = 20.DpiZoom();
+            //为第一个ctr预留垂直滚动条的宽度
+            if(CtrCount == 0) space += SystemInformation.VerticalScrollBarWidth;
+            AddCtr(ctr, space);
+        }
+
+        public void AddCtr(Control ctr, int space)
+        {
+            flpControls.Controls.Add(ctr);
+            ctr.Margin = new Padding(space);
+            ctr.MouseEnter += (sender, e) => this.OnMouseEnter(null);
+            ctr.MouseDown += (sender, e) => this.OnMouseEnter(null);
+            CenterControl(ctr);
+        }
+
+        public void AddCtrs(Control[] ctrs)
+        {
+            Array.ForEach(ctrs, ctr => AddCtr(ctr));
+        }
+
+        public void RemoveCtr(Control ctr)
+        {
+            if(ctr.Parent == flpControls) flpControls.Controls.Remove(ctr);
+        }
+
+        public void RemoveCtrAt(int index)
+        {
+            if(flpControls.Controls.Count > index) flpControls.Controls.RemoveAt(index);
+        }
+
+        public int GetCtrIndex(Control ctr)
+        {
+            return flpControls.Controls.GetChildIndex(ctr, true);
+        }
+
+        public void SetCtrIndex(Control ctr, int newIndex)
+        {
+            flpControls.Controls.SetChildIndex(ctr, newIndex);
+        }
+    }
+}

+ 36 - 0
ContextMenuManager/BulePointLilac.Controls/MyMainForm.cs

@@ -0,0 +1,36 @@
+using BulePointLilac.Methods;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Controls
+{
+    public class MyMainForm : Form
+    {
+        public MyMainForm()
+        {
+            this.Text = Application.ProductName;
+            this.MinimumSize = this.Size = new Size(866, 649).DpiZoom();
+            this.StartPosition = FormStartPosition.CenterScreen;
+            this.Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath);
+            this.Controls.AddRange(new Control[] { MainBody, SideBar, StatusBar, ToolBar });
+            SideBar.Resize += (sender, e) => MainBody.Width = ClientSize.Width - SideBar.Width;
+            ToolBar.CanMoveForm();
+            StatusBar.CanMoveForm();
+        }
+
+        protected MyToolBar ToolBar = new MyToolBar();
+        protected MySideBar SideBar = new MySideBar();
+        protected MyStatusBar StatusBar = new MyStatusBar();
+        protected MyListBox MainBody = new MyListBox
+        {
+            Dock = DockStyle.Left
+        };
+
+        protected override void OnResize(EventArgs e)
+        {
+            base.OnResize(e);
+            MainBody.Width = ClientSize.Width - SideBar.Width;
+        }
+    }
+}

+ 201 - 0
ContextMenuManager/BulePointLilac.Controls/MySideBar.cs

@@ -0,0 +1,201 @@
+using BulePointLilac.Methods;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Controls
+{
+    public class MySideBar : Panel
+    {
+        public MySideBar()
+        {
+            this.Dock = DockStyle.Left;
+            this.ItemHeight = 30.DpiZoom();
+            this.Font = new Font(SystemFonts.MenuFont.FontFamily, 10F);
+            this.ForeColor = Color.FromArgb(50, 50, 50);
+            this.BackColor = Color.FromArgb(245, 245, 245);
+            this.BackgroundImageLayout = ImageLayout.None;
+            this.Controls.AddRange(new Control[] { LblSeparator, PnlSelected, PnlHovered });
+            PnlHovered.Paint += PaintItem;
+            PnlSelected.Paint += PaintItem;
+            this.SelectIndex = -1;
+        }
+
+        private string[] itemNames;
+        public string[] ItemNames
+        {
+            get => itemNames;
+            set
+            {
+                itemNames = value;
+                if(value != null)
+                {
+                    int maxWidth = 0;
+                    Array.ForEach(value, item => maxWidth = Math.Max(maxWidth, TextRenderer.MeasureText(item, Font).Width));
+                    PnlHovered.Width = PnlSelected.Width = Width = maxWidth + 2 * HorizontalSpace;
+                }
+                PaintItems();
+                SelectIndex = -1;
+            }
+        }
+
+        private int itemHeight;
+        public int ItemHeight
+        {
+            get => itemHeight;
+            set => PnlHovered.Height = PnlSelected.Height = itemHeight = value;
+        }//项上下边缘距离
+        public int TopSpace { get; set; } = 2.DpiZoom();//第一项顶部与上边缘的距离
+        public int HorizontalSpace { get; set; } = 20.DpiZoom();//项文字与项左右边缘距离
+        private float VerticalSpace => (itemHeight - TextHeight) * 0.5F;//项文字与项上下边缘距离
+        private int TextHeight => TextRenderer.MeasureText(" ", Font).Height;//项文字高度
+
+        public Color SeparatorColor
+        {
+            get => LblSeparator.BackColor;
+            set => LblSeparator.BackColor = value;
+        }//分隔线颜色
+        public Color SelectedBackColor
+        {
+            get => PnlSelected.BackColor;
+            set => PnlSelected.BackColor = value;
+        }
+        public Color HoveredBackColor
+        {
+            get => PnlHovered.BackColor;
+            set => PnlHovered.BackColor = value;
+        }
+        public Color SelectedForeColor
+        {
+            get => PnlSelected.ForeColor;
+            set => PnlSelected.ForeColor = value;
+        }
+        public Color HoveredForeColor
+        {
+            get => PnlHovered.ForeColor;
+            set => PnlHovered.ForeColor = value;
+        }
+
+        readonly Panel PnlSelected = new Panel
+        {
+            BackColor = Color.FromArgb(40, 140, 210),
+            ForeColor = Color.White,
+            Enabled = false
+        };
+        readonly Panel PnlHovered = new Panel
+        {
+            BackColor = Color.FromArgb(80, 180, 250),
+            ForeColor = Color.White,
+            Enabled = false
+        };
+        readonly Label LblSeparator = new Label
+        {
+            BackColor = Color.FromArgb(200, 200, 200),
+            Dock = DockStyle.Right,
+            Width = 1,
+        };
+
+        private void PaintItems()
+        {
+            this.BackgroundImage = new Bitmap(Width, Height);
+            using(Graphics g = Graphics.FromImage(BackgroundImage))
+            {
+                g.Clear(BackColor);
+                if(itemNames == null) return;
+                for(int i = 0; i < itemNames.Length; i++)
+                {
+                    if(itemNames[i] != null)
+                    {
+                        g.DrawString(itemNames[i], Font, new SolidBrush(ForeColor),
+                            new PointF(HorizontalSpace, TopSpace + i * ItemHeight + VerticalSpace));
+                    }
+                    else
+                    {
+                        g.DrawLine(new Pen(SeparatorColor),
+                            new PointF(HorizontalSpace, TopSpace + (i + 0.5F) * ItemHeight),
+                            new PointF(Width - HorizontalSpace, TopSpace + (i + 0.5F) * ItemHeight)
+                        );
+                    }
+                }
+            }
+        }
+
+        private void RefreshItem(Panel panel, int index)
+        {
+            panel.Top = index < 0 ? -ItemHeight : (TopSpace + index * ItemHeight);
+            panel.Text = index < 0 ? null : ItemNames[index];
+            panel.Refresh();
+        }
+
+        private void PaintItem(object sender, PaintEventArgs e)
+        {
+            Control ctr = (Control)sender;
+            e.Graphics.Clear(ctr.BackColor);
+            e.Graphics.DrawString(ctr.Text, Font,
+                new SolidBrush(ctr.ForeColor), new PointF(HorizontalSpace, VerticalSpace));
+        }
+
+        private void ShowItem(Panel panel, MouseEventArgs e)
+        {
+            if(itemNames == null) return;
+            int index = (e.Y - TopSpace) / ItemHeight;
+            if(index >= itemNames.Length || string.IsNullOrEmpty(itemNames[index]) || index == SelectIndex)
+            {
+                this.Cursor = Cursors.Default;
+                HoverIndex = SelectIndex;
+            }
+            else
+            {
+                this.Cursor = Cursors.Hand;
+                if(panel == PnlSelected) SelectIndex = index;
+                else HoverIndex = index;
+            }
+        }
+
+        protected override void OnMouseMove(MouseEventArgs e)
+        {
+            base.OnMouseMove(e);
+            ShowItem(PnlHovered, e);
+        }
+        protected override void OnMouseDown(MouseEventArgs e)
+        {
+            base.OnMouseDown(e);
+            if(e.Button == MouseButtons.Left) ShowItem(PnlSelected, e);
+        }
+        protected override void OnMouseLeave(EventArgs e)
+        {
+            base.OnMouseLeave(e);
+            HoverIndex = SelectIndex;
+        }
+
+        public event EventHandler SelectIndexChanged;
+
+        public event EventHandler HoverIndexChanged;
+
+        private int selectIndex;
+        public int SelectIndex
+        {
+            get => selectIndex;
+            set
+            {
+                HoverIndex = value;
+                RefreshItem(PnlSelected, value);
+                selectIndex = value;
+                SelectIndexChanged?.Invoke(null, null);
+            }
+        }
+
+        private int hoverIndex;
+        public int HoverIndex
+        {
+            get => hoverIndex;
+            set
+            {
+                if(hoverIndex == value) return;
+                RefreshItem(PnlHovered, value);
+                hoverIndex = value;
+                HoverIndexChanged?.Invoke(null, null);
+            }
+        }
+    }
+}

+ 46 - 0
ContextMenuManager/BulePointLilac.Controls/MyStatusBar.cs

@@ -0,0 +1,46 @@
+using BulePointLilac.Methods;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Controls
+{
+    public class MyStatusBar : Panel
+    {
+        public static readonly string DefaultText = $"Ver: {new Version(Application.ProductVersion).ToString(2)}    {Application.CompanyName}";
+
+        public MyStatusBar()
+        {
+            this.Height = 30.DpiZoom();
+            this.Dock = DockStyle.Bottom;
+            this.Font = SystemFonts.StatusFont;
+            this.BackColor = Color.FromArgb(70, 130, 200);
+            this.ForeColor = Color.White;
+        }
+
+        public new string Text
+        {
+            get => base.Text;
+            set { base.Text = value; Refresh(); }
+        }
+        public new Font Font
+        {
+            get => base.Font;
+            set { base.Font = value; Refresh(); }
+        }
+        public new Color ForeColor
+        {
+            get => base.ForeColor;
+            set { base.ForeColor = value; Refresh(); }
+        }
+
+        protected override void OnPaint(PaintEventArgs e)
+        {
+            base.OnPaint(e);
+            int left = this.Height / 3;
+            int top = (this.Height - TextRenderer.MeasureText(this.Text, this.Font).Height) / 2;
+            e.Graphics.Clear(this.BackColor);
+            e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(this.ForeColor), left, top);
+        }
+    }
+}

+ 111 - 0
ContextMenuManager/BulePointLilac.Controls/MyToolBar.cs

@@ -0,0 +1,111 @@
+using BulePointLilac.Methods;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Controls
+{
+    public class MyToolBar : FlowLayoutPanel
+    {
+        public MyToolBar()
+        {
+            this.Height = 80.DpiZoom();
+            this.Dock = DockStyle.Top;
+            this.DoubleBuffered = true;
+            this.BackColor = Color.FromArgb(85, 145, 215);
+        }
+
+        private MyToolBarButton selectedButton;
+        public MyToolBarButton SelectedButton
+        {
+            get => selectedButton;
+            set
+            {
+                if(selectedButton == value) return;
+                if(selectedButton != null) selectedButton.Opacity = 0;
+                selectedButton = value;
+                value.Opacity = 0.4F;
+                SelectedButtonChanged?.Invoke(null, null);
+            }
+        }
+
+        public event EventHandler SelectedButtonChanged;
+
+        public int SelectedIndex
+        {
+            get => Controls.GetChildIndex(SelectedButton);
+            set => SelectedButton = (MyToolBarButton)Controls[value];
+        }
+
+        public void AddButton(MyToolBarButton button)
+        {
+            button.Parent = this;
+            button.Margin = new Padding(12.DpiZoom(), 4.DpiZoom(), 0, 0);
+            button.MouseDown += (sender, e) =>
+            {
+                if(e.Button == MouseButtons.Left) SelectedButton = button;
+            };
+            button.MouseEnter += (sender, e) =>
+            {
+                if(button != SelectedButton) button.Opacity = 0.2F;
+            };
+            button.MouseLeave += (sender, e) =>
+            {
+                if(button != SelectedButton) button.Opacity = 0;
+            };
+        }
+
+        public void AddButtons(MyToolBarButton[] buttons)
+        {
+            Array.ForEach(buttons, button => AddButton(button));
+        }
+    }
+
+    public class MyToolBarButton : Panel
+    {
+        public MyToolBarButton(Image image, string text)
+        {
+            this.DoubleBuffered = true;
+            this.Size = new Size(72, 72).DpiZoom();
+            this.Controls.AddRange(new Control[] { picImage, lblText });
+            picImage.Location = new Point(16, 6).DpiZoom();
+            lblText.Top = 52.DpiZoom();
+            lblText.Resize += (sender, e) => lblText.Left = (Width - lblText.Width) / 2;
+            lblText.SetEnabled(false);
+            this.Image = image;
+            this.Text = text;
+        }
+
+        readonly PictureBox picImage = new PictureBox
+        {
+            SizeMode = PictureBoxSizeMode.StretchImage,
+            Size = new Size(40, 40).DpiZoom(),
+            BackColor = Color.Transparent,
+            Enabled = false
+        };
+
+        readonly Label lblText = new Label
+        {
+            AutoSize = true,
+            Font = SystemFonts.MenuFont,
+            BackColor = Color.Transparent,
+            ForeColor = Color.White
+        };
+
+        public Image Image
+        {
+            get => picImage.Image;
+            set => picImage.Image = value;
+        }
+        public new string Text
+        {
+            get => lblText.Text;
+            set => lblText.Text = value;
+        }
+        public float Opacity
+        {
+            get => BackColor.A / 255;
+            set => BackColor = Color.FromArgb((int)(value * 255), Color.White);
+        }
+    }
+}

+ 68 - 0
ContextMenuManager/BulePointLilac.Controls/MyToolTip.cs

@@ -0,0 +1,68 @@
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Controls
+{
+    public sealed class MyToolTip : Form
+    {
+        public static readonly MyToolTip Instance = new MyToolTip();
+
+        public static void SetToolTip(Control ctr, string tip)
+        {
+            if(string.IsNullOrWhiteSpace(tip)) return;
+            ctr.MouseEnter += (sender, e) =>
+            {
+                Instance.Text = tip;
+                Instance.Size = Instance.LblTip.Size;
+                Instance.Location = ctr.PointToScreen(new Point(ctr.Width + 10, (ctr.Height - Instance.Height) / 2));
+                Instance.Visible = true;
+                ctr.FindForm().Activate();
+            };
+            ctr.MouseLeave += (sender, e) => Instance.Visible = false;
+        }
+
+        static MyToolTip()
+        {
+            Instance.Opacity = 0;
+            Instance.Show();
+            Instance.Visible = false;
+            Instance.Opacity = 1;
+        }
+        private MyToolTip()
+        {
+            this.TopMost = true;
+            this.BackColor = Color.FromArgb(235, 245, 255);
+            this.ForeColor = Color.FromArgb(80, 80, 80);
+            this.ShowIcon = this.ShowInTaskbar = false;
+            this.FormBorderStyle = FormBorderStyle.None;
+            this.Controls.Add(LblTip);
+        }
+        public new string Text
+        {
+            get => LblTip.Text;
+            set => LblTip.Text = value;
+        }
+        public new Font Font
+        {
+            get => LblTip.Font;
+            set => LblTip.Font = value;
+        }
+        public new Color ForeColor
+        {
+            get => LblTip.ForeColor;
+            set => LblTip.ForeColor = value;
+        }
+        public BorderStyle BorderStyle
+        {
+            get => LblTip.BorderStyle;
+            set => LblTip.BorderStyle = value;
+        }
+
+        readonly Label LblTip = new Label
+        {
+            AutoSize = true,
+            Font = SystemFonts.MenuFont,
+            BorderStyle = BorderStyle.FixedSingle,
+        };
+    }
+}

+ 41 - 0
ContextMenuManager/BulePointLilac.Controls/PictureButton.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Controls
+{
+    public class PictureButton : PictureBox
+    {
+        public PictureButton(Image image)
+        {
+            this.BaseImage = image;
+            this.SizeMode = PictureBoxSizeMode.AutoSize;
+            this.Cursor = Cursors.Hand;
+        }
+
+        private Image baseImage;
+        public Image BaseImage
+        {
+            get => baseImage;
+            set
+            {
+                baseImage = value;
+                this.Image = ToolStripRenderer.CreateDisabledImage(value);
+            }
+        }
+
+        protected override void OnMouseEnter(EventArgs e)
+        {
+            base.OnMouseEnter(e); this.Image = BaseImage;
+        }
+        protected override void OnMouseLeave(EventArgs e)
+        {
+            base.OnMouseLeave(e);
+            this.Image = ToolStripRenderer.CreateDisabledImage(BaseImage);
+        }
+        protected override void OnMouseDown(MouseEventArgs e)
+        {
+            if(e.Button == MouseButtons.Left) base.OnMouseDown(e);
+        }
+    }
+}

+ 74 - 0
ContextMenuManager/BulePointLilac.Controls/ResizbleForm.cs

@@ -0,0 +1,74 @@
+using System;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Controls
+{
+    /// <summary>限制水平、竖直方向调整大小的窗体</summary>
+    class ResizbleForm : Form
+    {
+        /// <summary>水平方向可调整大小</summary>
+        public bool HorizontalResizable { get; set; } = true;
+
+        /// <summary>竖直方向可调整大小</summary>
+        public bool VerticalResizable { get; set; } = true;
+
+        protected override void WndProc(ref Message m)
+        {
+            base.WndProc(ref m);
+            switch(m.Msg)
+            {
+                case WM_NCHITTEST:
+                    IntPtr hNowhere = new IntPtr((int)HitTest.Nowhere);
+                    HitTest value = (HitTest)m.Result;
+                    switch(value)
+                    {
+                        case HitTest.Top:
+                        case HitTest.Bottom:
+                            if(!VerticalResizable) m.Result = hNowhere;
+                            break;
+                        case HitTest.Left:
+                        case HitTest.Right:
+                            if(!HorizontalResizable) m.Result = hNowhere;
+                            break;
+                        case HitTest.TopLeft:
+                        case HitTest.TopRight:
+                        case HitTest.BottomLeft:
+                        case HitTest.BottomRight:
+                            if(!VerticalResizable || !HorizontalResizable) m.Result = hNowhere;
+                            break;
+                    }
+                    break;
+            }
+        }
+
+        /// <summary>光标移动或鼠标按下、释放时的消息</summary>
+        private const int WM_NCHITTEST = 0x84;
+        /// <summary>鼠标击中位置</summary>
+        private enum HitTest : int
+        {
+            Error = -2,
+            Transparent = -1,
+            Nowhere = 0,
+            Client = 1,
+            TitleBar = 2,
+            SysMenu = 3,
+            Size = 4,
+            GrowBox = 5,
+            Hscroll = 6,
+            Vscroll = 7,
+            MinButton = 8,
+            MaxButton = 9,
+            Left = 10,
+            Right = 11,
+            Top = 12,
+            TopLeft = 13,
+            TopRight = 14,
+            Bottom = 15,
+            BottomLeft = 16,
+            BottomRight = 17,
+            Border = 18,
+            Close = 20,
+            Help = 21
+        }
+    }
+}

+ 48 - 0
ContextMenuManager/BulePointLilac.Methods/ControlExtension.cs

@@ -0,0 +1,48 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Methods
+{
+    public static class ControlExtension
+    {
+        [DllImport("user32.dll")]
+        private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+        [DllImport("user32.dll")]
+        private static extern bool ReleaseCapture();
+        private const int WM_NCLBUTTONDOWN = 0xA1;
+        private const int HT_CAPTION = 0x2;
+
+        /// <summary>使控件能够移动所属窗体</summary>
+        /// <remarks>副作用:使用此方法将无法触发Click等事件</remarks>
+        /// <param name="ctr">目标控件</param>
+        public static void CanMoveForm(this Control ctr)
+        {
+            ctr.MouseDown += (sender, e) =>
+            {
+                if(e.Button == MouseButtons.Left && ctr.FindForm().WindowState == FormWindowState.Normal)
+                {
+                    ReleaseCapture();
+                    SendMessage(ctr.FindForm().Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
+                }
+            };
+        }
+
+        [DllImport("user32.dll")]
+        private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int wndproc);
+        [DllImport("user32.dll")]
+        private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
+        private const int GWL_STYLE = -16;
+        private const int WS_DISABLED = 0x8000000;
+
+        /// <summary>通过Win32API禁用/启用目标控件</summary>
+        /// <remarks>控件被禁用时仍可更改字体颜色,不需要同时设置ctr.Enabled=false</remarks>
+        /// <param name="ctr">目标控件</param>
+        /// <param name="enabled">启用为true,禁用为false</param>
+        public static void SetEnabled(this Control ctr, bool enabled)
+        {
+            if(enabled) { SetWindowLong(ctr.Handle, GWL_STYLE, (~WS_DISABLED) & GetWindowLong(ctr.Handle, GWL_STYLE)); }
+            else { SetWindowLong(ctr.Handle, GWL_STYLE, WS_DISABLED | GetWindowLong(ctr.Handle, GWL_STYLE)); }
+        }
+    }
+}

+ 72 - 0
ContextMenuManager/BulePointLilac.Methods/EncodingType.cs

@@ -0,0 +1,72 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace BulePointLilac.Methods
+{
+    /// 获取文本文件编码类型
+    /// 代码主要为转载,仅做简单改动
+    /// 代码作者:Napoléon
+    /// 代码原文:https://www.cnblogs.com/guyun/p/4262587.html
+    public static class EncodingType
+    {
+        /// <summary>给定文件的路径,读取文件的二进制数据,判断文件的编码类型</summary> 
+        /// <param name=“filePath“>文件路径</param> 
+        /// <returns>文件的编码类型</returns> 
+        public static Encoding GetType(string filePath)
+        {
+            using(FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
+                return GetType(fs);
+        }
+
+        static readonly byte[] Unicode = { 0xFF, 0xFE, 0x41 };
+        static readonly byte[] UnicodeBIG = { 0xFE, 0xFF, 0x00 };
+        static readonly byte[] UTF8 = { 0xEF, 0xBB, 0xBF }; //带BOM 
+
+        /// <summary> 通过给定的文件流,判断文件的编码类型</summary>
+        /// <param name=“fs“>文件流</param> 
+        /// <returns>文件的编码类型</returns> 
+        public static Encoding GetType(FileStream fs)
+        {
+            byte[] ss;
+            int.TryParse(fs.Length.ToString(), out int i);
+            using(BinaryReader reader = new BinaryReader(fs, Encoding.Default))
+                ss = reader.ReadBytes(i);
+            byte[] rs = ss.Take(3).ToArray();
+            if((rs == UTF8) || IsUTF8Bytes(ss)) return Encoding.UTF8;
+            else if(rs == UnicodeBIG) return Encoding.BigEndianUnicode;
+            else if(rs == Unicode) return Encoding.Unicode;
+            else return Encoding.Default;
+        }
+
+        /// <summary>判断是否是不带 BOM 的 UTF8 格式</summary> 
+        /// <param name=“data“></param> 
+        private static bool IsUTF8Bytes(byte[] data)
+        {
+            int count = 1; //计算当前正分析的字符应还有的字节数 
+            for(int i = 0; i < data.Length; i++)
+            {
+                byte curByte = data[i];//当前分析的字节. 
+                if(count == 1)
+                {
+                    if(curByte >= 0x80)
+                    {
+                        //判断当前 
+                        while(((curByte <<= 1) & 0x80) != 0) count++;
+                        //标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X 
+                        if(count == 1 || count > 6) return false;
+                    }
+                }
+                else
+                {
+                    //若是UTF-8 此时第一位必须为1 
+                    if((curByte & 0xC0) != 0x80) return false;
+                    else count--;
+                }
+            }
+            if(count > 1) throw new Exception("非预期的byte格式");
+            return true;
+        }
+    }
+}

+ 29 - 0
ContextMenuManager/BulePointLilac.Methods/HighDpi.cs

@@ -0,0 +1,29 @@
+using System.Drawing;
+using System.Windows;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Methods
+{
+    public static class HighDpi
+    {
+        public static readonly double DpiScale = Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth;
+
+        public static Point DpiZoom(this Point point) => new Point(DpiZoom(point.X), DpiZoom(point.Y));
+
+        public static PointF DpiZoom(this PointF point) => new PointF(DpiZoom(point.X), DpiZoom(point.Y));
+
+        public static Size DpiZoom(this Size size) => new Size(DpiZoom(size.Width), DpiZoom(size.Height));
+
+        public static SizeF DpiZoom(this SizeF size) => new SizeF(DpiZoom(size.Width), DpiZoom(size.Height));
+
+        public static Rectangle DpiZoom(this Rectangle r) => new Rectangle(DpiZoom(r.Location), DpiZoom(r.Size));
+
+        public static RectangleF DpiZoom(this RectangleF r) => new RectangleF(DpiZoom(r.Location), DpiZoom(r.Size));
+
+        public static int DpiZoom(this int num) => (int)(num * DpiScale);
+
+        public static float DpiZoom(this float num) => (float)(num * DpiScale);
+
+        public static double DpiZoom(this double num) => num * DpiScale;
+    }
+}

+ 64 - 0
ContextMenuManager/BulePointLilac.Methods/ImageExtension.cs

@@ -0,0 +1,64 @@
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+
+namespace BulePointLilac.Methods
+{
+    public static class ImageExtension
+    {
+        public static Image ToTransparent(this Image image, float opacity = 0.5F)
+        {
+            Bitmap bitmap = new Bitmap(image.Width, image.Height);
+            using(Graphics g = Graphics.FromImage(bitmap))
+            using(ImageAttributes attributes = new ImageAttributes())
+            {
+                ColorMatrix matrix = new ColorMatrix { Matrix33 = opacity };
+                attributes.SetColorMatrix(matrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
+                g.DrawImage(image, new Rectangle(0, 0, bitmap.Width, bitmap.Height),
+                    0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
+            }
+            return bitmap;
+        }
+
+        public static Image ResizeImage(this Image image, int width, int height)
+        {
+            Bitmap destImage = new Bitmap(width, height);
+            destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
+            using(Graphics g = Graphics.FromImage(destImage))
+            {
+                g.CompositingMode = CompositingMode.SourceCopy;
+                g.CompositingQuality = CompositingQuality.HighQuality;
+                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
+                g.SmoothingMode = SmoothingMode.HighQuality;
+                g.PixelOffsetMode = PixelOffsetMode.HighQuality;
+
+                using(ImageAttributes attributes = new ImageAttributes())
+                {
+                    attributes.SetWrapMode(WrapMode.TileFlipXY);
+                    g.DrawImage(image, new Rectangle(0, 0, width, height),
+                        0, 0, image.Width, image.Height, GraphicsUnit.Pixel, attributes);
+                }
+            }
+            return destImage;
+        }
+
+        public static Image ResizeImage(this Image image, double scale)
+        {
+            int width = (int)(image.Width * scale);
+            int height = (int)(image.Height * scale);
+            return image.ResizeImage(width, height);
+        }
+
+        public static Image ResizeImage(this Image image, Size newSize)
+        {
+            return image.ResizeImage(newSize.Width, newSize.Height);
+        }
+
+        public static Image RotateImage(this Image image, RotateFlipType rotateType)
+        {
+            Bitmap bitmap = new Bitmap(image);
+            bitmap.RotateFlip(rotateType);
+            return bitmap;
+        }
+    }
+}

+ 78 - 0
ContextMenuManager/BulePointLilac.Methods/IniFileHelper.cs

@@ -0,0 +1,78 @@
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace BulePointLilac.Methods
+{
+    public class IniFileHelper
+    {
+        [DllImport("kernel32", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = false, ThrowOnUnmappableChar = true)]
+        private static extern bool WritePrivateProfileString(string section, string key, string value, string filePath);
+
+        [DllImport("kernel32", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = false, ThrowOnUnmappableChar = true)]
+        private static extern int GetPrivateProfileString(string section, string key, string defVal, StringBuilder retVal, int size, string filePath);
+
+        public IniFileHelper(string filePath) { this.FilePath = filePath; }
+
+        public string FilePath { get; private set; }
+
+        public string GetValue(string section, string key)
+        {
+            StringBuilder retVal = new StringBuilder(255);
+            GetPrivateProfileString(section, key, "", retVal, 255, this.FilePath);
+            return retVal.ToString();
+        }
+
+        public void SetValue(string section, string key, string value)
+        {
+            WritePrivateProfileString(section, key, value, this.FilePath);
+        }
+
+        public void DeleteKey(string section, string key)
+        {
+            SetValue(section, key, null);
+        }
+
+        public void DeleteSection(string section)
+        {
+            SetValue(section, null, null);
+        }
+
+        public bool KeyExists(string section, string key)
+        {
+            return GetValue(section, key) != string.Empty;
+        }
+
+        public bool TryGetValue(string section, string key, out string value)
+        {
+            value = GetValue(section, key);
+            return value != string.Empty;
+        }
+    }
+
+    public static class DesktopIniHelper
+    {
+        public const string LocalizedFileNames = "LocalizedFileNames";
+
+        public static void SetLocalizedFileName(string filePath, string name)
+        {
+            string fileName = Path.GetFileName(filePath);
+            string iniPath = $@"{Path.GetDirectoryName(filePath)}\desktop.ini";
+            IniFileHelper helper = new IniFileHelper(iniPath);
+            helper.SetValue(LocalizedFileNames, fileName, name);
+        }
+
+        public static string GetLocalizedFileName(string filePath)
+        {
+            string fileName = Path.GetFileName(filePath);
+            string iniPath = $@"{Path.GetDirectoryName(filePath)}\desktop.ini";
+            IniFileHelper helper = new IniFileHelper(iniPath);
+            return helper.GetValue(LocalizedFileNames, fileName);
+        }
+
+        public static void DeleteLocalizedFileName(string filePath)
+        {
+            SetLocalizedFileName(filePath, null);
+        }
+    }
+}

+ 85 - 0
ContextMenuManager/BulePointLilac.Methods/IniReader.cs

@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace BulePointLilac.Methods
+{
+    public class IniReader
+    {
+        public IniReader(StringBuilder sb)
+        {
+            if(string.IsNullOrWhiteSpace(sb.ToString())) return;
+            List<string> lines = sb.ToString().Split(new[] { Environment.NewLine },
+                StringSplitOptions.RemoveEmptyEntries).ToList();//拆分为行
+            lines.ForEach(line => line.Trim());
+            ReadLines(lines);
+        }
+
+        public IniReader(string filePath)
+        {
+            if(!File.Exists(filePath)) return;
+            List<string> lines = new List<string>();
+            using(StreamReader reader = new StreamReader(filePath, EncodingType.GetType(filePath)))
+                while(!reader.EndOfStream)
+                {
+                    string line = reader.ReadLine().Trim();
+                    if(line != string.Empty) lines.Add(line);
+                }
+            ReadLines(lines);
+        }
+
+        protected readonly Dictionary<string, Dictionary<string, string>> rootDic
+            = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
+
+        private void ReadLines(List<string> lines)
+        {
+            lines.RemoveAll(
+                line => line.StartsWith(";")//移除注释
+                || (!line.StartsWith("[") && !line.Contains("=")));//移除非section行且非key行
+
+            if(lines.Count == 0) return;
+
+            List<int> indexs = new List<int> { 0 };
+            for(int i = 1; i < lines.Count; i++)
+            {
+                if(lines[i].StartsWith("[")) indexs.Add(i);//获取section行号
+            }
+            indexs.Add(lines.Count);
+
+            for(int i = 0; i < indexs.Count - 1; i++)
+            {
+                string section = lines[indexs[i]];
+                int m = section.IndexOf(']') - 1;
+                if(m < 0) continue;
+                section = section.Substring(1, m);
+                if(rootDic.ContainsKey(section)) continue;
+                var keyValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+                rootDic.Add(section, keyValues);
+
+                for(int j = indexs[i] + 1; j < indexs[i + 1]; j++)
+                {
+                    int k = lines[j].IndexOf('=');
+                    string key = lines[j].Substring(0, k).TrimEnd();
+                    string value = lines[j].Substring(k + 1).TrimStart();
+                    keyValues.Add(key, value);
+                }
+            }
+        }
+
+        public string GetValue(string section, string key)
+        {
+            if(rootDic.TryGetValue(section, out Dictionary<string, string> sectionDic))
+                if(sectionDic.TryGetValue(key, out string value))
+                    return value;
+            return string.Empty;
+        }
+
+        public bool TryGetValue(string section, string key, out string value)
+        {
+            value = GetValue(section, key);
+            return value != string.Empty;
+        }
+    }
+}

+ 14 - 0
ContextMenuManager/BulePointLilac.Methods/MessageBoxEx.cs

@@ -0,0 +1,14 @@
+using ContextMenuManager;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Methods
+{
+    public static class MessageBoxEx
+    {
+        public static DialogResult Show(string text, MessageBoxButtons buttons = MessageBoxButtons.OK,
+            MessageBoxIcon icon = MessageBoxIcon.Warning, string caption = null)
+        {
+            return MessageBox.Show(text, caption ?? AppString.General_AppName, buttons, icon);
+        }
+    }
+}

+ 147 - 0
ContextMenuManager/BulePointLilac.Methods/ObjectPath.cs

@@ -0,0 +1,147 @@
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+
+namespace BulePointLilac.Methods
+{
+    public class ObjectPath
+    {
+        /// <summary>路径类型</summary>
+        public enum PathType { File, Directory, Registry }
+
+        private static readonly List<string> EnvironmentDirectorys = new List<string> {
+                @"%SystemRoot%\System32",
+                @"%SystemRoot%",
+                @"%SystemRoot%\System32\WindowsPowerShell\v1.0"
+            };
+
+        private const string RegAppPath = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths";
+        private const string RegLastPath = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Applets\Regedit";
+
+        private static readonly char[] IllegalChars = { '/', '*', '?', '\"', '<', '>', '|' };
+        private static readonly List<string> IgnoreCommandParts = new List<string> { "", "%1", "%v" };
+
+        /// <summary>根据文件名获取完整的文件路径</summary>
+        /// <remarks>fileName为Win+R、注册表等可直接使用的文件名</remarks>
+        /// <param name="fileName">文件名</param>
+        /// <returns>成功提取返回现有文件路径,否则返回值为null</returns>
+        public static bool GetFullFilePath(string fileName, out string fullPath)
+        {
+            fullPath = null;
+            foreach(string name in new[] { fileName, $"{fileName}.exe" })
+            {
+                foreach(string dir in EnvironmentDirectorys)
+                {
+                    fullPath = Environment.ExpandEnvironmentVariables($@"{dir}\{name}");
+                    if(File.Exists(fullPath)) return true;
+                }
+
+                fullPath = Registry.GetValue($@"{RegAppPath}\{name}", "", null)?.ToString();
+                if(File.Exists(fullPath)) return true;
+            }
+            return false;
+        }
+
+        /// <summary>从包含现有文件路径的命令语句中提取文件路径</summary>
+        /// <param name="command">命令语句</param>
+        /// <returns>成功提取返回现有文件路径,否则返回值为null</returns>
+        public static string ExtractFilePath(string command)
+        {
+            if(string.IsNullOrWhiteSpace(command)) return null;
+            command = Environment.ExpandEnvironmentVariables(command).Replace(@"\\", @"\");
+            if(File.Exists(command)) return command;
+
+            string[] strs = Array.FindAll(command.Split(IllegalChars), str
+                => IgnoreCommandParts.Any(part => !part.Equals(str.Trim()))).Reverse().ToArray();
+            foreach(string str in strs)
+            {
+                int index = str.Length;
+                do
+                {
+                    string path1 = str.Substring(0, index);
+                    List<string> paths = new List<string> { path1 };
+                    if(path1.Contains(",")) paths.Add(path1.Substring(0, path1.LastIndexOf(',')));
+                    foreach(string path in paths)
+                    {
+                        if(File.Exists(path)) return path;
+                        if(GetFullFilePath(path, out string fullPath)) return fullPath;
+                    }
+                    index = path1.LastIndexOf(' ');
+                } while(index != -1);
+            }
+            return null;
+        }
+
+
+        /// <summary>移除文件或文件夹名称中的非法字符</summary>
+        /// <param name="fileName">文件或文件夹名称</param>
+        /// <returns>返回移除非法字符后的文件或文件夹名称</returns>
+        public static string RemoveIllegalChars(string fileName)
+        {
+            Array.ForEach(IllegalChars, c => fileName = fileName.Replace(c.ToString(), ""));
+            return fileName.Replace("\\", "").Replace(":", "");
+        }
+
+
+        /// <summary>判断文件或文件夹或注册表项是否存在</summary>
+        /// <param name="path">文件或文件夹或注册表项路径</param>
+        /// <param name="type">路径类型</param>
+        /// <returns>目标路径存在返回true,否则返回false</returns>
+        public static bool ObjectPathExist(string path, PathType type)
+        {
+            switch(type)
+            {
+                case PathType.File:
+                    return File.Exists(path);
+                case PathType.Directory:
+                    return Directory.Exists(path);
+                case PathType.Registry:
+                    return RegistryEx.GetRegistryKey(path) != null;
+                default:
+                    return false;
+            }
+        }
+
+        /// <summary>获取带序号的新路径</summary>
+        /// <param name="oldPath">目标路径</param>
+        /// <param name="type">路径类型</param>
+        /// <returns>如果目标路径不存在则返回目标路径,否则返回带序号的新路径</returns>
+        public static string GetNewPathWithIndex(string oldPath, PathType type)
+        {
+            string newPath;
+            string dirPath = type == PathType.Registry ? RegistryEx.GetParentPath(oldPath) : Path.GetDirectoryName(oldPath);
+            string name = type == PathType.Registry ? RegistryEx.GetKeyName(oldPath) : Path.GetFileNameWithoutExtension(oldPath);
+            string extension = type == PathType.Registry ? "" : Path.GetExtension(oldPath);
+
+            int index = 0;
+            do
+            {
+                newPath = $@"{dirPath}\{name}";
+                if(index > 0) newPath += index;
+                newPath += extension;
+                index++;
+            } while(ObjectPathExist(newPath, type));
+            return newPath;
+        }
+
+        public static void ShowPath(string path, PathType type)
+        {
+            switch(type)
+            {
+                case PathType.Directory:
+                    Process.Start(path);
+                    break;
+                case PathType.File:
+                    Process.Start("explorer.exe", $" /select,{path}");
+                    break;
+                case PathType.Registry:
+                    Registry.SetValue(RegLastPath, "LastKey", path);
+                    Process.Start("regedit.exe", "-m");
+                    break;
+            }
+        }
+    }
+}

+ 56 - 0
ContextMenuManager/BulePointLilac.Methods/PropertiesDialog.cs

@@ -0,0 +1,56 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace BulePointLilac.Methods
+{
+    public static class PropertiesDialog
+    {
+        public static bool Show(string filePath)
+        {
+            SHELLEXECUTEINFO info = new SHELLEXECUTEINFO
+            {
+                lpVerb = "Properties",
+                //lpParameters = "详细信息";//显示选项卡,此处有语言差异
+                lpFile = filePath,
+                nShow = SW_SHOW,
+                fMask = SEE_MASK_INVOKEIDLIST,
+                cbSize = CbSize
+            };
+            return ShellExecuteEx(ref info);
+        }
+
+        private const int SW_SHOW = 5;
+
+        private const uint SEE_MASK_INVOKEIDLIST = 12;
+
+        private static readonly int CbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO));
+
+        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
+        private static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo);
+
+        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+        private struct SHELLEXECUTEINFO
+        {
+            public int cbSize;
+            public uint fMask;
+            public IntPtr hwnd;
+            [MarshalAs(UnmanagedType.LPTStr)]
+            public string lpVerb;
+            [MarshalAs(UnmanagedType.LPTStr)]
+            public string lpFile;
+            [MarshalAs(UnmanagedType.LPTStr)]
+            public string lpParameters;
+            [MarshalAs(UnmanagedType.LPTStr)]
+            public string lpDirectory;
+            public int nShow;
+            public IntPtr hInstApp;
+            public IntPtr lpIDList;
+            [MarshalAs(UnmanagedType.LPTStr)]
+            public string lpClass;
+            public IntPtr hkeyClass;
+            public uint dwHotKey;
+            public IntPtr hIcon;
+            public IntPtr hProcess;
+        }
+    }
+}

+ 226 - 0
ContextMenuManager/BulePointLilac.Methods/RegTrustedInstaller.cs

@@ -0,0 +1,226 @@
+using Microsoft.Win32;
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.AccessControl;
+using System.Security.Principal;
+
+namespace BulePointLilac.Methods
+{
+    /// 获取TrustedInstaller权限注册表项的所有权
+    /// 代码主要为转载,仅做简单改动
+    /// 代码作者:JPBlanc
+    /// 代码原文:https://gist.github.com/JPBlanc/ca0e4f1830e4ca18a526#file-write_a_registry_own_by_trustedinstaller-cs
+    public class RegTrustedInstaller
+    {
+        static class NativeMethod
+        {
+            public const string TakeOwnership = "SeTakeOwnershipPrivilege";
+            public const string Restore = "SeRestorePrivilege";
+
+            public struct LUID
+            {
+                public int lowPart;
+                public int highPart;
+            }
+
+            public struct LUID_AND_ATTRIBUTES
+            {
+                public LUID Luid;
+                public int Attributes;
+            }
+
+            public struct TOKEN_PRIVILEGES
+            {
+                public int PrivilegeCount;
+                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
+                public LUID_AND_ATTRIBUTES[] Privileges;
+            }
+
+            public enum PrivilegeAttributes
+            {
+                /// <summary>特权被禁用.</summary>
+                Disabled = 0,
+                /// <summary>默认特权.</summary>
+                EnabledByDefault = 1,
+                /// <summary>特权被激活.</summary>
+                Enabled = 2,
+                /// <summary>特权被废除.</summary>
+                Removed = 4,
+                /// <summary>用于访问对象或服务的特权.</summary>
+                UsedForAccess = -2147483648
+            }
+
+            public enum TokenAccessRights
+            {
+                /// <summary>向进程附加主令牌的权限.</summary>
+                AssignPrimary = 0,
+                /// <summary>复制访问令牌的权利.</summary>
+                Duplicate = 1,
+                /// <summary>向进程附加模拟访问令牌的权限.</summary>
+                Impersonate = 4,
+                /// <summary>查询访问令牌的权利.</summary>
+                Query = 8,
+                /// <summary>有权查询访问令牌的来源.</summary>
+                QuerySource = 16,
+                /// <summary>启用或禁用访问令牌中的特权的权限.</summary>
+                AdjustPrivileges = 32,
+                /// <summary>调整访问令牌中的组属性的权限.</summary>
+                AdjustGroups = 64,
+                /// <summary>更改访问令牌的默认所有者、主组或DACL的权限.</summary>
+                AdjustDefault = 128,
+                /// <summary>正确调整访问令牌的会话ID.</summary>
+                AdjustSessionId = 256,
+                /// <summary>为令牌组合所有可能的访问权限.</summary>
+                AllAccess = AccessTypeMasks.StandardRightsRequired | AssignPrimary | Duplicate | Impersonate
+                            | Query | QuerySource | AdjustPrivileges | AdjustGroups | AdjustDefault | AdjustSessionId,
+                /// <summary>结合需要阅读的标准权利</summary>
+                Read = AccessTypeMasks.StandardRightsRead | Query,
+                /// <summary>组合了写入所需的标准权限</summary>
+                Write = AccessTypeMasks.StandardRightsWrite | AdjustPrivileges | AdjustGroups | AdjustDefault,
+                /// <summary>合并执行所需的标准权限</summary>
+                Execute = AccessTypeMasks.StandardRightsExecute | Impersonate
+            }
+
+            private enum AccessTypeMasks
+            {
+                Delete = 65536,
+                ReadControl = 131072,
+                WriteDAC = 262144,
+                WriteOwner = 524288,
+                Synchronize = 1048576,
+                StandardRightsRequired = 983040,
+                StandardRightsRead = ReadControl,
+                StandardRightsWrite = ReadControl,
+                StandardRightsExecute = ReadControl,
+                StandardRightsAll = 2031616,
+                SpecificRightsAll = 65535
+            }
+
+            [DllImport("advapi32.dll", SetLastError = true)]
+            private static extern bool AdjustTokenPrivileges(IntPtr accessTokenHandle, bool disableAllPrivileges,
+                ref TOKEN_PRIVILEGES newState, int bufferLength, ref TOKEN_PRIVILEGES previousState, ref int returnLength);
+
+            [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+            private static extern bool LookupPrivilegeValue(string systemName, string name, ref LUID luid);
+
+            [DllImport("advapi32.dll", SetLastError = true)]
+            private static extern bool OpenProcessToken(IntPtr processHandle, TokenAccessRights desiredAccess, ref IntPtr tokenHandle);
+
+            [DllImport("kernel32.dll", SetLastError = true)]
+            private static extern int GetLastError();
+
+            public static bool TrySetPrivilege(string sPrivilege, bool enablePrivilege)
+            {
+                bool blRc;
+                TOKEN_PRIVILEGES newTP = new TOKEN_PRIVILEGES();
+                TOKEN_PRIVILEGES oldTP = new TOKEN_PRIVILEGES();
+                LUID luid = new LUID();
+                int retrunLength = 0;
+                IntPtr processToken = IntPtr.Zero;
+
+                //本地进程令牌恢复
+                blRc = OpenProcessToken(Process.GetCurrentProcess().Handle, TokenAccessRights.AllAccess, ref processToken);
+                if(blRc == false) return false;
+
+                //恢复特权的唯一标识符空间
+                blRc = LookupPrivilegeValue(null, sPrivilege, ref luid);
+                if(blRc == false) return false;
+
+                //建立或取消特权
+                newTP.PrivilegeCount = 1;
+                newTP.Privileges = new LUID_AND_ATTRIBUTES[64];
+                newTP.Privileges[0].Luid = luid;
+
+                if(enablePrivilege) newTP.Privileges[0].Attributes = (int)PrivilegeAttributes.Enabled;
+                else newTP.Privileges[0].Attributes = (int)PrivilegeAttributes.Disabled;
+
+                oldTP.PrivilegeCount = 64;
+                oldTP.Privileges = new LUID_AND_ATTRIBUTES[64];
+                blRc = AdjustTokenPrivileges(processToken, false, ref newTP, 16, ref oldTP, ref retrunLength);
+
+                if(blRc == false) { GetLastError(); return false; }
+                return true;
+            }
+        }
+
+        /// <summary>获取注册表项权限</summary>
+        /// <remarks>将注册表项所有者改为当前管理员用户</remarks>
+        /// <param name="regPath">要获取权限的注册表完整路径</param>
+        public static void TakeRegKeyOwnerShip(string regPath)
+        {
+            if(string.IsNullOrWhiteSpace(regPath)) return;
+            RegistryKey key = null;
+            WindowsIdentity id = null;
+            //利用试错判断是否有写入权限
+            try { key = RegistryEx.GetRegistryKey(regPath, true); }
+            catch(Exception)
+            {
+                try
+                {
+                    //获取当前用户的ID
+                    id = WindowsIdentity.GetCurrent();
+
+                    //添加TakeOwnership特权
+                    bool flag = NativeMethod.TrySetPrivilege(NativeMethod.TakeOwnership, true);
+                    if(!flag) throw new PrivilegeNotHeldException(NativeMethod.TakeOwnership);
+
+                    //添加恢复特权(必须这样做才能更改所有者)
+                    flag = NativeMethod.TrySetPrivilege(NativeMethod.Restore, true);
+                    if(!flag) throw new PrivilegeNotHeldException(NativeMethod.Restore);
+
+                    //打开没有权限的注册表路径
+                    key = RegistryEx.GetRegistryKey(regPath, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.TakeOwnership);
+
+                    RegistrySecurity security = key.GetAccessControl(AccessControlSections.All);
+
+                    //得到真正所有者
+                    //IdentityReference oldId = security.GetOwner(typeof(SecurityIdentifier));
+                    //SecurityIdentifier siTrustedInstaller = new SecurityIdentifier(oldId.ToString());
+
+                    //使进程用户成为所有者
+                    security.SetOwner(id.User);
+                    key.SetAccessControl(security);
+
+                    //添加完全控制
+                    RegistryAccessRule fullAccess = new RegistryAccessRule(id.User, RegistryRights.FullControl,
+                        InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow);
+                    security.AddAccessRule(fullAccess);
+                    key.SetAccessControl(security);
+
+                    //注册表操作(写入、删除)
+                    //key.SetValue("temp", "");//示例
+
+                    //恢复原有所有者
+                    //security.SetOwner(siTrustedInstaller);
+                    //key.SetAccessControl(security);
+
+                    //收回原有权利
+                    //security.RemoveAccessRule(fullAccess);
+                    //key.SetAccessControl(security);
+
+                    ///得到真正所有者、注册表操作、恢复原有所有者、收回原有权利,这四部分在原文中没有被注释掉
+                    ///但是如果保留这四部分,会在恢复原有所有者这一步抛出异常,提示没有权限,
+                    ///不过我发现经过上面的操作,虽然无法还原所有者权限,但是已经获取了注册表权限
+                    ///即已经将TrustedInstaller权限更改为当前管理员用户权限,我要的目的已经达到了
+                }
+                catch(Exception) { }
+            }
+            finally { key?.Dispose(); id?.Dispose(); }
+        }
+
+        /// <summary>获取注册表项及其子项、递归子级子项权限</summary>
+        /// <remarks>将注册表项所有者改为当前管理员用户</remarks>
+        /// <param name="regPath">要获取权限的注册表完整路径</param>
+        public static void TakeRegTreeOwnerShip(string regPath)
+        {
+            if(string.IsNullOrWhiteSpace(regPath)) return;
+            TakeRegKeyOwnerShip(regPath);
+            using(RegistryKey key = RegistryEx.GetRegistryKey(regPath))
+                if(key != null)
+                    foreach(string subKeyName in key.GetSubKeyNames())
+                        TakeRegTreeOwnerShip($@"{key.Name}\{subKeyName}");
+
+        }
+    }
+}

+ 110 - 0
ContextMenuManager/BulePointLilac.Methods/RegistryEx.cs

@@ -0,0 +1,110 @@
+using Microsoft.Win32;
+using System;
+using System.Security.AccessControl;
+
+namespace BulePointLilac.Methods
+{
+
+    public static class RegistryEx
+    {
+
+        /// <summary>获取指定路径注册表项的上一级路径</summary>
+        public static string GetParentPath(string regPath) => regPath.Substring(0, regPath.LastIndexOf('\\'));
+
+        /// <summary>获取指定路径注册表项的项名</summary>
+        public static string GetKeyName(string regPath) => regPath.Substring(regPath.LastIndexOf('\\') + 1);
+
+        /// <summary>获取指定路径注册表项的根项项名</summary>
+        public static string GetRootName(string regPath) => regPath.Substring(0, regPath.IndexOf('\\'));
+
+        /// <summary>获取不包含根项部分的注册表路径</summary>
+        public static string GetPathWithoutRoot(string regPath) => regPath.Substring(regPath.IndexOf('\\') + 1);
+
+        /// <summary>删除指定路径的注册表项的指定名称的键值</summary>
+        /// <param name="regPath">注册表项路径</param>
+        /// <param name="valueName">要删除的键值名称</param>
+        /// <param name="throwOnMissingValue">找不到键值时是否抛出异常</param>
+        public static void DeleteValue(string regPath, string valueName, bool throwOnMissingValue = false)
+        {
+            GetRegistryKey(regPath, true)?.DeleteValue(valueName, throwOnMissingValue);
+        }
+
+        /// <summary>递归删除指定注册表项及所有子项</summary>
+        /// <param name="regPath">注册表路径</param>
+        /// <param name="throwOnMissingKey">找不到注册表项时是否抛出异常</param>
+        public static void DeleteKeyTree(string regPath, bool throwOnMissingKey = false)
+        {
+            string dirPath = GetParentPath(regPath);
+            string keyName = GetKeyName(regPath);
+            GetRegistryKey(dirPath, true)?.DeleteSubKeyTree(keyName, throwOnMissingKey);
+        }
+
+        /// <summary>获取指定注册表路径的根项RegistryKey和不包含根项部分的注册表路径</summary>
+        /// <param name="regPath">注册表路径</param>
+        /// <param name="root">成功解析返回一个RegistryKey,否则抛出异常</param>
+        /// <param name="subRegPath">不包含根项的注册表路径</param>
+        public static void GetRootAndSubRegPath(string regPath, out RegistryKey root, out string subRegPath)
+        {
+            int index = regPath.IndexOf('\\');
+            subRegPath = regPath.Substring(index + 1);
+            string rootPath = regPath.Substring(0, index).ToUpper();
+            switch(rootPath)
+            {
+                case "HKEY_CLASSES_ROOT":
+                    root = Registry.ClassesRoot;
+                    break;
+                case "HKEY_CURRENT_USER":
+                    root = Registry.CurrentUser;
+                    break;
+                case "HKEY_LOCAL_MACHINE":
+                    root = Registry.LocalMachine;
+                    break;
+                case "HKEY_USERS":
+                    root = Registry.Users;
+                    break;
+                case "HKEY_CURRENT_CONFIG":
+                    root = Registry.CurrentConfig;
+                    break;
+                default:
+                    throw new ArgumentNullException("非法的根项!");
+            }
+        }
+
+        /// <summary>获取指定注册表项路径的RegistryKey</summary>
+        /// <param name="regPath">注册表项路径</param>
+        /// <param name="writable">写入访问权限</param>
+        /// <param name="create">是否创建新项</param>
+        public static RegistryKey GetRegistryKey(string regPath, bool writable = false, bool create = false)
+        {
+            GetRootAndSubRegPath(regPath, out RegistryKey root, out string keyPath);
+            using(root)
+            {
+                if(create) return root.CreateSubKey(keyPath, writable);
+                else return root.OpenSubKey(keyPath, writable);
+            }
+        }
+
+        public static RegistryKey GetRegistryKey(string regPath, RegistryKeyPermissionCheck check, RegistryRights rights)
+        {
+            GetRootAndSubRegPath(regPath, out RegistryKey root, out string keyPath);
+            using(root) return root.OpenSubKey(keyPath, check, rights);
+        }
+    }
+
+    public static class RegistryKeyExtension
+    {
+        public static void CopyTo(this RegistryKey srcKey, RegistryKey dstKey)
+        {
+            foreach(string name in srcKey.GetValueNames())
+            {
+                dstKey.SetValue(name, srcKey.GetValue(name), srcKey.GetValueKind(name));
+            }
+            foreach(string name in srcKey.GetSubKeyNames())
+            {
+                using(RegistryKey srcSubKey = srcKey.OpenSubKey(name))
+                using(RegistryKey dstSubKey = dstKey.CreateSubKey(name, true))
+                    srcSubKey.CopyTo(dstSubKey);
+            }
+        }
+    }
+}

+ 173 - 0
ContextMenuManager/BulePointLilac.Methods/ResourceIcon.cs

@@ -0,0 +1,173 @@
+using Microsoft.Win32;
+using System;
+using System.Drawing;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace BulePointLilac.Methods
+{
+    public static class ResourceIcon
+    {
+        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
+        private static extern int ExtractIconEx(string lpFileName, int nIconIndex, IntPtr[] phIconLarge, IntPtr[] phIconSmall, uint nIcons);
+
+        [DllImport("user32.dll", SetLastError = true)]
+        private static extern bool DestroyIcon(IntPtr hIcon);
+
+        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false, ThrowOnUnmappableChar = true)]
+        private static extern IntPtr LoadLibrary(string lpLibFileName);
+
+        [DllImport("kernel32.dll")]
+        private static extern bool FreeLibrary(IntPtr hLibModule);
+
+        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+        private static extern IntPtr LoadImage(IntPtr hInst, string lpFileName, uint uType, int cx, int cy, uint fuLoad);
+
+        [DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Auto)]
+        private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, FileInfoFlags uFlags);
+
+        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
+        private struct SHFILEINFO
+        {
+            public IntPtr hIcon;
+            public int iIcon;
+            public uint dwAttributes;
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
+            public string szDisplayName;
+            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
+            public string szTypeName;
+        }
+
+        [Flags]
+        public enum FileInfoFlags : uint
+        {
+            SHGFI_ICON = 0x000000100,               // get icon
+            SHGFI_DISPLAYNAME = 0x000000200,        // get display name
+            SHGFI_TYPENAME = 0x000000400,           // get type name
+            SHGFI_ATTRIBUTES = 0x000000800,         // get attributes
+            SHGFI_ICONLOCATION = 0x000001000,       // get icon location
+            SHGFI_EXETYPE = 0x000002000,            // return exe type
+            SHGFI_SYSICONINDEX = 0x000004000,       // get system icon index
+            SHGFI_LINKOVERLAY = 0x000008000,        // put a link overlay on icon
+            SHGFI_SELECTED = 0x000010000,           // show icon in selected state
+            SHGFI_ATTR_SPECIFIED = 0x000020000,     // get only specified attributes
+            SHGFI_LARGEICON = 0x000000000,          // get large icon
+            SHGFI_SMALLICON = 0x000000001,          // get small icon
+            SHGFI_OPENICON = 0x000000002,           // get open icon
+            SHGFI_SHELLICONSIZE = 0x000000004,      // get shell size icon
+            SHGFI_PIDL = 0x000000008,               // pszPath is a pidl
+            SHGFI_USEFILEATTRIBUTES = 0x000000010,  // use passed dwFileAttribute
+            SHGFI_ADDOVERLAYS = 0x000000020,        // apply the appropriate overlays
+            SHGFI_OVERLAYINDEX = 0x000000040        // get the index of the overlay in the upper 8 bits of the iIcon
+        }
+
+        /// <summary>获取文件类型的关联图标</summary>
+        /// <param name="extension">文件类型的扩展名,如.txt</param>
+        /// <returns>获取到的图标</returns>
+        public static Icon GetExtensionIcon(string extension)
+        {
+            FileInfoFlags flags = FileInfoFlags.SHGFI_ICON | FileInfoFlags.SHGFI_LARGEICON | FileInfoFlags.SHGFI_USEFILEATTRIBUTES;
+            return GetIcon(extension, flags);
+        }
+
+        /// <summary>获取文件夹、磁盘驱动器的图标</summary>
+        /// <param name="folderPath">文件夹或磁盘驱动器路径</param>
+        /// <returns>获取到的图标</returns>
+        public static Icon GetFolderIcon(string folderPath)
+        {
+            FileInfoFlags flags = FileInfoFlags.SHGFI_ICON | FileInfoFlags.SHGFI_LARGEICON;
+            return GetIcon(folderPath, flags);
+        }
+
+        /// <summary>根据文件信息标志提取指定文件路径的图标</summary>
+        /// <param name="filePath">文件路径</param>
+        /// <param name="flags">文件信息标志</param>
+        /// <returns>获取到的图标</returns>
+        public static Icon GetIcon(string filePath, FileInfoFlags flags)
+        {
+            SHFILEINFO info = new SHFILEINFO();
+            IntPtr hInfo = SHGetFileInfo(filePath, 0, ref info, (uint)Marshal.SizeOf(info), flags);
+            if(hInfo.Equals(IntPtr.Zero)) return null;
+            Icon icon = (Icon)Icon.FromHandle(info.hIcon).Clone();
+            DestroyIcon(info.hIcon); //释放资源
+            return icon;
+        }
+
+        /// <summary>获取指定位置的图标</summary>
+        /// <param name="iconLocation">图标位置</param>
+        /// <returns>获取到的图标</returns>
+        public static Icon GetIcon(string iconLocation)
+        {
+            return GetIcon(iconLocation, out _, out _);
+        }
+
+        /// <summary>获取指定位置的图标</summary>
+        /// <param name="iconLocation">图标位置</param>
+        /// <param name="iconPath">返回图标文件路径</param>
+        /// <param name="iconIndex">返回图标索引</param>
+        /// <returns>获取到的图标</returns>
+        public static Icon GetIcon(string iconLocation, out string iconPath, out int iconIndex)
+        {
+            iconIndex = 0; iconPath = null;
+            if(string.IsNullOrWhiteSpace(iconLocation)) return null;
+            iconLocation = Environment.ExpandEnvironmentVariables(iconLocation).Replace("\"", "");
+            int index = iconLocation.LastIndexOf(',');
+            if(index == -1) iconPath = iconLocation;
+            else
+            {
+                if(File.Exists(iconLocation)) iconPath = iconLocation;
+                else
+                {
+                    bool flag = int.TryParse(iconLocation.Substring(index + 1), out iconIndex);
+                    iconPath = flag ? iconLocation.Substring(0, index) : null;
+                }
+            }
+            return GetIcon(iconPath, iconIndex);
+        }
+
+        /// <summary>获取指定文件中指定索引的图标</summary>
+        /// <param name="iconPath">图标文件路径</param>
+        /// <param name="iconIndex">图标索引</param>
+        /// <returns>获取到的图标</returns>
+        public static Icon GetIcon(string iconPath, int iconIndex)
+        {
+            Icon icon = null;
+            if(string.IsNullOrWhiteSpace(iconPath)) return icon;
+            iconPath = Environment.ExpandEnvironmentVariables(iconPath).Replace("\"", "");
+
+            if(Path.GetFileName(iconPath).ToLower() == "shell32.dll")
+            {
+                iconPath = "shell32.dll";//系统强制文件重定向
+                icon = GetReplacedShellIcon(iconIndex);//注册表图标重定向
+                if(icon != null) return icon;
+            }
+
+            IntPtr hInst = IntPtr.Zero;
+            IntPtr[] hIcons = new[] { IntPtr.Zero };
+            //iconIndex为负数就是指定资源标识符, 为正数就是该图标在资源文件中的顺序序号, 为-1时不能使用ExtractIconEx提取图标
+            if(iconIndex == -1)
+            {
+                hInst = LoadLibrary(iconPath);
+                hIcons[0] = LoadImage(hInst, "#1", 1, SystemInformation.IconSize.Width, SystemInformation.IconSize.Height, 0);
+            }
+            else ExtractIconEx(iconPath, iconIndex, hIcons, null, 1);
+
+            try { icon = (Icon)Icon.FromHandle(hIcons[0]).Clone(); }
+            catch { icon = null; }
+            finally { DestroyIcon(hIcons[0]); FreeLibrary(hInst); }//释放资源
+            return icon;
+        }
+
+        private const string ShellIconPath = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Icons";
+        /// <summary>获取shell32.dll中的图标被替换后的图标</summary>
+        /// <param name="iconIndex">图标索引</param>
+        /// <returns>获取到的图标</returns>
+        public static Icon GetReplacedShellIcon(int iconIndex)
+        {
+            string iconLocation = Registry.GetValue(ShellIconPath, iconIndex.ToString(), null)?.ToString();
+            if(iconLocation != null) return GetIcon(iconLocation) ?? GetIcon("imageres.dll", 2);
+            else return null;
+        }
+    }
+}

+ 22 - 0
ContextMenuManager/BulePointLilac.Methods/ResourceString.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace BulePointLilac.Methods
+{
+    public static class ResourceString
+    {
+        /// <summary> 获取格式为"@filename,-strID"直接字符串 </summary>
+        /// <param name="resStr">要转换的字符串</param>
+        /// <returns>resStr为Null时返回值为string.Empty; resStr首字符为@但解析失败时返回string.Empty</returns>
+        public static string GetDirectString(string resStr)
+        {
+            StringBuilder outBuff = new StringBuilder(1024);
+            SHLoadIndirectString(resStr, outBuff, 1024, IntPtr.Zero);
+            return outBuff.ToString();
+        }
+
+        [DllImport("shlwapi.dll", BestFitMapping = false, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false, ThrowOnUnmappableChar = true)]
+        public static extern int SHLoadIndirectString(string pszSource, StringBuilder pszOutBuf, uint cchOutBuf, IntPtr ppvReserved);
+    }
+}

+ 488 - 0
ContextMenuManager/BulePointLilac.Methods/RichTextBoxExtension.cs

@@ -0,0 +1,488 @@
+using System;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using System.Xml.Linq;
+
+namespace BulePointLilac.Methods
+{
+    public static class RichTextBoxExtension
+    {
+        /// 代码原文:https://archive.codeplex.com/?p=xmlrichtextbox
+        /// 本人仅作简单修改,将原继承类改写为扩展方法
+        /// <summary>RichTextBox中XML语法高亮</summary>
+        /// <param name="xmlStr">要显示的xml文本</param>
+        public static void LoadXml(this RichTextBox box, string xmlStr)
+        {
+            try
+            {
+                xmlStr = XDocument.Parse(xmlStr).ToString().Trim();
+                if(string.IsNullOrEmpty(xmlStr)) return;
+            }
+            catch(Exception e) { throw e; }
+
+            XmlStateMachine machine = new XmlStateMachine();
+            if(xmlStr.StartsWith("<?"))
+            {
+                string declaration = machine.GetXmlDeclaration(xmlStr);
+                if(declaration != string.Empty) xmlStr = declaration + Environment.NewLine + xmlStr;
+            }
+
+            int location = 0;
+            int failCount = 0;
+            int tokenTryCount = 0;
+            while(location < xmlStr.Length)
+            {
+                string token = machine.GetNextToken(xmlStr, location, out XmlTokenType ttype);
+                Color color = machine.GetTokenColor(ttype);
+                box.AppendText(token, color);
+                location += token.Length;
+                tokenTryCount++;
+
+                // Check for ongoing failure
+                if(token.Length == 0) failCount++;
+                if(failCount > 10 || tokenTryCount > xmlStr.Length)
+                {
+                    string theRestOfIt = xmlStr.Substring(location, xmlStr.Length - location);
+                    //this.AppendText(Environment.NewLine + Environment.NewLine + theRestOfIt); // DEBUG
+                    box.AppendText(theRestOfIt);
+                    break;
+                }
+            }
+        }
+
+        public static void LoadIni(this RichTextBox box, string iniStr)
+        {
+            string[] lines = iniStr.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
+            Array.ForEach(lines, line =>
+            {
+                string str = line.Trim();
+                if(str.StartsWith(";"))
+                {
+                    box.AppendText(str, Color.Green);
+                }
+                else if(str.StartsWith("["))
+                {
+                    if(str.Contains("]"))
+                    {
+                        int index = str.IndexOf(']');
+                        box.AppendText(str.Substring(0, index + 1), Color.Brown);
+                        box.AppendText(str.Substring(index + 1), Color.Gray);
+                    }
+                    else box.AppendText(str, Color.Gray);
+                }
+                else if(str.Contains("="))
+                {
+                    int index = str.IndexOf('=');
+                    box.AppendText(str.Substring(0, index), Color.Red);
+                    box.AppendText("=", Color.Black);
+                    box.AppendText(str.Substring(index + 1), Color.Blue);
+                }
+                else box.AppendText(str, Color.Gray);
+                box.AppendText(Environment.NewLine);
+            });
+        }
+
+        public static void AppendText(this RichTextBox box, string text, Color color)
+        {
+            box.SelectionStart = box.TextLength;
+            box.SelectionLength = 0;
+            box.SelectionColor = color;
+            box.AppendText(text);
+            box.SelectionColor = box.ForeColor;
+        }
+
+        sealed class XmlStateMachine
+        {
+            public XmlTokenType CurrentState = XmlTokenType.Unknown;
+            private string subString = string.Empty;
+            private string token = string.Empty;
+
+            public string GetNextToken(string s, int location, out XmlTokenType ttype)
+            {
+                ttype = XmlTokenType.Unknown;
+                // skip past any whitespace (token added to it at the end of method)
+                string whitespace = GetWhitespace(s, location);
+                if(!string.IsNullOrEmpty(whitespace)) location += whitespace.Length;
+                subString = s.Substring(location, s.Length - location);
+                token = string.Empty;
+                if(CurrentState == XmlTokenType.CDataStart)
+                {
+                    // check for empty CDATA
+                    if(subString.StartsWith("]]>"))
+                    {
+                        CurrentState = XmlTokenType.CDataEnd;
+                        token = "]]>";
+                    }
+                    else
+                    {
+                        CurrentState = XmlTokenType.CDataValue;
+                        int n = subString.IndexOf("]]>");
+                        token = subString.Substring(0, n);
+                    }
+                }
+                else if(CurrentState == XmlTokenType.DocTypeStart)
+                {
+                    CurrentState = XmlTokenType.DocTypeName;
+                    token = "DOCTYPE";
+                }
+                else if(CurrentState == XmlTokenType.DocTypeName)
+                {
+                    CurrentState = XmlTokenType.DocTypeDeclaration;
+                    int n = subString.IndexOf("[");
+                    token = subString.Substring(0, n);
+                }
+                else if(CurrentState == XmlTokenType.DocTypeDeclaration)
+                {
+                    CurrentState = XmlTokenType.DocTypeDefStart;
+                    token = "[";
+                }
+                else if(CurrentState == XmlTokenType.DocTypeDefStart)
+                {
+                    if(subString.StartsWith("]>"))
+                    {
+                        CurrentState = XmlTokenType.DocTypeDefEnd;
+                        token = "]>";
+                    }
+                    else
+                    {
+                        CurrentState = XmlTokenType.DocTypeDefValue;
+                        int n = subString.IndexOf("]>");
+                        token = subString.Substring(0, n);
+                    }
+                }
+                else if(CurrentState == XmlTokenType.DocTypeDefValue)
+                {
+                    CurrentState = XmlTokenType.DocTypeDefEnd;
+                    token = "]>";
+                }
+                else if(CurrentState == XmlTokenType.DoubleQuotationMarkStart)
+                {
+                    // check for empty attribute value
+                    if(subString[0] == '\"')
+                    {
+                        CurrentState = XmlTokenType.DoubleQuotationMarkEnd;
+                        token = "\"";
+                    }
+                    else
+                    {
+                        CurrentState = XmlTokenType.AttributeValue;
+                        int n = subString.IndexOf("\"");
+                        token = subString.Substring(0, n);
+                    }
+                }
+                else if(CurrentState == XmlTokenType.SingleQuotationMarkStart)
+                {
+                    // check for empty attribute value
+                    if(subString[0] == '\'')
+                    {
+                        CurrentState = XmlTokenType.SingleQuotationMarkEnd;
+                        token = "\'";
+                    }
+                    else
+                    {
+                        CurrentState = XmlTokenType.AttributeValue;
+                        int n = subString.IndexOf("'");
+                        token = subString.Substring(0, n);
+                    }
+                }
+                else if(CurrentState == XmlTokenType.CommentStart)
+                {
+                    // check for empty comment
+                    if(subString.StartsWith("-->"))
+                    {
+                        CurrentState = XmlTokenType.CommentEnd;
+                        token = "-->";
+                    }
+                    else
+                    {
+                        CurrentState = XmlTokenType.CommentValue;
+                        token = ReadCommentValue(subString);
+                    }
+                }
+                else if(CurrentState == XmlTokenType.NodeStart)
+                {
+                    CurrentState = XmlTokenType.NodeName;
+                    token = ReadNodeName(subString);
+                }
+                else if(CurrentState == XmlTokenType.XmlDeclarationStart)
+                {
+                    CurrentState = XmlTokenType.NodeName;
+                    token = ReadNodeName(subString);
+                }
+                else if(CurrentState == XmlTokenType.NodeName)
+                {
+                    if(subString[0] != '/' &&
+                        subString[0] != '>')
+                    {
+                        CurrentState = XmlTokenType.AttributeName;
+                        token = ReadAttributeName(subString);
+                    }
+                    else
+                    {
+                        HandleReservedXmlToken();
+                    }
+                }
+                else if(CurrentState == XmlTokenType.NodeEndValueStart)
+                {
+                    if(subString[0] == '<')
+                    {
+                        HandleReservedXmlToken();
+                    }
+                    else
+                    {
+                        CurrentState = XmlTokenType.NodeValue;
+                        token = ReadNodeValue(subString);
+                    }
+                }
+                else if(CurrentState == XmlTokenType.DoubleQuotationMarkEnd)
+                {
+                    HandleAttributeEnd();
+                }
+                else if(CurrentState == XmlTokenType.SingleQuotationMarkEnd)
+                {
+                    HandleAttributeEnd();
+                }
+                else
+                {
+                    HandleReservedXmlToken();
+                }
+                if(token != string.Empty)
+                {
+                    ttype = CurrentState;
+                    return whitespace + token;
+                }
+                return string.Empty;
+            }
+
+            public Color GetTokenColor(XmlTokenType ttype)
+            {
+                switch(ttype)
+                {
+                    case XmlTokenType.NodeValue:
+                    case XmlTokenType.EqualSignStart:
+                    case XmlTokenType.EqualSignEnd:
+                    case XmlTokenType.DoubleQuotationMarkStart:
+                    case XmlTokenType.DoubleQuotationMarkEnd:
+                    case XmlTokenType.SingleQuotationMarkStart:
+                    case XmlTokenType.SingleQuotationMarkEnd:
+                        return Color.Black;
+                    case XmlTokenType.XmlDeclarationStart:
+                    case XmlTokenType.XmlDeclarationEnd:
+                    case XmlTokenType.NodeStart:
+                    case XmlTokenType.NodeEnd:
+                    case XmlTokenType.NodeEndValueStart:
+                    case XmlTokenType.CDataStart:
+                    case XmlTokenType.CDataEnd:
+                    case XmlTokenType.CommentStart:
+                    case XmlTokenType.CommentEnd:
+                    case XmlTokenType.AttributeValue:
+                    case XmlTokenType.DocTypeStart:
+                    case XmlTokenType.DocTypeEnd:
+                    case XmlTokenType.DocTypeDefStart:
+                    case XmlTokenType.DocTypeDefEnd:
+                        return Color.Blue;
+                    case XmlTokenType.CDataValue:
+                    case XmlTokenType.DocTypeDefValue:
+                        return Color.Gray;
+                    case XmlTokenType.CommentValue:
+                        return Color.Green;
+                    case XmlTokenType.DocTypeName:
+                    case XmlTokenType.NodeName:
+                        return Color.Brown;
+                    case XmlTokenType.AttributeName:
+                    case XmlTokenType.DocTypeDeclaration:
+                        return Color.Red;
+                    default:
+                        return Color.Orange;
+                }
+            }
+
+            public string GetXmlDeclaration(string s)
+            {
+                int start = s.IndexOf("<?");
+                int end = s.IndexOf("?>");
+                if(start > -1 && end > start)
+                {
+                    return s.Substring(start, end - start + 2);
+                }
+                return string.Empty;
+            }
+            private void HandleAttributeEnd()
+            {
+                if(subString.StartsWith(">"))
+                {
+                    HandleReservedXmlToken();
+                }
+                else if(subString.StartsWith("/>"))
+                {
+                    HandleReservedXmlToken();
+                }
+                else if(subString.StartsWith("?>"))
+                {
+                    HandleReservedXmlToken();
+                }
+                else
+                {
+                    CurrentState = XmlTokenType.AttributeName;
+                    token = ReadAttributeName(subString);
+                }
+            }
+
+            private void HandleReservedXmlToken()
+            {
+                // check if state changer
+                // <, >, =, </, />, <![CDATA[, <!--, -->
+                if(subString.StartsWith("<![CDATA["))
+                {
+                    CurrentState = XmlTokenType.CDataStart;
+                    token = "<![CDATA[";
+                }
+                else if(subString.StartsWith("<!DOCTYPE"))
+                {
+                    CurrentState = XmlTokenType.DocTypeStart;
+                    token = "<!";
+                }
+                else if(subString.StartsWith("</"))
+                {
+                    CurrentState = XmlTokenType.NodeStart;
+                    token = "</";
+                }
+                else if(subString.StartsWith("<!--"))
+                {
+                    CurrentState = XmlTokenType.CommentStart;
+                    token = "<!--";
+                }
+                else if(subString.StartsWith("<?"))
+                {
+                    CurrentState = XmlTokenType.XmlDeclarationStart;
+                    token = "<?";
+                }
+                else if(subString.StartsWith("<"))
+                {
+                    CurrentState = XmlTokenType.NodeStart;
+                    token = "<";
+                }
+                else if(subString.StartsWith("="))
+                {
+                    CurrentState = XmlTokenType.EqualSignStart;
+                    if(CurrentState == XmlTokenType.AttributeValue) CurrentState = XmlTokenType.EqualSignEnd;
+                    token = "=";
+                }
+                else if(subString.StartsWith("?>"))
+                {
+                    CurrentState = XmlTokenType.XmlDeclarationEnd;
+                    token = "?>";
+                }
+                else if(subString.StartsWith(">"))
+                {
+                    CurrentState = XmlTokenType.NodeEndValueStart;
+                    token = ">";
+                }
+                else if(subString.StartsWith("-->"))
+                {
+                    CurrentState = XmlTokenType.CommentEnd;
+                    token = "-->";
+                }
+                else if(subString.StartsWith("]>"))
+                {
+                    CurrentState = XmlTokenType.DocTypeEnd;
+                    token = "]>";
+                }
+                else if(subString.StartsWith("]]>"))
+                {
+                    CurrentState = XmlTokenType.CDataEnd;
+                    token = "]]>";
+                }
+                else if(subString.StartsWith("/>"))
+                {
+                    CurrentState = XmlTokenType.NodeEnd;
+                    token = "/>";
+                }
+                else if(subString.StartsWith("\""))
+                {
+                    if(CurrentState == XmlTokenType.AttributeValue)
+                    {
+                        CurrentState = XmlTokenType.DoubleQuotationMarkEnd;
+                    }
+                    else
+                    {
+                        CurrentState = XmlTokenType.DoubleQuotationMarkStart;
+                    }
+                    token = "\"";
+                }
+                else if(subString.StartsWith("'"))
+                {
+                    CurrentState = XmlTokenType.SingleQuotationMarkStart;
+                    if(CurrentState == XmlTokenType.AttributeValue)
+                    {
+                        CurrentState = XmlTokenType.SingleQuotationMarkEnd;
+                    }
+                    token = "'";
+                }
+            }
+
+            private string ReadNodeName(string s)
+            {
+                string nodeName = "";
+                for(int i = 0; i < s.Length; i++)
+                {
+                    if(s[i] == '/' || s[i] == ' ' || s[i] == '>') return nodeName;
+                    else nodeName += s[i].ToString();
+                }
+                return nodeName;
+            }
+
+            private string ReadAttributeName(string s)
+            {
+                string attName = "";
+                int n = s.IndexOf('=');
+                if(n != -1) attName = s.Substring(0, n);
+                return attName;
+            }
+
+            private string ReadNodeValue(string s)
+            {
+                string nodeValue = "";
+                int n = s.IndexOf('<');
+                if(n != -1) nodeValue = s.Substring(0, n);
+                return nodeValue;
+            }
+
+            private string ReadCommentValue(string s)
+            {
+                string commentValue = "";
+                int n = s.IndexOf("-->");
+                if(n != -1) commentValue = s.Substring(0, n);
+                return commentValue;
+            }
+
+            private string GetWhitespace(string s, int location)
+            {
+                bool foundWhitespace = false;
+                StringBuilder sb = new StringBuilder();
+                for(int i = 0; (location + i) < s.Length; i++)
+                {
+                    char c = s[location + i];
+                    if(char.IsWhiteSpace(c))
+                    {
+                        foundWhitespace = true;
+                        sb.Append(c);
+                    }
+                    else break;
+                }
+                if(foundWhitespace) return sb.ToString();
+                return string.Empty;
+            }
+        }
+
+        enum XmlTokenType
+        {
+            Whitespace, XmlDeclarationStart, XmlDeclarationEnd, NodeStart, NodeEnd, NodeEndValueStart, NodeName,
+            NodeValue, AttributeName, AttributeValue, EqualSignStart, EqualSignEnd, CommentStart, CommentValue,
+            CommentEnd, CDataStart, CDataValue, CDataEnd, DoubleQuotationMarkStart, DoubleQuotationMarkEnd,
+            SingleQuotationMarkStart, SingleQuotationMarkEnd, DocTypeStart, DocTypeName, DocTypeDeclaration,
+            DocTypeDefStart, DocTypeDefValue, DocTypeDefEnd, DocTypeEnd, DocumentEnd, Unknown
+        }
+    }
+}

+ 375 - 0
ContextMenuManager/ContextMenuManager.csproj

@@ -0,0 +1,375 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{EF7E60E9-3565-42BA-AFB3-7FE73A1B011C}</ProjectGuid>
+    <OutputType>WinExe</OutputType>
+    <RootNamespace>ContextMenuManager</RootNamespace>
+    <AssemblyName>ContextMenuManager</AssemblyName>
+    <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+    <IsWebBootstrapper>false</IsWebBootstrapper>
+    <TargetFrameworkProfile />
+    <PublishUrl>publish\</PublishUrl>
+    <Install>true</Install>
+    <InstallFrom>Disk</InstallFrom>
+    <UpdateEnabled>false</UpdateEnabled>
+    <UpdateMode>Foreground</UpdateMode>
+    <UpdateInterval>7</UpdateInterval>
+    <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+    <UpdatePeriodically>false</UpdatePeriodically>
+    <UpdateRequired>false</UpdateRequired>
+    <MapFileExtensions>true</MapFileExtensions>
+    <ApplicationRevision>0</ApplicationRevision>
+    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+    <UseApplicationTrust>false</UseApplicationTrust>
+    <BootstrapperEnabled>true</BootstrapperEnabled>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+    <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
+    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <PropertyGroup>
+    <ApplicationIcon>Properties\AppIcon.ico</ApplicationIcon>
+  </PropertyGroup>
+  <PropertyGroup>
+    <TargetZone>LocalIntranet</TargetZone>
+  </PropertyGroup>
+  <PropertyGroup>
+    <GenerateManifests>false</GenerateManifests>
+  </PropertyGroup>
+  <PropertyGroup />
+  <PropertyGroup>
+    <SignManifests>false</SignManifests>
+  </PropertyGroup>
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup>
+    <Win32Resource>
+    </Win32Resource>
+  </PropertyGroup>
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup>
+    <StartupObject>ContextMenuManager.Program</StartupObject>
+  </PropertyGroup>
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup />
+  <PropertyGroup>
+    <ApplicationManifest>Properties\App.manifest</ApplicationManifest>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="PresentationFramework" />
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="System.Deployment" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Xml.Linq" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="BulePointLilac.Controls\MyToolTip.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="BulePointLilac.Controls\ResizbleForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="BulePointLilac.Controls\MyToolBar.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="BulePointLilac.Methods\IniFileHelper.cs" />
+    <Compile Include="BulePointLilac.Methods\IniReader.cs" />
+    <Compile Include="BulePointLilac.Methods\MessageBoxEx.cs" />
+    <Compile Include="BulePointLilac.Methods\RichTextBoxExtension.cs" />
+    <Compile Include="Controls\AddCommonButton.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\SelectItemsForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\IBtnMoveUpDownItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\SubMenuModeForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Controls\ShellStoreDialog.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\IFoldGroupItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\IBtnOpenPathItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\IBtnDeleteItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\ITsiDeleteItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\ITsiCommandItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\ITsiFilePathItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\ITsiIconItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\ITsiTextItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\ITsiRegPathItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\IBtnShowMenuItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\IChkVisibleItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\ITsiWebSearchItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\NewItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\NewItemForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="Controls\ShellCommonDialog.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\AboutApp.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\FileExtensionDialog.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\GuidBlockedItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\GuidBlockedList.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\NewOpenWithDialog.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\NewSendToDialog.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\NewShellDialog.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\OpenWithList.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\RegRuleItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\OpenWithItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\ExplorerRestarter.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\SendToItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\SendToList.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\ShellList.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\ShellNewItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\ShellNewList.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\ThirdRulesList.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\WinXItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\WinXList.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="MainForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="BulePointLilac.Controls\IconDialog.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="BulePointLilac.Controls\InputDialog.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="BulePointLilac.Controls\MyCheckBox.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="BulePointLilac.Controls\MyListBox.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="BulePointLilac.Controls\MyMainForm.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="BulePointLilac.Controls\MySideBar.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="BulePointLilac.Controls\MyStatusBar.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\ShellSubMenuDialog.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="BulePointLilac.Controls\PictureButton.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\ShellExItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\ShellItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="BulePointLilac.Methods\ControlExtension.cs" />
+    <Compile Include="BulePointLilac.Methods\HighDpi.cs" />
+    <Compile Include="BulePointLilac.Methods\RegistryEx.cs" />
+    <Compile Include="BulePointLilac.Methods\PropertiesDialog.cs" />
+    <Compile Include="BulePointLilac.Methods\RegTrustedInstaller.cs" />
+    <Compile Include="AppString.cs" />
+    <Compile Include="AppImage.cs" />
+    <Compile Include="BulePointLilac.Methods\EncodingType.cs" />
+    <Compile Include="GuidInfo.cs" />
+    <Compile Include="BulePointLilac.Methods\ImageExtension.cs" />
+    <Compile Include="BulePointLilac.Methods\ResourceIcon.cs" />
+    <Compile Include="BulePointLilac.Methods\ResourceString.cs" />
+    <Compile Include="BulePointLilac.Methods\ObjectPath.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Updater.cs" />
+    <None Include="App.config" />
+    <None Include="Properties\App.manifest" />
+    <None Include="Properties\Resources\Texts\AppLanguageDic.ini" />
+    <None Include="Properties\Resources\Texts\GuidInfosDic.ini" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DependentUpon>Settings.settings</DependentUpon>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <BootstrapperPackage Include=".NETFramework,Version=v4.6">
+      <Visible>False</Visible>
+      <ProductName>Microsoft .NET Framework 4.6 %28x86 和 x64%29</ProductName>
+      <Install>true</Install>
+    </BootstrapperPackage>
+    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+      <Visible>False</Visible>
+      <ProductName>.NET Framework 3.5 SP1</ProductName>
+      <Install>false</Install>
+    </BootstrapperPackage>
+  </ItemGroup>
+  <ItemGroup>
+    <COMReference Include="IWshRuntimeLibrary">
+      <Guid>{F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}</Guid>
+      <VersionMajor>1</VersionMajor>
+      <VersionMinor>0</VersionMinor>
+      <Lcid>0</Lcid>
+      <WrapperTool>tlbimp</WrapperTool>
+      <Isolated>False</Isolated>
+      <EmbedInteropTypes>True</EmbedInteropTypes>
+    </COMReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Properties\AppIcon.ico" />
+    <Content Include="Properties\Resources\Texts\ShellCommonDic.xml" />
+    <Content Include="Properties\Resources\Images\About.png" />
+    <Content Include="Properties\Resources\Images\Add.png" />
+    <Content Include="Properties\Resources\Images\AddCommon.png" />
+    <Content Include="Properties\Resources\Images\AddExisting.png" />
+    <Content Include="Properties\Resources\Images\AddSeparator.png" />
+    <Content Include="Properties\Resources\Images\CustomType.png" />
+    <Content Include="Properties\Resources\Images\Delete.png" />
+    <Content Include="Properties\Resources\Images\Home.png" />
+    <Content Include="Properties\Resources\Images\MicrosoftStore.png" />
+    <Content Include="Properties\Resources\Images\NewItem.png" />
+    <Content Include="Properties\Resources\Images\Open.png" />
+    <Content Include="Properties\Resources\Images\SeparatorItem.png" />
+    <Content Include="Properties\Resources\Images\Setting.png" />
+    <Content Include="Properties\Resources\Images\Star.png" />
+    <Content Include="Properties\Resources\Images\SubItems.png" />
+    <Content Include="Properties\Resources\Images\TurnOff.png" />
+    <Content Include="Properties\Resources\Images\TurnOn.png" />
+    <Content Include="Properties\Resources\Images\Type.png" />
+    <Content Include="Properties\Resources\Images\Types.png" />
+    <Content Include="Properties\Resources\Images\Up.png" />
+    <Content Include="Properties\Resources\Images\Donate.png" />
+    <Content Include="Properties\Resources\Texts\ThirdRulesDic.xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <PropertyGroup>
+    <PreBuildEvent>
+    </PreBuildEvent>
+  </PropertyGroup>
+</Project>

+ 272 - 0
ContextMenuManager/Controls/AboutApp.cs

@@ -0,0 +1,272 @@
+using BulePointLilac.Methods;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class DonateBox : Panel
+    {
+        public DonateBox()
+        {
+            this.Dock = DockStyle.Fill;
+            this.BackColor = Color.White;
+            this.Controls.AddRange(new Control[] { lblInfo, picQR });
+        }
+
+        readonly Label lblInfo = new Label
+        {
+            Font = new Font(SystemFonts.MenuFont.FontFamily, 10F),
+            Text = AppString.Text_Donate,
+            AutoSize = true
+        };
+
+        readonly PictureBox picQR = new PictureBox
+        {
+            Image = Properties.Resources.Donate,
+            SizeMode = PictureBoxSizeMode.Zoom,
+            Size = new Size(600, 200).DpiZoom()
+        };
+
+        protected override void OnResize(EventArgs e)
+        {
+            base.OnResize(e);
+            picQR.Left = (this.Width - picQR.Width) / 2;
+            lblInfo.Left = (this.Width - lblInfo.Width) / 2;
+            picQR.Top = (this.Height - picQR.Height + lblInfo.Height) / 2;
+            lblInfo.Top = picQR.Top - lblInfo.Height * 2;
+        }
+    }
+
+    sealed class AboutAppBox : RichTextBox
+    {
+        public AboutAppBox()
+        {
+            this.ReadOnly = true;
+            this.Dock = DockStyle.Fill;
+            this.BackColor = Color.White;
+            this.BorderStyle = BorderStyle.None;
+            this.ForeColor = Color.FromArgb(60, 60, 60);
+            this.Font = new Font(SystemFonts.MenuFont.FontFamily, 11F);
+        }
+
+        const int WM_SETFOCUS = 0x0007;
+        const int WM_KILLFOCUS = 0x0008;
+        protected override void WndProc(ref Message m)
+        {
+            switch(m.Msg)
+            {
+                case WM_SETFOCUS:
+                    m.Msg = WM_KILLFOCUS; break;
+            }
+            base.WndProc(ref m);
+        }
+
+        protected override void OnLinkClicked(LinkClickedEventArgs e)
+        {
+            base.OnLinkClicked(e); Process.Start(e.LinkText);
+        }
+    }
+
+    sealed class DictionariesBox : TabControl
+    {
+        public DictionariesBox()
+        {
+            this.Dock = DockStyle.Fill;
+            this.Controls.AddRange(pages);
+            this.Font = new Font(SystemFonts.MenuFont.FontFamily, 11F);
+            cms.Items.AddRange(items);
+            for(int i = 0; i < 5; i++)
+            {
+                boxs[i] = new AboutAppBox { Parent = pages[i] };
+                if(i > 0) boxs[i].ContextMenuStrip = cms;
+            }
+            items[0].Click += (sender, e) => EditText();
+            items[2].Click += (sender, e) => SaveFile();
+        }
+
+        readonly TabPage[] pages = new TabPage[] {
+            new TabPage(AppString.Text_DictionaryDescription),
+            new TabPage(AppString.Text_LanguageDictionary),
+            new TabPage(AppString.Text_GuidInfosDictionary),
+            new TabPage(AppString.Text_ThridRulesDictionary),
+            new TabPage(AppString.Text_CommonItemsDictionary)
+        };
+        readonly AboutAppBox[] boxs = new AboutAppBox[5];
+        readonly ContextMenuStrip cms = new ContextMenuStrip();
+        readonly ToolStripItem[] items = new ToolStripItem[] {
+            new ToolStripMenuItem(AppString.Edit),
+            new ToolStripSeparator(),
+            new ToolStripMenuItem(AppString.Save)
+        };
+
+        [DllImport("user32.dll")]
+        private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
+
+        [DllImport("user32.dll")]
+        private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
+        const int WM_SETTEXT = 0x000C;
+        private void EditText()
+        {
+            Process process = Process.Start("notepad.exe");
+            Thread.Sleep(200);
+            IntPtr handle = FindWindowEx(process.MainWindowHandle, IntPtr.Zero, "Edit", null);
+            SendMessage(handle, WM_SETTEXT, 0, GetInitialText());
+        }
+
+        private void SaveFile()
+        {
+            using(SaveFileDialog dlg = new SaveFileDialog())
+            {
+                string dirPath = Program.ConfigDir;
+                switch(SelectedIndex)
+                {
+                    case 1:
+                        dirPath = Program.LanguagesDir;
+                        dlg.FileName = Program.ZH_CNINI;
+                        break;
+                    case 2:
+                        dlg.FileName = Program.GUIDINFOSINI;
+                        break;
+                    case 3:
+                        dlg.FileName = Program.ThIRDRULESDICXML;
+                        break;
+                    case 4:
+                        dlg.FileName = Program.SHELLCOMMONDICXML;
+                        break;
+                }
+                dlg.Filter = $"{dlg.FileName}|*{Path.GetExtension(dlg.FileName)}";
+                Directory.CreateDirectory(dirPath);
+                dlg.InitialDirectory = dirPath;
+                if(dlg.ShowDialog() == DialogResult.OK)
+                {
+                    File.WriteAllText(dlg.FileName, GetInitialText(), Encoding.UTF8);
+                }
+            }
+        }
+
+        private string GetInitialText()
+        {
+            switch(this.SelectedIndex)
+            {
+                case 1:
+                    return Properties.Resources.AppLanguageDic;
+                case 2:
+                    return Properties.Resources.GuidInfosDic;
+                case 3:
+                    return Properties.Resources.ThirdRulesDic;
+                case 4:
+                    return Properties.Resources.ShellCommonDic;
+                default:
+                    return string.Empty;
+            }
+        }
+
+        bool hadLoaded = false;
+        public async void LoadTexts()
+        {
+            if(hadLoaded) return;
+            hadLoaded = true;
+            boxs[0].Text = AppString.Text_Dictionaries;
+            await Task.Run(() => boxs[1].BeginInvoke(new Action<string>(boxs[1].LoadIni), new[] { Properties.Resources.AppLanguageDic }));
+            await Task.Run(() => boxs[2].BeginInvoke(new Action<string>(boxs[2].LoadIni), new[] { Properties.Resources.GuidInfosDic }));
+            await Task.Run(() => boxs[3].BeginInvoke(new Action<string>(boxs[3].LoadXml), new[] { Properties.Resources.ThirdRulesDic }));
+            await Task.Run(() => boxs[4].BeginInvoke(new Action<string>(boxs[4].LoadXml), new[] { Properties.Resources.ShellCommonDic }));
+        }
+    }
+
+    sealed class LanguagesBox : Panel
+    {
+        const string OtherLanguagesUrl = "https://gitee.com/BluePointLilac/ContextMenuManager/tree/Master/languages";
+
+        public LanguagesBox()
+        {
+            this.Dock = DockStyle.Fill;
+            this.Font = new Font(SystemFonts.MenuFont.FontFamily, 11F);
+            this.Controls.AddRange(new Control[] { cmbLanguages, llbOtherLanguages, txtTranslators });
+            this.OnResize(null);
+            cmbLanguages.SelectionChangeCommitted += (sender, e) => ChangeLanguage();
+            llbOtherLanguages.LinkClicked += (sender, e) => Process.Start(OtherLanguagesUrl);
+        }
+
+        readonly ComboBox cmbLanguages = new ComboBox
+        {
+            Width = 200.DpiZoom(),
+            DropDownStyle = ComboBoxStyle.DropDownList
+        };
+
+        readonly LinkLabel llbOtherLanguages = new LinkLabel
+        {
+            Text = AppString.Text_OtherLanguages,
+            AutoSize = true
+        };
+
+        readonly TextBox txtTranslators = new TextBox
+        {
+            ReadOnly = true,
+            Multiline = true,
+            ScrollBars = ScrollBars.Vertical
+        };
+
+        readonly List<string> iniPaths = new List<string>();
+
+        int selectIndex = 0;
+
+        protected override void OnResize(EventArgs e)
+        {
+            base.OnResize(e);
+            int a = 20.DpiZoom();
+            txtTranslators.Left = cmbLanguages.Left = cmbLanguages.Top = llbOtherLanguages.Top = a;
+            txtTranslators.Top = cmbLanguages.Bottom + a;
+            txtTranslators.Width = this.ClientSize.Width - 2 * a;
+            txtTranslators.Height = this.ClientSize.Height - txtTranslators.Top - a;
+            txtTranslators.BackColor = this.BackColor;
+            llbOtherLanguages.Left = txtTranslators.Right - llbOtherLanguages.Width;
+        }
+
+        public void LoadLanguages()
+        {
+            cmbLanguages.Items.Clear();
+            cmbLanguages.Items.Add("(默认) 简体中文");
+            cmbLanguages.SelectedIndex = 0;
+            string str = AppString.Text_Translators + Environment.NewLine;
+            DirectoryInfo di = new DirectoryInfo(Program.LanguagesDir);
+            if(di.Exists)
+            {
+                iniPaths.Clear();
+                FileInfo[] fis = di.GetFiles();
+                for(int i = 0; i < fis.Length; i++)
+                {
+                    FileInfo fi = fis[i];
+                    IniReader reader = new IniReader(fi.FullName);
+                    string name = reader.GetValue("General", "Language");
+                    string translator = reader.GetValue("General", "Translator");
+                    cmbLanguages.Items.Add(name);
+                    str += Environment.NewLine + name + "\t\t" + translator;
+                    iniPaths.Add(fi.FullName);
+                    if(fi.FullName.Equals(Program.LanguageFilePath, StringComparison.OrdinalIgnoreCase))
+                        cmbLanguages.SelectedIndex = i + 1;
+                }
+                txtTranslators.Text = str;
+            }
+            selectIndex = cmbLanguages.SelectedIndex;
+        }
+
+        private void ChangeLanguage()
+        {
+            if(cmbLanguages.SelectedIndex == selectIndex) return;
+            string path = "default";
+            if(cmbLanguages.SelectedIndex > 0) path = iniPaths[cmbLanguages.SelectedIndex - 1];
+            new IniFileHelper(Program.ConfigIniPath).SetValue("General", "Language", path);
+            MessageBoxEx.Show(AppString.MessageBox_RestartApp, MessageBoxButtons.OK, MessageBoxIcon.Information);
+            Application.Restart();
+        }
+    }
+}

+ 12 - 0
ContextMenuManager/Controls/AddCommonButton.cs

@@ -0,0 +1,12 @@
+using BulePointLilac.Controls;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class AddCommonButton : PictureButton
+    {
+        public AddCommonButton() : base(AppImage.AddCommon)
+        {
+            MyToolTip.SetToolTip(this, AppString.Tip_AddCommonItems);
+        }
+    }
+}

+ 63 - 0
ContextMenuManager/Controls/ExplorerRestarter.cs

@@ -0,0 +1,63 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System;
+using System.Diagnostics;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    public sealed class ExplorerRestarter : MyListItem
+    {
+        public ExplorerRestarter()
+        {
+            this.Visible = false;
+            this.Dock = DockStyle.Bottom;
+            this.Image = AppImage.Explorer;
+            this.Text = AppString.Text_RestartExplorer;
+            MyToolTip.SetToolTip(BtnRestart, AppString.Tip_RestartExplorer);
+            this.AddCtr(BtnRestart);
+            this.CanMoveForm();
+            BtnRestart.MouseDown += (sender, e) => { Explorer.ReStart(); this.Visible = false; };
+            RestartHandler += (sender, e) => this.Visible = NeedRestart;
+        }
+
+        protected override void OnVisibleChanged(EventArgs e)
+        {
+            base.OnVisibleChanged(e);
+            if(this.Parent != null) this.Parent.Height += Visible ? Height : -Height;
+        }
+
+        private readonly PictureButton BtnRestart = new PictureButton(AppImage.Refresh);
+
+        private static event EventHandler RestartHandler;
+
+        private static bool needRestart;
+        public static bool NeedRestart
+        {
+            get => needRestart;
+            set
+            {
+                needRestart = value;
+                RestartHandler?.Invoke(null, null);
+            }
+        }
+    }
+
+    public static class Explorer
+    {
+        /// <summary>重启Explorer</summary>
+        public static void ReStart()
+        {
+            new Process
+            {
+                StartInfo = new ProcessStartInfo
+                {
+                    FileName = "cmd.exe",
+                    Arguments = "/s /c tskill explorer",
+                    WindowStyle = ProcessWindowStyle.Hidden,
+                    UseShellExecute = true
+                }
+            }.Start();
+        }
+    }
+}

+ 137 - 0
ContextMenuManager/Controls/FileExtensionDialog.cs

@@ -0,0 +1,137 @@
+using BulePointLilac.Methods;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class FileExtensionDialog : CommonDialog
+    {
+        const string FileExtsPath = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts";
+        public string Extension { get; private set; }
+        public override void Reset() { }
+
+        protected override bool RunDialog(IntPtr hwndOwner)
+        {
+            using(FileExtensionForm frm = new FileExtensionForm())
+            {
+                bool flag = frm.ShowDialog() == DialogResult.OK;
+                if(flag) this.Extension = frm.Extension;
+                return flag;
+            }
+        }
+
+        public static string GetTypeName(string extension, bool includeUWP = true)
+        {
+            using(var root = Microsoft.Win32.Registry.ClassesRoot)
+            {
+                bool TypeNameExists(string typeName)
+                {
+                    if(!string.IsNullOrEmpty(typeName))
+                        using(var typeKey = root.OpenSubKey(typeName))
+                            if(typeKey != null) return true;
+                    return false;
+                }
+
+                using(var extKey = root.OpenSubKey(extension))
+                {
+                    if(extKey == null) return null;
+                    string defaultType = extKey.GetValue("")?.ToString();
+                    using(var key = extKey.OpenSubKey("OpenWithProgids"))
+                    {
+                        if(key == null)
+                        {
+                            if(TypeNameExists(defaultType)) return defaultType;
+                            else return null;
+                        }
+                        foreach(string valueName in key.GetValueNames())
+                        {
+                            if(!includeUWP && key.GetValueKind(valueName) != Microsoft.Win32.RegistryValueKind.String)
+                            {
+                                if(TypeNameExists(defaultType)) return defaultType;
+                                continue;
+                            }
+                            if(TypeNameExists(valueName)) return valueName;
+                        }
+                    }
+                }
+            }
+            return null;
+        }
+
+        sealed class FileExtensionForm : Form
+        {
+            public FileExtensionForm()
+            {
+                this.AcceptButton = btnOk;
+                this.CancelButton = btnCancel;
+                this.Text = AppString.Text_SelectExtension;
+                this.Font = SystemFonts.MenuFont;
+                this.ShowIcon = this.ShowInTaskbar = false;
+                this.MaximizeBox = this.MinimizeBox = false;
+                this.FormBorderStyle = FormBorderStyle.FixedSingle;
+                this.StartPosition = FormStartPosition.CenterParent;
+                InitializeComponents();
+                LoadExtensions();
+                btnOk.Click += (sender, e) =>
+                {
+                    if(cmbExtension.Items.Contains(cmbExtension.Text))
+                    {
+                        this.Extension = $".{cmbExtension.Text}";
+                        this.DialogResult = DialogResult.OK;
+                    }
+                    else
+                    {
+                        MessageBoxEx.Show(AppString.MessageBox_UnsupportedExtension);
+                        cmbExtension.Focus();
+                    }
+                };
+            }
+
+            public string Extension { get; private set; }
+
+            readonly ComboBox cmbExtension = new ComboBox
+            {
+                AutoCompleteMode = AutoCompleteMode.SuggestAppend,
+                AutoCompleteSource = AutoCompleteSource.ListItems,
+                DropDownHeight = 294.DpiZoom()
+            };
+            readonly Button btnOk = new Button
+            {
+                Text = AppString.Ok,
+                AutoSize = true
+            };
+            readonly Button btnCancel = new Button
+            {
+                DialogResult = DialogResult.Cancel,
+                Text = AppString.Cancel,
+                AutoSize = true
+            };
+
+            private void InitializeComponents()
+            {
+                this.ClientSize = new Size(316, 110).DpiZoom();
+                this.Controls.AddRange(new Control[] { cmbExtension, btnOk, btnCancel });
+                int a = 20.DpiZoom();
+                cmbExtension.Left = a;
+                cmbExtension.Width = 85.DpiZoom();
+                cmbExtension.Top = btnOk.Top = btnCancel.Top = 2 * a;
+                btnOk.Left = cmbExtension.Right + a;
+                btnCancel.Left = btnOk.Right + a;
+            }
+
+            private void LoadExtensions()
+            {
+                using(var extKey = RegistryEx.GetRegistryKey(FileExtsPath))
+                {
+                    if(extKey == null) return;
+                    foreach(string extension in extKey.GetSubKeyNames())
+                    {
+                        if(!extension.StartsWith(".") || GetTypeName(extension) == null) continue;
+                        cmbExtension.Items.Add(extension.Substring(1));
+                    }
+                }
+            }
+        }
+    }
+}

+ 65 - 0
ContextMenuManager/Controls/GuidBlockedItem.cs

@@ -0,0 +1,65 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    class GuidBlockedItem : MyListItem, IBtnDeleteItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiRegPathItem
+    {
+        const string HKLMBLOCKED = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked";
+        const string HKCUBLOCKED = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Blocked";
+        public static readonly string[] BlockedPaths = { HKLMBLOCKED, HKCUBLOCKED };
+
+        public GuidBlockedItem(Guid guid, string guidPath)
+        {
+            InitializeComponents();
+            this.Guid = guid;
+            this.RegPath = guidPath;
+        }
+
+        private Guid guid;
+        public Guid Guid
+        {
+            get => guid;
+            set
+            {
+                guid = value;
+                this.Text = $"{GuidInfo.GetText(value)}\n{value}";
+                this.Image = GuidInfo.GetImage(value);
+            }
+        }
+
+        public DeleteButton BtnDelete { get; set; }
+        public ObjectPathButton BtnOpenPath { get; set; }
+
+        public string SearchText => Guid.ToString();
+        public string ItemFilePath => GuidInfo.GetFilePath(Guid);
+
+        public WebSearchMenuItem TsiSearch { get; set; }
+        public FileLocationMenuItem TsiFileLocation { get; set; }
+        public FilePropertiesMenuItem TsiFileProperties { get; set; }
+        public RegLocationMenuItem TsiRegLocation { get; set; }
+        public string RegPath { get; set; }
+
+        private void InitializeComponents()
+        {
+            BtnDelete = new DeleteButton(this);
+            ContextMenuStrip = new ContextMenuStrip();
+            TsiSearch = new WebSearchMenuItem(this);
+            TsiFileProperties = new FilePropertiesMenuItem(this);
+            TsiFileLocation = new FileLocationMenuItem(this);
+            TsiRegLocation = new RegLocationMenuItem(this);
+
+            ContextMenuStrip.Items.AddRange(new ToolStripItem[] {TsiSearch, new ToolStripSeparator(),
+                TsiFileProperties, TsiFileLocation, TsiRegLocation });
+        }
+
+        public void DeleteMe()
+        {
+            Array.ForEach(BlockedPaths, path => { RegistryEx.DeleteValue(path, Guid.ToString("B")); });
+            ExplorerRestarter.NeedRestart = true;
+            this.Dispose();
+        }
+    }
+}

+ 67 - 0
ContextMenuManager/Controls/GuidBlockedList.cs

@@ -0,0 +1,67 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class GuidBlockedList : MyList
+    {
+        public void LoadItems()
+        {
+            this.ClearItems();
+            this.AddNewItem();
+            this.LoadCommonItems();
+        }
+
+        private void LoadCommonItems()
+        {
+            List<string> values = new List<string>();
+            Array.ForEach(GuidBlockedItem.BlockedPaths, path =>
+            {
+                using(RegistryKey key = RegistryEx.GetRegistryKey(path))
+                    if(key != null) values.AddRange(key.GetValueNames());
+            });
+            Array.ForEach(values.Distinct(StringComparer.OrdinalIgnoreCase).ToArray(), value =>
+            {
+                if(GuidInfo.TryGetGuid(value, out Guid guid, out string guidPath))
+                    this.AddItem(new GuidBlockedItem(guid, guidPath));
+            });
+        }
+
+        private void AddNewItem()
+        {
+            NewItem newItem = new NewItem { Text = AppString.Text_NewGuidBlockedItem };
+            this.AddItem(newItem);
+            newItem.NewItemAdd += (sender, e) =>
+            {
+                using(InputDialog dlg = new InputDialog { Title = AppString.Text_InputGuid })
+                {
+                    if(GuidInfo.TryGetGuid(Clipboard.GetText(), out Guid guid)) dlg.Text = guid.ToString();
+                    if(dlg.ShowDialog() != DialogResult.OK) return;
+                    if(GuidInfo.TryGetGuid(dlg.Text, out guid, out string guidPath))
+                    {
+                        Array.ForEach(GuidBlockedItem.BlockedPaths, path =>
+                        {
+                            Registry.SetValue(path, guid.ToString("B"), string.Empty);
+                        });
+                        for(int i = 1; i < Controls.Count; i++)
+                        {
+                            if(((GuidBlockedItem)Controls[i]).Guid.Equals(guid))
+                            {
+                                MessageBoxEx.Show(AppString.MessageBox_HasBeenAdded);
+                                return;
+                            }
+                        }
+                        this.InsertItem(new GuidBlockedItem(guid, guidPath), 1);
+                        ExplorerRestarter.NeedRestart = true;
+                    }
+                    else MessageBoxEx.Show(AppString.MessageBox_UnknownGuid);
+                }
+            };
+        }
+    }
+}

+ 26 - 0
ContextMenuManager/Controls/Interfaces/IBtnDeleteItem.cs

@@ -0,0 +1,26 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    interface IBtnDeleteItem
+    {
+        DeleteButton BtnDelete { get; set; }
+        void DeleteMe();
+    }
+
+    sealed class DeleteButton : PictureButton
+    {
+        public DeleteButton(IBtnDeleteItem item) : base(AppImage.Delete)
+        {
+            ((MyListItem)item).AddCtr(this);
+            this.MouseDown += (sender, e) =>
+            {
+                if(MessageBoxEx.Show(AppString.MessageBox_ConfirmDelete,
+                MessageBoxButtons.YesNo) == DialogResult.Yes)
+                    item.DeleteMe();
+            };
+        }
+    }
+}

+ 19 - 0
ContextMenuManager/Controls/Interfaces/IBtnMoveUpDownItem.cs

@@ -0,0 +1,19 @@
+using BulePointLilac.Controls;
+
+namespace ContextMenuManager.Controls
+{
+    interface IBtnMoveUpDownItem
+    {
+        MoveButton BtnMoveUp { get; set; }
+        MoveButton BtnMoveDown { get; set; }
+    }
+
+    sealed class MoveButton : PictureButton
+    {
+        public MoveButton(IBtnMoveUpDownItem item, bool isUp) : base(isUp ? AppImage.Up : AppImage.Down)
+        {
+            ((MyListItem)item).AddCtr(this);
+            ((MyListItem)item).SetCtrIndex(this, isUp ? 2 : 1);
+        }
+    }
+}

+ 21 - 0
ContextMenuManager/Controls/Interfaces/IBtnOpenPathItem.cs

@@ -0,0 +1,21 @@
+using BulePointLilac.Controls;
+using static BulePointLilac.Methods.ObjectPath;
+
+namespace ContextMenuManager.Controls
+{
+    interface IBtnOpenPathItem
+    {
+        string TargetPath { get; set; }
+        PathType PathType { get; set; }
+        ObjectPathButton BtnOpenPath { get; set; }
+    }
+
+    sealed class ObjectPathButton : PictureButton
+    {
+        public ObjectPathButton(IBtnOpenPathItem item) : base(AppImage.Open)
+        {
+            ((MyListItem)item).AddCtr(this);
+            this.MouseDown += (sender, e) => ShowPath(item.TargetPath, item.PathType);
+        }
+    }
+}

+ 21 - 0
ContextMenuManager/Controls/Interfaces/IBtnShowMenuItem.cs

@@ -0,0 +1,21 @@
+using BulePointLilac.Controls;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    interface IBtnShowMenuItem
+    {
+        ContextMenuStrip ContextMenuStrip { get; set; }
+        MenuButton BtnShowMenu { get; set; }
+    }
+
+    sealed class MenuButton : PictureButton
+    {
+        public MenuButton(IBtnShowMenuItem item) : base(AppImage.Setting)
+        {
+            item.ContextMenuStrip = new ContextMenuStrip();
+            ((MyListItem)item).AddCtr(this);
+            this.MouseDown += (sender, e) => item.ContextMenuStrip.Show(this, 0, Height);
+        }
+    }
+}

+ 27 - 0
ContextMenuManager/Controls/Interfaces/IChkVisibleItem.cs

@@ -0,0 +1,27 @@
+using BulePointLilac.Controls;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    interface IChkVisibleItem
+    {
+        bool ItemVisible { get; set; }
+        VisibleCheckBox ChkVisible { get; set; }
+    }
+
+    sealed class VisibleCheckBox : MyCheckBox
+    {
+        public VisibleCheckBox(IChkVisibleItem item)
+        {
+            ((MyListItem)item).AddCtr(this);
+            this.MouseDown += (sender, e) =>
+            {
+                if(e.Button == MouseButtons.Left)
+                {
+                    item.ItemVisible = !this.Checked;
+                    this.Checked = item.ItemVisible;
+                }
+            };
+        }
+    }
+}

+ 72 - 0
ContextMenuManager/Controls/Interfaces/IFoldGroupItem.cs

@@ -0,0 +1,72 @@
+using BulePointLilac.Controls;
+using System.Drawing;
+using System.Windows.Forms;
+using static BulePointLilac.Methods.ObjectPath;
+
+namespace ContextMenuManager.Controls
+{
+    interface IFoldGroupItem
+    {
+        FoldButton BtnFold { get; set; }
+        bool IsFold { get; set; }
+    }
+
+    interface IFoldSubItem
+    {
+        IFoldGroupItem FoldGroupItem { get; set; }
+    }
+
+    sealed class FoldButton : PictureButton
+    {
+        private bool isFold;
+        public bool IsFold
+        {
+            get => isFold;
+            set
+            {
+                isFold = value;
+                this.BaseImage = ReplaceImage(value);
+                Control list = ((MyListItem)FoldGroup).Parent;
+                if(list == null) return;
+                foreach(Control ctr in list.Controls)
+                {
+                    if(ctr is IFoldSubItem item && item.FoldGroupItem == FoldGroup) ctr.Visible = !value;
+                }
+            }
+        }
+
+        private IFoldGroupItem FoldGroup { get; set; }
+
+        static Image ReplaceImage(bool fold) => fold ? AppImage.Up : AppImage.Down;
+
+        public FoldButton(IFoldGroupItem owner, bool fold = false) : base(ReplaceImage(fold))
+        {
+            this.FoldGroup = owner;
+            ((MyListItem)owner).AddCtr(this);
+            this.MouseDown += (sender, e) =>
+            {
+                this.IsFold = !this.IsFold;
+                this.Image = this.BaseImage;
+            };
+        }
+    }
+
+    sealed class GroupPathItem : MyListItem, IFoldGroupItem, IBtnOpenPathItem
+    {
+        public bool IsFold
+        {
+            get => BtnFold.IsFold;
+            set => BtnFold.IsFold = value;
+        }
+        public string TargetPath { get; set; }
+        public PathType PathType { get; set; }
+        public ObjectPathButton BtnOpenPath { get; set; }
+        public FoldButton BtnFold { get; set; }
+
+        public GroupPathItem()
+        {
+            BtnFold = new FoldButton(this);
+            BtnOpenPath = new ObjectPathButton(this);
+        }
+    }
+}

+ 38 - 0
ContextMenuManager/Controls/Interfaces/ITsiCommandItem.cs

@@ -0,0 +1,38 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    interface ITsiCommandItem
+    {
+        string ItemCommand { get; set; }
+        ChangeCommandMenuItem TsiChangeCommand { get; set; }
+    }
+
+    sealed class ChangeCommandMenuItem : ToolStripMenuItem
+    {
+        public ChangeCommandMenuItem(ITsiCommandItem item) : base(AppString.Menu_ChangeCommand)
+        {
+            this.Click += (sender, e) =>
+            {
+                string command = ChangeCommand(item.ItemCommand);
+                if(command != null) item.ItemCommand = command;
+            };
+        }
+
+        public static string ChangeCommand(string command)
+        {
+            using(InputDialog dlg = new InputDialog { Text = command, Title = AppString.Menu_ChangeCommand })
+            {
+                if(dlg.ShowDialog() != DialogResult.OK) return null;
+                if(string.IsNullOrEmpty(dlg.Text))
+                {
+                    MessageBoxEx.Show(AppString.MessageBox_CommandCannotBeEmpty);
+                    return ChangeCommand(command);
+                }
+                else return dlg.Text;
+            }
+        }
+    }
+}

+ 24 - 0
ContextMenuManager/Controls/Interfaces/ITsiDeleteItem.cs

@@ -0,0 +1,24 @@
+using BulePointLilac.Methods;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    interface ITsiDeleteItem
+    {
+        DeleteMeMenuItem TsiDeleteMe { get; set; }
+        void DeleteMe();
+    }
+
+    sealed class DeleteMeMenuItem : ToolStripMenuItem
+    {
+        public DeleteMeMenuItem(ITsiDeleteItem item) : base(AppString.Menu_Delete)
+        {
+            this.Click += (sender, e) =>
+            {
+                if(MessageBoxEx.Show(AppString.MessageBox_ConfirmDeletePermanently,
+                    MessageBoxButtons.YesNo) == DialogResult.Yes)
+                    item.DeleteMe();
+            };
+        }
+    }
+}

+ 41 - 0
ContextMenuManager/Controls/Interfaces/ITsiFilePathItem.cs

@@ -0,0 +1,41 @@
+using BulePointLilac.Methods;
+using System.IO;
+using System.Windows.Forms;
+using static BulePointLilac.Methods.ObjectPath;
+
+namespace ContextMenuManager.Controls
+{
+    interface ITsiFilePathItem
+    {
+        string ItemFilePath { get; }
+        ContextMenuStrip ContextMenuStrip { get; set; }
+        FileLocationMenuItem TsiFileLocation { get; set; }
+        FilePropertiesMenuItem TsiFileProperties { get; set; }
+    }
+
+    sealed class FileLocationMenuItem : ToolStripMenuItem
+    {
+        public FileLocationMenuItem(ITsiFilePathItem item) : base(AppString.Menu_FileLocation)
+        {
+            bool FileExists() => File.Exists(item.ItemFilePath);
+            bool DirExists() => Directory.Exists(item.ItemFilePath);
+            item.ContextMenuStrip.Opening += (sender, e)
+                => this.Visible = FileExists() || DirExists();
+            this.Click += (sender, e) =>
+            {
+                if(FileExists()) ShowPath(item.ItemFilePath, PathType.File);
+                else if(DirExists()) ShowPath(item.ItemFilePath, PathType.Directory);
+            };
+        }
+    }
+
+    sealed class FilePropertiesMenuItem : ToolStripMenuItem
+    {
+        public FilePropertiesMenuItem(ITsiFilePathItem item) : base(AppString.Menu_FileProperties)
+        {
+            item.ContextMenuStrip.Opening += (sender, e)
+                => this.Visible = File.Exists(item.ItemFilePath) || Directory.Exists(item.ItemFilePath);
+            this.Click += (sender, e) => PropertiesDialog.Show(item.ItemFilePath);
+        }
+    }
+}

+ 48 - 0
ContextMenuManager/Controls/Interfaces/ITsiIconItem.cs

@@ -0,0 +1,48 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    interface ITsiIconItem
+    {
+        ChangeIconMenuItem TsiChangeIcon { get; set; }
+        string IconLocation { get; set; }
+        string IconPath { get; set; }
+        int IconIndex { get; set; }
+        Image Image { get; set; }
+        Icon ItemIcon { get; }
+    }
+
+    sealed class ChangeIconMenuItem : ToolStripMenuItem
+    {
+        public ChangeIconMenuItem(ITsiIconItem item) : base(AppString.Menu_ChangeIcon)
+        {
+            this.Click += (sender, e) =>
+            {
+                string iconPath = item.IconPath;
+                int iconIndex = item.IconIndex;
+                using(Icon icon = ChangeIcon(ref iconPath, ref iconIndex))
+                {
+                    if(icon == null) return;
+                    item.IconPath = iconPath;
+                    item.IconIndex = iconIndex;
+                    item.IconLocation = $"{iconPath},{iconIndex}";
+                    item.Image = icon.ToBitmap();
+                }
+            };
+        }
+
+        public static Icon ChangeIcon(ref string iconPath, ref int iconIndex)
+        {
+            using(IconDialog dlg = new IconDialog { IconPath = iconPath, IconIndex = iconIndex })
+            {
+                if(dlg.ShowDialog() != DialogResult.OK) return null;
+                iconPath = dlg.IconPath;
+                iconIndex = dlg.IconIndex;
+            }
+            return ResourceIcon.GetIcon(iconPath, iconIndex);
+        }
+    }
+}

+ 27 - 0
ContextMenuManager/Controls/Interfaces/ITsiRegPathItem.cs

@@ -0,0 +1,27 @@
+using BulePointLilac.Methods;
+using System.Windows.Forms;
+using static BulePointLilac.Methods.ObjectPath;
+
+namespace ContextMenuManager.Controls
+{
+    interface ITsiRegPathItem
+    {
+        string RegPath { get; set; }
+        ContextMenuStrip ContextMenuStrip { get; set; }
+        RegLocationMenuItem TsiRegLocation { get; set; }
+    }
+
+    sealed class RegLocationMenuItem : ToolStripMenuItem
+    {
+
+        public RegLocationMenuItem(ITsiRegPathItem item) : base(AppString.Menu_RegistryLocation)
+        {
+            this.Click += (sender, e) => ShowPath(item.RegPath, PathType.Registry);
+            item.ContextMenuStrip.Opening += (sender, e) =>
+            {
+                using(var key = RegistryEx.GetRegistryKey(item.RegPath))
+                    this.Visible = key != null;
+            };
+        }
+    }
+}

+ 46 - 0
ContextMenuManager/Controls/Interfaces/ITsiTextItem.cs

@@ -0,0 +1,46 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    interface ITsiTextItem
+    {
+        string Text { get; set; }
+        string ItemText { get; set; }
+        ChangeTextMenuItem TsiChangeText { get; set; }
+    }
+
+    sealed class ChangeTextMenuItem : ToolStripMenuItem
+    {
+        public ChangeTextMenuItem(ITsiTextItem item) : base(AppString.Menu_ChangeText)
+        {
+            this.Click += (sender, e) =>
+            {
+                string name = ChangeText(item.Text);
+                if(name == null) return;
+                item.ItemText = name;
+                item.Text = ResourceString.GetDirectString(item.ItemText);
+            };
+        }
+
+        public static string ChangeText(string text)
+        {
+            using(InputDialog dlg = new InputDialog { Text = text, Title = AppString.Menu_ChangeText })
+            {
+                if(dlg.ShowDialog() != DialogResult.OK) return null;
+                if(dlg.Text.Length == 0)
+                {
+                    MessageBoxEx.Show(AppString.MessageBox_TextCannotBeEmpty);
+                    return ChangeText(text);
+                }
+                else if(ResourceString.GetDirectString(dlg.Text).Length == 0)
+                {
+                    MessageBoxEx.Show(AppString.MessageBox_StringParsingFailed);
+                    return ChangeText(text);
+                }
+                else return dlg.Text;
+            }
+        }
+    }
+}

+ 28 - 0
ContextMenuManager/Controls/Interfaces/ITsiWebSearchItem.cs

@@ -0,0 +1,28 @@
+using System.Diagnostics;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    interface ITsiWebSearchItem
+    {
+        string SearchText { get; }
+        WebSearchMenuItem TsiSearch { get; set; }
+    }
+
+    sealed class WebSearchMenuItem : ToolStripMenuItem
+    {
+        public static string EnginePath = "https://www.baidu.com/s?wd=";
+
+        public WebSearchMenuItem(ITsiWebSearchItem item) : base(AppString.Menu_WebSearch)
+        {
+            this.Click += (sender, e) => WebSearch(item.SearchText);
+        }
+
+        public static void WebSearch(string text)
+        {
+            //替换网址转义符
+            text = text.Replace("%", "%25").Replace("#", "%23").Replace("&", "%26").Replace("+", "%2B");
+            Process.Start(EnginePath + text);
+        }
+    }
+}

+ 19 - 0
ContextMenuManager/Controls/NewItem.cs

@@ -0,0 +1,19 @@
+using BulePointLilac.Controls;
+using System;
+
+namespace ContextMenuManager.Controls
+{
+    class NewItem : MyListItem
+    {
+        public NewItem()
+        {
+            this.Image = AppImage.NewItem;
+            this.Text = AppString.Text_NewItem;
+            this.AddCtr(BtnAddNewItem);
+            MyToolTip.SetToolTip(BtnAddNewItem, AppString.Text_NewItem);
+            BtnAddNewItem.MouseDown += (sender, e) => NewItemAdd?.Invoke(null, null);
+        }
+        public event EventHandler NewItemAdd;
+        readonly PictureButton BtnAddNewItem = new PictureButton(AppImage.AddNewItem);
+    }
+}

+ 81 - 0
ContextMenuManager/Controls/NewItemForm.cs

@@ -0,0 +1,81 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    class NewItemForm : ResizbleForm
+    {
+        public NewItemForm()
+        {
+            this.AcceptButton = btnOk;
+            this.CancelButton = btnCancel;
+            this.Font = SystemFonts.MenuFont;
+            this.MaximizeBox = this.MinimizeBox = false;
+            this.ShowIcon = this.ShowInTaskbar = false;
+            this.StartPosition = FormStartPosition.CenterParent;
+            this.SizeGripStyle = SizeGripStyle.Hide;
+            this.VerticalResizable = false;
+            InitializeComponents();
+        }
+
+        public string ItemText { get => txtText.Text; set => txtText.Text = value; }
+        public string ItemCommand { get => txtCommand.Text; set => txtCommand.Text = value; }
+
+        protected readonly Label lblName = new Label
+        {
+            Text = AppString.Text_ItemName,
+            AutoSize = true
+        };
+        protected readonly Label lblCommand = new Label
+        {
+            Text = AppString.Text_ItemCommand,
+            AutoSize = true
+        };
+        protected readonly TextBox txtText = new TextBox();
+        protected readonly TextBox txtCommand = new TextBox();
+        protected readonly Button btnBrowse = new Button
+        {
+            Text = AppString.Browse,
+            AutoSize = true
+        };
+        protected readonly Button btnOk = new Button
+        {
+            Text = AppString.Ok,
+            AutoSize = true
+        };
+        protected readonly Button btnCancel = new Button
+        {
+            DialogResult = DialogResult.Cancel,
+            Text = AppString.Cancel,
+            AutoSize = true
+        };
+
+        private static Size LastSize = new Size();
+
+        protected virtual void InitializeComponents()
+        {
+            this.Controls.AddRange(new Control[] { lblName, lblCommand, txtText, txtCommand, btnBrowse, btnOk, btnCancel });
+            int a = 20.DpiZoom();
+            btnBrowse.Anchor = btnOk.Anchor = btnCancel.Anchor = AnchorStyles.Right | AnchorStyles.Top;
+            txtText.Top = lblName.Top = lblName.Left = lblCommand.Left = a;
+            btnBrowse.Top = txtCommand.Top = lblCommand.Top = txtText.Bottom + a;
+            btnOk.Top = btnCancel.Top = btnBrowse.Bottom + a;
+            btnCancel.Left = btnBrowse.Left = this.ClientSize.Width - btnCancel.Width - a;
+            btnOk.Left = btnCancel.Left - btnOk.Width - a;
+            int b = Math.Max(lblName.Width, lblCommand.Width) + btnBrowse.Width + 4 * a;
+            this.ClientSize = new Size(250.DpiZoom() + b, btnOk.Bottom + a);
+            this.MinimumSize = this.Size;
+            this.Resize += (sender, e) =>
+            {
+                txtText.Width = txtCommand.Width = this.ClientSize.Width - b;
+                txtText.Left = txtCommand.Left = btnBrowse.Left - txtCommand.Width - a;
+                LastSize = this.Size;
+            };
+            if(LastSize != null) this.Size = LastSize;
+            this.OnResize(null);
+        }
+    }
+}

+ 87 - 0
ContextMenuManager/Controls/NewOpenWithDialog.cs

@@ -0,0 +1,87 @@
+using BulePointLilac.Methods;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class NewOpenWithDialog : CommonDialog
+    {
+        public string RegPath { get; private set; }
+        public override void Reset() { }
+
+        protected override bool RunDialog(IntPtr hwndOwner)
+        {
+            using(NewOpenWithForm frm = new NewOpenWithForm())
+            {
+                bool flag = frm.ShowDialog() == DialogResult.OK;
+                if(flag) this.RegPath = frm.RegPath;
+                return flag;
+            }
+        }
+
+        sealed class NewOpenWithForm : NewItemForm
+        {
+            public string RegPath { get; private set; }
+
+            private string FilePath;
+            private string AppRegPath;
+
+            protected override void InitializeComponents()
+            {
+                base.InitializeComponents();
+                this.Text = AppString.Text_NewOpenWithItem;
+                btnBrowse.Click += (sender, e) => BrowseFile();
+                btnOk.Click += (sender, e) =>
+                {
+                    if(string.IsNullOrEmpty(ItemText))
+                    {
+                        MessageBoxEx.Show(AppString.MessageBox_TextCannotBeEmpty);
+                        return;
+                    }
+                    if(string.IsNullOrWhiteSpace(ItemCommand))
+                    {
+                        MessageBoxEx.Show(AppString.MessageBox_TextCannotBeEmpty);
+                        return;
+                    }
+                    FilePath = ObjectPath.ExtractFilePath(ItemCommand);
+                    AppRegPath = $@"HKEY_CLASSES_ROOT\Applications\{Path.GetFileName(FilePath)}";
+                    if(FilePath == null || RegistryEx.GetRegistryKey(AppRegPath) != null)
+                    {
+                        MessageBoxEx.Show(AppString.MessageBox_UnsupportedFilename);
+                        return;
+                    }
+                    AddNewItem();
+                    this.DialogResult = DialogResult.OK;
+                };
+            }
+
+            private void BrowseFile()
+            {
+                using(OpenFileDialog dlg = new OpenFileDialog())
+                {
+                    dlg.Filter = $"{AppString.Programs}|*.exe";
+                    if(dlg.ShowDialog() == DialogResult.OK)
+                    {
+                        ItemCommand = $"\"{dlg.FileName}\" \"%1\"";
+                        ItemText = FileVersionInfo.GetVersionInfo(dlg.FileName).FileDescription;
+                    }
+                }
+            }
+
+            private void AddNewItem()
+            {
+                using(var key = RegistryEx.GetRegistryKey(AppRegPath, true, true))
+                {
+                    key.SetValue("FriendlyAppName", ItemText);
+                    using(var cmdKey = key.CreateSubKey(@"shell\open\command", true))
+                    {
+                        cmdKey.SetValue("", ItemCommand);
+                        RegPath = cmdKey.Name;
+                    }
+                }
+            }
+        }
+    }
+}

+ 116 - 0
ContextMenuManager/Controls/NewSendToDialog.cs

@@ -0,0 +1,116 @@
+using BulePointLilac.Methods;
+using System;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class NewSendToDialog : CommonDialog
+    {
+        public string FilePath { get; private set; }
+        public override void Reset() { }
+
+        protected override bool RunDialog(IntPtr hwndOwner)
+        {
+            using(NewSendToItemForm frm = new NewSendToItemForm())
+            {
+                bool flag = frm.ShowDialog() == DialogResult.OK;
+                if(flag) this.FilePath = frm.FilePath;
+                return flag;
+            }
+        }
+
+        sealed class NewSendToItemForm : NewItemForm
+        {
+            private static readonly IWshRuntimeLibrary.WshShell WshShell = new IWshRuntimeLibrary.WshShell();
+            public string FilePath { get; set; }
+
+            readonly RadioButton rdoFile = new RadioButton
+            {
+                Text = AppString.SideBar_File,
+                AutoSize = true,
+                Checked = true
+            };
+            readonly RadioButton rdoFolder = new RadioButton
+            {
+                Text = AppString.SideBar_Folder,
+                AutoSize = true
+            };
+
+            protected override void InitializeComponents()
+            {
+                base.InitializeComponents();
+                this.Text = AppString.Text_NewSendToItem;
+                this.Controls.AddRange(new[] { rdoFile, rdoFolder });
+                rdoFile.Top = rdoFolder.Top = btnOk.Top;
+                rdoFile.Left = lblCommand.Left;
+                rdoFolder.Left = rdoFile.Right + 20.DpiZoom();
+
+                btnBrowse.Click += (sender, e) =>
+                {
+                    if(rdoFile.Checked) BrowseFile();
+                    else BrowseFolder();
+                };
+
+                btnOk.Click += (sender, e) =>
+                {
+                    if(string.IsNullOrWhiteSpace(ItemText))
+                    {
+                        MessageBoxEx.Show(AppString.MessageBox_TextCannotBeEmpty);
+                        return;
+                    }
+                    if(string.IsNullOrWhiteSpace(ItemCommand))
+                    {
+                        MessageBoxEx.Show(AppString.MessageBox_CommandCannotBeEmpty);
+                        return;
+                    }
+                    if(ObjectPath.ExtractFilePath(ItemCommand) == null && !Directory.Exists(ItemCommand))
+                    {
+                        MessageBoxEx.Show(AppString.FileOrFolderNotExists);
+                        return;
+                    }
+                    AddNewItem();
+                    DialogResult = DialogResult.OK;
+                };
+            }
+
+            private void BrowseFile()
+            {
+                using(OpenFileDialog dlg = new OpenFileDialog())
+                {
+                    dlg.Filter = $"{AppString.Programs}|*.exe;*.bat;*.cmd;*.vbs;*.vbe;*.jse;*.wsf";
+                    if(dlg.ShowDialog() == DialogResult.OK)
+                    {
+                        ItemCommand = dlg.FileName;
+                        ItemText = Path.GetFileNameWithoutExtension(dlg.FileName);
+                    }
+                }
+            }
+
+            private void BrowseFolder()
+            {
+                using(FolderBrowserDialog dlg = new FolderBrowserDialog())
+                {
+                    dlg.SelectedPath = ItemCommand;
+                    if(dlg.ShowDialog() == DialogResult.OK)
+                    {
+                        ItemCommand = dlg.SelectedPath;
+                        ItemText = new DirectoryInfo(dlg.SelectedPath).Name;
+                    }
+                }
+            }
+
+            private void AddNewItem()
+            {
+                FilePath = $@"{SendToList.SendToPath}\{ObjectPath.RemoveIllegalChars(ItemText)}.lnk";
+                FilePath = ObjectPath.GetNewPathWithIndex(FilePath, ObjectPath.PathType.File);
+
+                IWshRuntimeLibrary.IWshShortcut shortcut = WshShell.CreateShortcut(FilePath);
+                shortcut.TargetPath = ItemCommand;
+                if(rdoFile.Checked) shortcut.WorkingDirectory = Path.GetDirectoryName(ItemCommand);
+                shortcut.Save();
+                DesktopIniHelper.SetLocalizedFileName(FilePath, ItemText);
+            }
+        }
+    }
+}

+ 138 - 0
ContextMenuManager/Controls/NewShellDialog.cs

@@ -0,0 +1,138 @@
+using BulePointLilac.Methods;
+using System;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class NewShellDialog : CommonDialog
+    {
+        public string ShellPath { get; set; }//传入的Shell注册表路径
+        public string ScenePath { get; set; }//菜单项所处环境注册表路径
+        public string NewItemRegPath { get; private set; }//返回的新ShellItem的注册表路径
+        public string NewItemKeyName => RegistryEx.GetKeyName(NewItemRegPath);
+
+        public override void Reset() { }
+
+        protected override bool RunDialog(IntPtr hwndOwner)
+        {
+            using(NewShellItemForm frm = new NewShellItemForm
+            {
+                ScenePath = this.ScenePath,
+                ShellPath = this.ShellPath
+            })
+            {
+                bool flag = frm.ShowDialog() == DialogResult.OK;
+                if(flag) this.NewItemRegPath = frm.NewItemRegPath;
+                return flag;
+            }
+        }
+
+        sealed class NewShellItemForm : NewItemForm
+        {
+            public string ShellPath { get; set; }
+            public string NewItemRegPath { get; private set; }//返回的新建菜单项注册表路径
+            public string ScenePath { get; set; }//菜单所处环境路径,用于判断添加后缀
+
+            readonly RadioButton rdoSingle = new RadioButton
+            {
+                Text = AppString.Text_Single,
+                AutoSize = true,
+                Checked = true
+            };
+            readonly RadioButton rdoMulti = new RadioButton
+            {
+                Text = AppString.Text_Multi,
+                AutoSize = true
+            };
+
+            static readonly string[] DirScenePaths = {
+                ShellList.MENUPATH_DIRECTORY,
+                ShellList.MENUPATH_DIRECTORY_IMAGE,
+                ShellList.MENUPATH_DIRECTORY_VIDEO,
+                ShellList.MENUPATH_DIRECTORY_AUDIO
+            };
+            static readonly string[] FileObjectsScenePaths = {
+                ShellList.MENUPATH_FILE,
+                ShellList.MENUPATH_FOLDER,
+                ShellList.MENUPATH_ALLOBJECTS,
+                ShellList.SYSFILEASSPATH,
+                ShellList.MENUPATH_UNKNOWN
+            };
+
+            protected override void InitializeComponents()
+            {
+                base.InitializeComponents();
+                this.Text = AppString.Text_NewShellItem;
+                this.Controls.AddRange(new[] { rdoSingle, rdoMulti });
+                rdoSingle.Top = rdoMulti.Top = btnOk.Top;
+                rdoSingle.Left = lblCommand.Left;
+                rdoMulti.Left = rdoSingle.Right + 20.DpiZoom();
+
+                rdoMulti.CheckedChanged += (sender, e) =>
+                {
+                    lblCommand.Enabled = txtCommand.Enabled
+                    = btnBrowse.Enabled = !rdoMulti.Checked;
+                };
+
+                btnBrowse.Click += (sender, e) => BrowseFile();
+
+                btnOk.Click += (sender, e) =>
+                {
+                    if(string.IsNullOrWhiteSpace(txtText.Text))
+                    {
+                        MessageBoxEx.Show(AppString.MessageBox_TextCannotBeEmpty);
+                    }
+                    else
+                    {
+                        AddNewItem();
+                        DialogResult = DialogResult.OK;
+                    }
+                };
+            }
+
+            private void BrowseFile()
+            {
+                using(OpenFileDialog dlg = new OpenFileDialog())
+                {
+                    dlg.Filter = $"{AppString.Programs}|*.exe;*.bat;*.cmd;*.pif;*.com";
+                    if(dlg.ShowDialog() != DialogResult.OK) return;
+                    ItemCommand = $"\"{dlg.FileName}\"";
+                    ItemText = Path.GetFileNameWithoutExtension(dlg.FileName);
+                    if(Array.FindIndex(DirScenePaths, path
+                       => ScenePath.Equals(path, StringComparison.OrdinalIgnoreCase)) != -1)
+                    {
+                        ItemCommand += " \"%V\"";//自动加目录后缀
+                    }
+                    else if(Array.FindIndex(FileObjectsScenePaths, path
+                       => ScenePath.StartsWith(path, StringComparison.OrdinalIgnoreCase)) != -1)
+                    {
+                        ItemCommand += " \"%1\"";//自动加文件对象后缀
+                    }
+                }
+            }
+
+            private void AddNewItem()
+            {
+                using(var shellKey = RegistryEx.GetRegistryKey(ShellPath, true, true))
+                {
+                    string keyName = ItemText.Replace("\\", "").Trim();
+                    NewItemRegPath = ObjectPath.GetNewPathWithIndex($@"{ShellPath}\{keyName}", ObjectPath.PathType.Registry);
+                    keyName = RegistryEx.GetKeyName(NewItemRegPath);
+
+                    using(var key = shellKey.CreateSubKey(keyName, true))
+                    {
+                        key.SetValue("MUIVerb", ItemText);
+                        if(rdoMulti.Checked)
+                            key.SetValue("SubCommands", "");
+                        else
+                        {
+                            if(!string.IsNullOrWhiteSpace(ItemCommand))
+                                key.CreateSubKey("command", true).SetValue("", ItemCommand);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 124 - 0
ContextMenuManager/Controls/OpenWithItem.cs

@@ -0,0 +1,124 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using Microsoft.Win32;
+using System;
+using System.Diagnostics;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class OpenWithItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, ITsiTextItem,
+        ITsiCommandItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiRegPathItem, ITsiDeleteItem
+    {
+
+        public OpenWithItem(string regPath)
+        {
+            InitializeComponents();
+            this.RegPath = regPath;
+        }
+
+        private string regPath;
+        public string RegPath
+        {
+            get => regPath;
+            set
+            {
+                regPath = value;
+                this.ItemFilePath = ObjectPath.ExtractFilePath(ItemCommand);
+                this.Text = this.ItemText;
+                this.Image = this.ItemIcon.ToBitmap();
+                ChkVisible.Checked = this.ItemVisible;
+            }
+        }
+
+        private string AppPath => RegistryEx.GetParentPath(RegistryEx.GetParentPath(RegistryEx.GetParentPath(RegPath)));
+        private bool NameEquals => RegistryEx.GetKeyName(AppPath).Equals(Path.GetFileName(ItemFilePath), StringComparison.OrdinalIgnoreCase);
+        private Icon ItemIcon => Icon.ExtractAssociatedIcon(ItemFilePath);
+
+        public string ItemText
+        {
+            get
+            {
+                string name = null;
+                if(NameEquals)
+                {
+                    name = Registry.GetValue(AppPath, "FriendlyAppName", null)?.ToString();
+                    name = ResourceString.GetDirectString(name);
+                }
+                if(string.IsNullOrEmpty(name)) name = FileVersionInfo.GetVersionInfo(ItemFilePath).FileDescription;
+                if(string.IsNullOrEmpty(name)) name = Path.GetFileName(ItemFilePath);
+                return name;
+            }
+            set
+            {
+                Registry.SetValue(AppPath, "FriendlyAppName", value);
+            }
+        }
+
+        public string ItemCommand
+        {
+            get => Registry.GetValue(RegPath, "", null)?.ToString();
+            set
+            {
+                if(ObjectPath.ExtractFilePath(value) != ItemFilePath)
+                {
+                    MessageBoxEx.Show(AppString.MessageBox_CannotChangePath);
+                }
+                else Registry.SetValue(RegPath, "", value);
+            }
+        }
+
+        public bool ItemVisible
+        {
+            get => Registry.GetValue(AppPath, "NoOpenWith", null) == null;
+            set
+            {
+                if(value) RegistryEx.DeleteValue(AppPath, "NoOpenWith");
+                else Registry.SetValue(AppPath, "NoOpenWith", "");
+            }
+        }
+
+        public string SearchText => $"{AppString.SideBar_OpenWith} {Text}";
+        public string ItemFilePath { get; private set; }
+
+        public VisibleCheckBox ChkVisible { get; set; }
+        public MenuButton BtnShowMenu { get; set; }
+        public ChangeTextMenuItem TsiChangeText { get; set; }
+        public ChangeCommandMenuItem TsiChangeCommand { get; set; }
+        public WebSearchMenuItem TsiSearch { get; set; }
+        public FilePropertiesMenuItem TsiFileProperties { get; set; }
+        public FileLocationMenuItem TsiFileLocation { get; set; }
+        public RegLocationMenuItem TsiRegLocation { get; set; }
+        public DeleteMeMenuItem TsiDeleteMe { get; set; }
+        readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu_Details);
+
+        private void InitializeComponents()
+        {
+            BtnShowMenu = new MenuButton(this);
+            ChkVisible = new VisibleCheckBox(this);
+            TsiChangeText = new ChangeTextMenuItem(this);
+            TsiChangeCommand = new ChangeCommandMenuItem(this);
+            TsiSearch = new WebSearchMenuItem(this);
+            TsiFileProperties = new FilePropertiesMenuItem(this);
+            TsiFileLocation = new FileLocationMenuItem(this);
+            TsiRegLocation = new RegLocationMenuItem(this);
+            TsiDeleteMe = new DeleteMeMenuItem(this);
+
+            ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText,
+                new ToolStripSeparator(), TsiDetails, new ToolStripSeparator(), TsiDeleteMe });
+
+            TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(),
+                TsiChangeCommand, TsiFileProperties, TsiFileLocation, TsiRegLocation });
+
+            ContextMenuStrip.Opening += (sender, e) => TsiChangeText.Enabled = this.NameEquals;
+        }
+
+        public void DeleteMe()
+        {
+            RegistryEx.DeleteKeyTree(this.RegPath);
+            this.Dispose();
+        }
+    }
+}

+ 68 - 0
ContextMenuManager/Controls/OpenWithList.cs

@@ -0,0 +1,68 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class OpenWithList : MyList
+    {
+        public void LoadItems()
+        {
+            this.ClearItems();
+            this.LoadCommonItems();
+            this.SortItemByText();
+            this.AddNewItem();
+            this.InsertItem(new RegRuleItem(RegRuleItem.UseStoreOpenWith) { MarginRight = RegRuleItem.SysMarginRignt }, 1);
+        }
+
+        private void LoadCommonItems()
+        {
+            using(RegistryKey appKey = Registry.ClassesRoot.OpenSubKey("Applications"))
+            {
+                foreach(string appName in appKey.GetSubKeyNames())
+                {
+                    if(!appName.Contains('.')) continue;//需要为有扩展名的文件名
+                    using(RegistryKey shellKey = appKey.OpenSubKey($@"{appName}\shell"))
+                    {
+                        if(shellKey == null) continue;
+
+                        List<string> names = shellKey.GetSubKeyNames().ToList();
+                        if(names.Contains("open", StringComparer.OrdinalIgnoreCase)) names.Insert(0, "open");
+
+                        string keyName = names.Find(name =>
+                        {
+                            using(var cmdKey = shellKey.OpenSubKey(name))
+                                return cmdKey.GetValue("NeverDefault") == null;
+                        });
+                        if(keyName == null) continue;
+
+                        using(RegistryKey commandKey = shellKey.OpenSubKey($@"{keyName}\command"))
+                        {
+                            string command = commandKey?.GetValue("")?.ToString();
+                            if(ObjectPath.ExtractFilePath(command) != null)
+                                this.AddItem(new OpenWithItem(commandKey.Name));
+                        }
+                    }
+                }
+            }
+        }
+
+        private void AddNewItem()
+        {
+            NewItem newItem = new NewItem();
+            this.InsertItem(newItem, 0);
+            newItem.NewItemAdd += (sender, e) =>
+            {
+                using(NewOpenWithDialog dlg = new NewOpenWithDialog())
+                {
+                    if(dlg.ShowDialog() == DialogResult.OK)
+                        this.InsertItem(new OpenWithItem(dlg.RegPath), 2);
+                }
+            };
+        }
+    }
+}

+ 209 - 0
ContextMenuManager/Controls/RegRuleItem.cs

@@ -0,0 +1,209 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using Microsoft.Win32;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class RegRuleItem : MyListItem, IChkVisibleItem, IFoldSubItem
+    {
+        public struct RegRule
+        {
+            public string RegPath { get; set; }
+            public string ValueName { get; set; }
+            public RegistryValueKind ValueKind { get; set; }
+            public object TurnOnValue { get; set; }
+            public object TurnOffValue { get; set; }
+            public RegRule(string regPath, string valueName, object turnOnValue,
+                object turnOffValue, RegistryValueKind valueKind = RegistryValueKind.DWord)
+            {
+                this.RegPath = regPath; this.ValueName = valueName;
+                this.TurnOnValue = turnOnValue; this.TurnOffValue = turnOffValue;
+                this.ValueKind = valueKind;
+            }
+        }
+
+        public struct ItemInfo
+        {
+            public string Text { get; set; }
+            public Image Image { get; set; }
+            public string Tip { get; set; }
+            public bool RestartExplorer { get; set; }
+        }
+
+        public struct RuleAndInfo
+        {
+            public RegRule[] Rules { get; set; }
+            public ItemInfo ItemInfo { get; set; }
+        }
+
+        private RegRuleItem(ItemInfo info)
+        {
+            this.Text = info.Text;
+            this.Image = info.Image;
+            this.RestartExplorer = info.RestartExplorer;
+            ChkVisible = new VisibleCheckBox(this);
+            MyToolTip.SetToolTip(ChkVisible, info.Tip);
+        }
+
+        public RegRuleItem(RegRule[] rules, ItemInfo info)
+            : this(info) { this.Rules = rules; }
+
+        public RegRuleItem(RegRule rule, ItemInfo info)
+            : this(info) { this.Rules = new[] { rule }; }
+
+        public RegRuleItem(RuleAndInfo ruleAndInfo)
+            : this(ruleAndInfo.Rules, ruleAndInfo.ItemInfo) { }
+
+        private RegRule[] _Rules;
+        public RegRule[] Rules
+        {
+            get => _Rules;
+            set
+            {
+                _Rules = value;
+                ChkVisible.Checked = ItemVisible;
+            }
+        }
+
+        public int MarginRight
+        {
+            get => ChkVisible.Margin.Right;
+            set
+            {
+                Padding p = ChkVisible.Margin;
+                ChkVisible.Margin = new Padding(p.Left, p.Top, value, p.Bottom);
+            }
+        }
+
+        public static readonly int SysMarginRignt = 78.DpiZoom();
+
+        public VisibleCheckBox ChkVisible { get; set; }
+        public bool RestartExplorer { get; set; }
+
+        public bool ItemVisible
+        {
+            get
+            {
+                foreach(RegRule rule in Rules)
+                {
+                    using(RegistryKey key = RegistryEx.GetRegistryKey(rule.RegPath))
+                    {
+                        if(key?.GetValue(rule.ValueName) == null) continue;
+                        if(key.GetValueKind(rule.ValueName) != rule.ValueKind) continue;
+                        if(key.GetValue(rule.ValueName).ToString().ToLower()
+                            == rule.TurnOffValue.ToString().ToLower()) return false;
+                    }
+                }
+                return true;
+            }
+            set
+            {
+                foreach(RegRule rule in Rules)
+                {
+                    object data = value ? rule.TurnOnValue : rule.TurnOffValue;
+                    Registry.SetValue(rule.RegPath, rule.ValueName, data, rule.ValueKind);
+                }
+                if(RestartExplorer) ExplorerRestarter.NeedRestart = true;
+            }
+        }
+
+        public IFoldGroupItem FoldGroupItem { get; set; }
+
+        const string LM_SMWCPE = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer";
+        const string CU_SMWCPE = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer";
+        const string LM_SMWCE = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer";
+        const string CU_SMWCE = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer";
+        const string LM_SPMWE = @"HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Explorer";
+        const string CU_SPMWE = @"HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\Explorer";
+
+        public static RuleAndInfo CustomFolder = new RuleAndInfo
+        {
+            Rules = new[] {
+                new RegRule(LM_SMWCPE, "NoCustomizeThisFolder", 0, 1),
+                new RegRule(LM_SMWCPE, "NoCustomizeWebView", 0, 1),
+                new RegRule(CU_SMWCPE, "NoCustomizeThisFolder", 0, 1),
+                new RegRule(CU_SMWCPE, "NoCustomizeWebView", 0, 1)
+        },
+            ItemInfo = new ItemInfo
+            {
+                Text = AppString.Text_CustomFolder,
+                Image = AppImage.CustomFolder,
+                Tip = AppString.Tip_CustomFolder,
+                RestartExplorer = true
+            }
+        };
+
+        public static RuleAndInfo NetworkDrive = new RuleAndInfo
+        {
+            Rules = new[] {
+                new RegRule(LM_SMWCPE, "NoNetConnectDisconnect", 0, 1),
+                new RegRule(CU_SMWCPE, "NoNetConnectDisconnect", 0, 1)
+        },
+            ItemInfo = new ItemInfo
+            {
+                Text = $"{AppString.MapNetworkDrive} && {AppString.DisconnectNetworkDrive}",
+                Image = AppImage.NetworkDrive,
+                RestartExplorer = true
+            }
+        };
+
+        public static RuleAndInfo RecycleBinProperties = new RuleAndInfo
+        {
+            Rules = new[] {
+                new RegRule(LM_SMWCPE, "NoPropertiesRecycleBin", 0, 1),
+                new RegRule(CU_SMWCPE, "NoPropertiesRecycleBin", 0, 1)
+        },
+            ItemInfo = new ItemInfo
+            {
+                Text = AppString.RecycleBinProperties,
+                Image = AppImage.RecycleBinProperties,
+                RestartExplorer = true
+            }
+        };
+
+        public static RuleAndInfo SendToDrive = new RuleAndInfo
+        {
+            Rules = new[] {
+                new RegRule(LM_SMWCPE, "NoDrivesInSendToMenu", 0, 1),
+                new RegRule(CU_SMWCPE, "NoDrivesInSendToMenu", 0, 1)
+        },
+            ItemInfo = new ItemInfo
+            {
+                Text = AppString.RemovableDrive,
+                Image = AppImage.Drive,
+                Tip = AppString.Tip_SendToDrive,
+                RestartExplorer = true
+            }
+        };
+
+        public static RuleAndInfo DeferBuildSendTo = new RuleAndInfo
+        {
+            Rules = new[] {
+                new RegRule(LM_SMWCE, "DelaySendToMenuBuild", 0, 1),
+                new RegRule(CU_SMWCE, "DelaySendToMenuBuild", 0, 1)
+        },
+            ItemInfo = new ItemInfo
+            {
+                Text = AppString.Text_BuildSendtoMenu,
+                Image = AppImage.SendTo,
+                Tip = AppString.Tip_BuildSendtoMenu
+            }
+        };
+
+        public static RuleAndInfo UseStoreOpenWith = new RuleAndInfo
+        {
+            Rules = new[] {
+                new RegRule(LM_SPMWE, "NoUseStoreOpenWith", 0, 1),
+                new RegRule(CU_SPMWE, "NoUseStoreOpenWith", 0, 1)
+        },
+            ItemInfo = new ItemInfo
+            {
+                Text = AppString.Text_UseStoreOpenWith,
+                Image = AppImage.MicrosoftStore,
+                Tip = AppString.Tip_UseStoreOpenWith
+            }
+        };
+    }
+}

+ 65 - 0
ContextMenuManager/Controls/SelectItemsForm.cs

@@ -0,0 +1,65 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    class SelectItemsForm : Form
+    {
+        public SelectItemsForm()
+        {
+            this.AcceptButton = btnOk;
+            this.CancelButton = btnCancel;
+            this.Font = SystemFonts.MessageBoxFont;
+            this.ShowIcon = this.ShowInTaskbar = false;
+            this.StartPosition = FormStartPosition.CenterParent;
+            this.MinimumSize = this.Size = new Size(652, 425).DpiZoom();
+            list.Owner = listBox;
+            InitializeComponents();
+        }
+
+        protected MyList list = new MyList();
+        protected MyListBox listBox = new MyListBox();
+        protected Panel pnlBorder = new Panel
+        {
+            BackColor = Color.FromArgb(200, 200, 200)
+        };
+        protected Button btnOk = new Button
+        {
+            Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
+            DialogResult = DialogResult.OK,
+            Text = AppString.Ok,
+            AutoSize = true
+        };
+        protected Button btnCancel = new Button
+        {
+            Anchor = AnchorStyles.Bottom | AnchorStyles.Right,
+            DialogResult = DialogResult.Cancel,
+            Text = AppString.Cancel,
+            AutoSize = true
+        };
+
+        private void InitializeComponents()
+        {
+            this.Controls.AddRange(new Control[] { listBox, pnlBorder, btnOk, btnCancel });
+            int a = 20.DpiZoom();
+            listBox.Location = new Point(a, a);
+            pnlBorder.Location = new Point(a - 1, a - 1);
+            btnOk.Top = btnCancel.Top = this.ClientSize.Height - btnCancel.Height - a;
+            btnCancel.Left = this.ClientSize.Width - btnCancel.Width - a;
+            btnOk.Left = btnCancel.Left - btnOk.Width - a;
+            this.OnResize(null);
+        }
+
+        protected override void OnResize(EventArgs e)
+        {
+            base.OnResize(e);
+            listBox.Width = ClientSize.Width - 2 * listBox.Left;
+            listBox.Height = btnOk.Top - 2 * listBox.Top;
+            pnlBorder.Width = listBox.Width + 2;
+            pnlBorder.Height = listBox.Height + 2;
+        }
+    }
+}

+ 159 - 0
ContextMenuManager/Controls/SendToItem.cs

@@ -0,0 +1,159 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using Microsoft.Win32;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class SendToItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, ITsiTextItem, ITsiIconItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiDeleteItem
+    {
+
+        private static readonly IWshRuntimeLibrary.WshShell WshShell = new IWshRuntimeLibrary.WshShell();
+
+        public SendToItem(string filePath)
+        {
+            InitializeComponents();
+            this.FilePath = filePath;
+        }
+
+        private string filePath;
+        public string FilePath
+        {
+            get => filePath;
+            set
+            {
+                filePath = value;
+                if(IsShortcut) this.Shortcut = WshShell.CreateShortcut(value);
+                this.Text = this.ItemText;
+                this.Image = this.ItemIcon.ToBitmap();
+                ChkVisible.Checked = this.ItemVisible;
+            }
+        }
+
+        private IWshRuntimeLibrary.IWshShortcut Shortcut;
+        private string FileName => Path.GetFileName(FilePath);
+        private string FileExtension => Path.GetExtension(FilePath);
+        private bool IsShortcut => FileExtension.ToLower() == ".lnk";
+        public string SearchText => $"{AppString.SideBar_SendTo} {Text}";
+
+        public string ItemFilePath
+        {
+            get
+            {
+                if(IsShortcut) return Shortcut.TargetPath;
+                else
+                {
+                    string guidPath = Registry.ClassesRoot.OpenSubKey(FileExtension)?.GetValue("")?.ToString();
+                    if(string.IsNullOrEmpty(guidPath)) return null;
+                    else return Registry.ClassesRoot.OpenSubKey($@"{guidPath}\InProcServer32")?.GetValue("")?.ToString();
+                }
+            }
+        }
+
+        public bool ItemVisible
+        {
+            get => (File.GetAttributes(FilePath) & FileAttributes.Hidden) != FileAttributes.Hidden;
+            set
+            {
+                FileAttributes attributes = File.GetAttributes(FilePath);
+                if(value) attributes &= ~FileAttributes.Hidden;
+                else attributes |= FileAttributes.Hidden;
+                File.SetAttributes(FilePath, attributes);
+            }
+        }
+
+        public string ItemText
+        {
+            get
+            {
+                string name = SendToList.GetMenuName(FileName);
+                if(name == string.Empty) name = Path.GetFileNameWithoutExtension(FilePath);
+                if(name == string.Empty) name = FileExtension;
+                return name;
+            }
+            set
+            {
+                DesktopIniHelper.SetLocalizedFileName(FilePath, value);
+                ExplorerRestarter.NeedRestart = true;
+            }
+        }
+
+        public Icon ItemIcon
+        {
+            get
+            {
+                Icon icon = null;
+                if(IsShortcut)
+                {
+                    icon = ResourceIcon.GetIcon(IconLocation, out string iconPath, out int iconIndex);
+                    IconPath = iconPath; IconIndex = iconIndex;
+                    if(icon == null)
+                    {
+                        if(File.Exists(Shortcut.TargetPath)) icon = Icon.ExtractAssociatedIcon(Shortcut.TargetPath);
+                        else if(Directory.Exists(Shortcut.TargetPath)) icon = ResourceIcon.GetFolderIcon(Shortcut.TargetPath);
+                    }
+                }
+                icon = icon ?? ResourceIcon.GetExtensionIcon(FileExtension);
+                return icon;
+            }
+        }
+
+        public string IconLocation
+        {
+            get
+            {
+                string location = Shortcut.IconLocation;
+                if(location.StartsWith(",")) location = $"{Shortcut.TargetPath}{location}";
+                return location;
+            }
+            set
+            {
+                Shortcut.IconLocation = value;
+                Shortcut.Save();
+            }
+        }
+
+        public string IconPath { get; set; }
+        public int IconIndex { get; set; }
+
+        public VisibleCheckBox ChkVisible { get; set; }
+        public MenuButton BtnShowMenu { get; set; }
+        public ChangeTextMenuItem TsiChangeText { get; set; }
+        public ChangeIconMenuItem TsiChangeIcon { get; set; }
+        public WebSearchMenuItem TsiSearch { get; set; }
+        public FilePropertiesMenuItem TsiFileProperties { get; set; }
+        public FileLocationMenuItem TsiFileLocation { get; set; }
+        public DeleteMeMenuItem TsiDeleteMe { get; set; }
+        readonly ToolStripSeparator TsiIconSeparator = new ToolStripSeparator();
+        readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu_Details);
+
+        private void InitializeComponents()
+        {
+            BtnShowMenu = new MenuButton(this);
+            ChkVisible = new VisibleCheckBox(this);
+            TsiChangeText = new ChangeTextMenuItem(this);
+            TsiChangeIcon = new ChangeIconMenuItem(this);
+            TsiSearch = new WebSearchMenuItem(this);
+            TsiFileLocation = new FileLocationMenuItem(this);
+            TsiFileProperties = new FilePropertiesMenuItem(this);
+            TsiDeleteMe = new DeleteMeMenuItem(this);
+
+            ContextMenuStrip.Opening += (sender, e) => TsiChangeIcon.Visible = TsiIconSeparator.Visible = IsShortcut;
+
+            ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText, new ToolStripSeparator(),
+                TsiChangeIcon, TsiIconSeparator, TsiDetails, new ToolStripSeparator(), TsiDeleteMe });
+
+            TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(),
+                TsiFileProperties, TsiFileLocation });
+        }
+
+        public void DeleteMe()
+        {
+            File.Delete(this.FilePath);
+            DesktopIniHelper.DeleteLocalizedFileName(FilePath);
+            this.Dispose();
+        }
+    }
+}

+ 56 - 0
ContextMenuManager/Controls/SendToList.cs

@@ -0,0 +1,56 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class SendToList : MyList
+    {
+        public static string SendToPath => Environment.ExpandEnvironmentVariables(@"%AppData%\Microsoft\Windows\SendTo");
+        private static string DesktopIniPath => $@"{SendToPath}\desktop.ini";
+
+        public static IniReader DesktopIniReader;
+
+        public static string GetMenuName(string fileName)
+        {
+            string name = DesktopIniReader.GetValue("LocalizedFileNames", fileName);
+            return ResourceString.GetDirectString(name);
+        }
+
+        public void LoadItems()
+        {
+            this.ClearItems();
+            this.LoadCommonItems();
+            this.SortItemByText();
+            this.AddNewItem();
+            this.AddItem(new RegRuleItem(RegRuleItem.SendToDrive) { MarginRight = RegRuleItem.SysMarginRignt });
+            this.AddItem(new RegRuleItem(RegRuleItem.DeferBuildSendTo) { MarginRight = RegRuleItem.SysMarginRignt });
+        }
+
+        private void LoadCommonItems()
+        {
+            DesktopIniReader = new IniReader(DesktopIniPath);
+            Array.ForEach(new DirectoryInfo(SendToPath).GetFiles(), fi =>
+            {
+                if(fi.Name.ToLower() != "desktop.ini")
+                    this.AddItem(new SendToItem(fi.FullName));
+            });
+        }
+
+        private void AddNewItem()
+        {
+            NewItem newItem = new NewItem();
+            this.InsertItem(newItem, 0);
+            newItem.NewItemAdd += (sender, e) =>
+            {
+                using(NewSendToDialog dlg = new NewSendToDialog())
+                {
+                    if(dlg.ShowDialog() == DialogResult.OK)
+                        this.InsertItem(new SendToItem(dlg.FilePath), 2);
+                }
+            };
+        }
+    }
+}

+ 227 - 0
ContextMenuManager/Controls/ShellCommonDialog.cs

@@ -0,0 +1,227 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using System.Xml;
+using static Microsoft.Win32.Registry;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class ShellCommonDialog : CommonDialog
+    {
+        public List<string> SelectedShellPaths { get; private set; }
+        public Dictionary<string, Guid> SelectedShellExPathAndGuids { get; private set; }
+        public string ShellExPath { get; set; }
+        public string ShellPath { get; set; }
+        public string ScenePath { get; set; }
+        public override void Reset() { }
+
+        protected override bool RunDialog(IntPtr hwndOwner)
+        {
+            using(ShellCommonForm frm = new ShellCommonForm(ScenePath, ShellPath, ShellExPath))
+            {
+                if(frm.ShowDialog() == DialogResult.OK)
+                {
+                    this.SelectedShellPaths = frm.SelectedShellPaths;
+                    this.SelectedShellExPathAndGuids = frm.SelectedShellExPathAndGuids;
+                    return true;
+                }
+                return false;
+            }
+        }
+
+        sealed class ShellCommonForm : SelectItemsForm
+        {
+            public ShellCommonForm(string scenePath, string shellPath, string shellExPath)
+            {
+                this.ScenePath = scenePath;
+                this.ShellPath = shellPath;
+                this.ShellExPath = shellExPath;
+                this.Text = AppString.Text_CheckCommon;
+                btnOk.Click += (sender, e) => GetSelectedItems();
+                LoadItems();
+            }
+
+            public List<string> SelectedShellPaths { get; private set; } = new List<string>();
+            public Dictionary<string, Guid> SelectedShellExPathAndGuids { get; private set; } = new Dictionary<string, Guid>();
+            public string ScenePath { get; set; }
+            public string ShellPath { get; set; }
+            public string ShellExPath { get; set; }
+            private XmlElement shellXE;
+            private XmlElement shellExXE;
+
+            public void LoadItems()
+            {
+                XmlDocument doc1 = new XmlDocument();
+                if(!File.Exists(Program.AppDataShellCommonDicPath))
+                {
+                    File.WriteAllText(Program.AppDataShellCommonDicPath, Properties.Resources.ShellCommonDic, Encoding.UTF8);
+                }
+                doc1.Load(Program.AppDataShellCommonDicPath);
+
+                if(File.Exists(Program.ShellCommonDicPath))
+                {
+                    XmlDocument doc2 = new XmlDocument();
+                    doc2.Load(Program.ShellCommonDicPath);
+                    foreach(XmlNode xn in doc2.DocumentElement.ChildNodes)
+                    {
+                        XmlNode node = doc1.ImportNode(xn, true);
+                        doc1.DocumentElement.AppendChild(node);
+                    }
+                }
+
+                foreach(XmlElement groupXE in doc1.DocumentElement.ChildNodes)
+                {
+                    string scenePath = groupXE.GetAttribute("RegPath");
+                    if(ScenePath.Equals(scenePath, StringComparison.OrdinalIgnoreCase))
+                    {
+                        shellXE = (XmlElement)groupXE.SelectSingleNode("Shell");
+                        shellExXE = (XmlElement)groupXE.SelectSingleNode("ShellEx");
+                        if(shellXE != null) LoadShellItems();
+                        if(ShellExPath != null && shellExXE != null) LoadShellExItems();
+                    }
+                }
+            }
+
+            private void LoadShellItems()
+            {
+                foreach(XmlElement itemXE in shellXE.GetElementsByTagName("Item"))
+                {
+                    XmlElement szXE = (XmlElement)itemXE.SelectSingleNode("Value/REG_SZ");
+                    string keyName = itemXE.GetAttribute("KeyName");
+                    if(string.IsNullOrWhiteSpace(keyName)) continue;
+                    ShellCommonItem item = new ShellCommonItem
+                    {
+                        DefaultKeyName = keyName,
+                        ItemXE = itemXE
+                    };
+                    if(szXE != null)
+                    {
+                        item.Text = ResourceString.GetDirectString(szXE.GetAttribute("MUIVerb"));
+                        if(szXE.HasAttribute("Icon")) item.Image = ResourceIcon.GetIcon(szXE.GetAttribute("Icon"))?.ToBitmap();
+                        else if(szXE.HasAttribute("HasLUAShield")) item.Image = AppImage.Shield;
+                    }
+                    if(item.Image == null) item.Image = AppImage.NotFound;
+                    if(string.IsNullOrWhiteSpace(item.Text)) item.Text = item.DefaultKeyName;
+                    item.SetTip(itemXE.GetAttribute("Tip"));
+                    list.AddItem(item);
+                }
+            }
+
+            private void LoadShellExItems()
+            {
+                foreach(XmlElement itemXE in shellExXE.GetElementsByTagName("Item"))
+                {
+                    if(!GuidInfo.TryGetGuid(itemXE.GetAttribute("Guid"), out Guid guid)) continue;
+                    if(ShellExItem.GetPathAndGuids(ShellExPath).Values.Contains(guid)) continue;
+                    ShellExCommonItem item = new ShellExCommonItem
+                    {
+                        Image = ResourceIcon.GetIcon(itemXE.GetAttribute("Icon"))?.ToBitmap() ?? AppImage.DllDefaultIcon,
+                        Text = ResourceString.GetDirectString(itemXE.GetAttribute("Text")),
+                        DefaultKeyName = itemXE.GetAttribute("KeyName"),
+                        Guid = guid
+                    };
+                    if(string.IsNullOrWhiteSpace(item.Text)) item.Text = GuidInfo.GetText(guid);
+                    if(string.IsNullOrWhiteSpace(item.DefaultKeyName)) item.DefaultKeyName = guid.ToString("B");
+                    item.SetTip(itemXE.GetAttribute("Tip"));
+                    list.AddItem(item);
+                }
+            }
+
+            private void GetSelectedItems()
+            {
+                foreach(Control ctr in list.Controls)
+                {
+                    if(ctr.GetType() == typeof(ShellCommonItem)) CreateShellItem((ShellCommonItem)ctr);
+                    else if(ctr.GetType() == typeof(ShellExCommonItem)) CreateShellExItem((ShellExCommonItem)ctr);
+                }
+            }
+
+            private void CreateShellItem(ShellCommonItem item)
+            {
+                if(!item.IsSelected) return;
+                string regPath = ObjectPath.GetNewPathWithIndex
+                    ($@"{ShellPath}\{item.DefaultKeyName}", ObjectPath.PathType.Registry);
+                ShellCommonItem.WriteSubKeysValue(item.ItemXE, regPath);
+                SelectedShellPaths.Add(regPath);
+            }
+
+            private void CreateShellExItem(ShellExCommonItem item)
+            {
+                if(!item.IsSelected) return;
+                string regPath = ObjectPath.GetNewPathWithIndex
+                    ($@"{ShellExPath}\ContextMenuHandlers\{item.DefaultKeyName}", ObjectPath.PathType.Registry);
+                SetValue(regPath, "", item.Guid.ToString("B"));
+                SelectedShellExPathAndGuids.Add(regPath, item.Guid);
+            }
+        }
+    }
+
+    class CheckBoxItem : MyListItem
+    {
+        readonly CheckBox chkSelected = new CheckBox { AutoSize = true };
+        public bool IsSelected => chkSelected.Checked;
+
+        public CheckBoxItem()
+        {
+            this.AddCtr(chkSelected);
+        }
+
+        public void SetTip(string tip)
+        {
+            MyToolTip.SetToolTip(chkSelected, tip);
+        }
+    }
+
+    sealed class ShellCommonItem : CheckBoxItem
+    {
+        public string DefaultKeyName { get; set; }
+        public XmlElement ItemXE { get; set; }
+
+        public static void WriteAttributesValue(XmlNode valueXN, string regPath)
+        {
+            if(valueXN == null) return;
+            XmlNode szXN = valueXN.SelectSingleNode("REG_SZ");
+            XmlNode dwordXN = valueXN.SelectSingleNode("REG_DWORD");
+            XmlNode expand_szXN = valueXN.SelectSingleNode("REG_EXPAND_SZ");
+            if(szXN != null)
+                foreach(XmlAttribute a in szXN.Attributes)
+                    SetValue(regPath, a.Name, a.Value, Microsoft.Win32.RegistryValueKind.String);
+            if(expand_szXN != null)
+                foreach(XmlAttribute a in expand_szXN.Attributes)
+                    SetValue(regPath, a.Name, a.Value, Microsoft.Win32.RegistryValueKind.ExpandString);
+            if(dwordXN != null)
+                foreach(XmlAttribute a in dwordXN.Attributes)
+                {
+                    int value = a.Value.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
+                        ? Convert.ToInt32(a.Value, 16) : Convert.ToInt32(a.Value);
+                    SetValue(regPath, a.Name, value, Microsoft.Win32.RegistryValueKind.DWord);
+                }
+        }
+
+        public static void WriteSubKeysValue(XmlElement keyXE, string regPath)
+        {
+            if(keyXE == null) return;
+            string defaultValue = keyXE.GetAttribute("Default");
+            if(!string.IsNullOrWhiteSpace(defaultValue)) SetValue(regPath, "", defaultValue);
+            WriteAttributesValue(keyXE.SelectSingleNode("Value"), regPath);
+
+            XmlNode subKeyXN = keyXE.SelectSingleNode("SubKey");
+            if(subKeyXN != null)
+            {
+                foreach(XmlElement xe in subKeyXN.ChildNodes)
+                    WriteSubKeysValue(xe, $@"{regPath}\{xe.Name}");
+            }
+        }
+    }
+
+    sealed class ShellExCommonItem : CheckBoxItem
+    {
+        public string DefaultKeyName { get; set; }
+        public Guid Guid { get; set; }
+    }
+}

+ 130 - 0
ContextMenuManager/Controls/ShellExItem.cs

@@ -0,0 +1,130 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class ShellExItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem,
+        ITsiWebSearchItem, ITsiFilePathItem, ITsiRegPathItem, ITsiDeleteItem
+    {
+        public static Dictionary<string, Guid> GetPathAndGuids(string shellExPath)
+        {
+            Dictionary<string, Guid> dic = new Dictionary<string, Guid>();
+            foreach(string cmhPart in CmhParts)
+            {
+                using(RegistryKey cmKey = RegistryEx.GetRegistryKey($@"{shellExPath}\{cmhPart}"))
+                {
+                    if(cmKey == null) continue;
+                    foreach(string keyName in cmKey.GetSubKeyNames())
+                    {
+                        using(RegistryKey key = cmKey.OpenSubKey(keyName))
+                        {
+                            if(!GuidInfo.TryGetGuid(key.GetValue("")?.ToString(), out Guid guid))
+                                GuidInfo.TryGetGuid(keyName, out guid);
+                            if(!guid.Equals(Guid.Empty))
+                                dic.Add(key.Name, guid);
+                        }
+                    }
+                }
+            }
+            return dic;
+        }
+
+        public static readonly string[] CmhParts = { "ContextMenuHandlers", "-ContextMenuHandlers" };
+        private const string LnkOpenGuid = "00021401-0000-0000-c000-000000000046";
+
+        public ShellExItem(Guid guid, string regPath)
+        {
+            InitializeComponents();
+            this.Guid = guid;
+            this.RegPath = regPath;
+        }
+
+        private string regPath;
+        public string RegPath
+        {
+            get => regPath;
+            set
+            {
+                regPath = value;
+                this.Text = this.ItemText;
+                this.Image = GuidInfo.GetImage(Guid);
+                ChkVisible.Checked = this.ItemVisible;
+            }
+        }
+
+        public Guid Guid { get; set; }
+        public string SearchText => Text;
+        public string ItemFilePath => GuidInfo.GetFilePath(Guid);
+        private string KeyName => RegistryEx.GetKeyName(RegPath);
+        private string ShellExPath => RegistryEx.GetParentPath(RegistryEx.GetParentPath(RegPath));
+        private string CmhKeyName => RegistryEx.GetKeyName(RegistryEx.GetParentPath(RegPath));
+        private string DefaultValue => Registry.GetValue(RegPath, "", null)?.ToString();
+        private string ItemText => GuidInfo.GetText(Guid) ?? ((Guid.ToString("B") == KeyName) ? DefaultValue : KeyName);
+        private string BuckupPath => $@"{ShellExPath}\{(ItemVisible ? CmhParts[1] : CmhParts[0])}\{KeyName}";
+        private bool IsOpenLnkItem => Guid.ToString() == LnkOpenGuid;
+        private bool TryProtectOpenItem => IsOpenLnkItem && MessageBoxEx.Show
+            (AppString.MessageBox_PromptIsOpenItem, MessageBoxButtons.YesNo) != DialogResult.Yes;
+
+        public bool ItemVisible
+        {
+            get => CmhKeyName.Equals(CmhParts[0], StringComparison.OrdinalIgnoreCase);
+            set
+            {
+                if(!value && TryProtectOpenItem) return;
+                using(RegistryKey srcKey = RegistryEx.GetRegistryKey(RegPath))
+                using(RegistryKey dstKey = RegistryEx.GetRegistryKey(BuckupPath, true, true))
+                    srcKey?.CopyTo(dstKey);
+                RegistryEx.DeleteKeyTree(RegPath);
+                RegPath = BuckupPath;
+            }
+        }
+
+        public VisibleCheckBox ChkVisible { get; set; }
+        public MenuButton BtnShowMenu { get; set; }
+        public WebSearchMenuItem TsiSearch { get; set; }
+        public FilePropertiesMenuItem TsiFileProperties { get; set; }
+        public FileLocationMenuItem TsiFileLocation { get; set; }
+        public RegLocationMenuItem TsiRegLocation { get; set; }
+        public DeleteMeMenuItem TsiDeleteMe { get; set; }
+        readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu_Details);
+        readonly ToolStripMenuItem TsiCopyGuid = new ToolStripMenuItem(AppString.Menu_CopyGuid);
+
+        private void InitializeComponents()
+        {
+            BtnShowMenu = new MenuButton(this);
+            ChkVisible = new VisibleCheckBox(this);
+            TsiSearch = new WebSearchMenuItem(this);
+            TsiFileLocation = new FileLocationMenuItem(this);
+            TsiFileProperties = new FilePropertiesMenuItem(this);
+            TsiRegLocation = new RegLocationMenuItem(this);
+            TsiDeleteMe = new DeleteMeMenuItem(this);
+
+            ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiCopyGuid, new ToolStripSeparator(),
+                TsiDetails, new ToolStripSeparator(), TsiDeleteMe });
+
+            TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(),
+                TsiFileProperties, TsiFileLocation, TsiRegLocation});
+
+            ContextMenuStrip.Opening += (sender, e) => TsiDeleteMe.Enabled = !IsOpenLnkItem;
+            TsiCopyGuid.Click += (sender, e) => CopyGuid();
+        }
+
+        private void CopyGuid()
+        {
+            Clipboard.SetText(Guid.ToString());
+            MessageBoxEx.Show($"{AppString.MessageBox_CopiedToClipboard}:\n{Guid}",
+                MessageBoxButtons.OK, MessageBoxIcon.Information);
+        }
+
+        public void DeleteMe()
+        {
+            RegistryEx.DeleteKeyTree(this.RegPath);
+            RegistryEx.DeleteKeyTree(this.BuckupPath);
+            this.Dispose();
+        }
+    }
+}

+ 379 - 0
ContextMenuManager/Controls/ShellItem.cs

@@ -0,0 +1,379 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    class ShellItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, ITsiTextItem,
+        ITsiCommandItem, ITsiIconItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiRegPathItem, ITsiDeleteItem
+    {
+        /// <summary>Shell子菜单注册表项路径</summary>
+        public const string CommandStorePath = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell";
+
+        /// <summary>Shell类型菜单特殊注册表项名默认名称</summary>
+        private static readonly Dictionary<string, string> DefaultNames
+            = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) {
+            {"open", AppString.Open }, {"edit", AppString.Edit }, {"print", AppString.Print },
+            {"find", AppString.Find }, {"play", AppString.Play }, {"runas", AppString.Runas },
+            {"explore", AppString.Text_Explore },//"浏览" 未找到合适的本地化字符串资源
+        };
+
+        /// <summary>菜单项目在菜单中出现的位置</summary>
+        enum Positions { Default, Top, Bottom }
+
+        public ShellItem(string regPath)
+        {
+            InitializeComponents();
+            this.RegPath = regPath;
+        }
+
+        private string regPath;
+        public string RegPath
+        {
+            get => regPath;
+            set
+            {
+                regPath = value;
+                this.Text = this.ItemText;
+                this.Image = this.ItemIcon.ToBitmap();
+                if(!HasIcon) this.Image = Image.ToTransparent();
+                ChkVisible.Checked = this.ItemVisible;
+                BtnSubItems.Visible = IsMultiItem;
+            }
+        }
+
+        public string SearchText => Text;
+        private string CommandPath => $@"{RegPath}\command";
+        public string KeyName => RegistryEx.GetKeyName(RegPath);
+        private bool IsMultiItem => Registry.GetValue(RegPath, "SubCommands", null) != null;
+        protected virtual bool IsSubItem => false;
+        private bool IsOpenItem => KeyName.ToLower() == "open";
+        private bool TryProtectOpenItem => IsOpenItem && MessageBoxEx.Show(AppString.MessageBox_PromptIsOpenItem,
+                MessageBoxButtons.YesNo) != DialogResult.Yes;
+
+        public string ItemFilePath => GuidInfo.GetFilePath(Guid) ?? ObjectPath.ExtractFilePath(ItemCommand);
+        private bool HasIcon => IconLocation != null || HasLUAShield;
+
+        private bool OnlyInExplorer
+        {
+            get => Registry.GetValue(RegPath, "OnlyInBrowserWindow", null) != null;
+            set
+            {
+                if(value)
+                {
+                    if(TryProtectOpenItem) return;
+                    Registry.SetValue(RegPath, "OnlyInBrowserWindow", "");
+                }
+                else RegistryEx.DeleteValue(RegPath, "OnlyInBrowserWindow");
+            }
+        }
+
+        private bool OnlyWithShift
+        {
+            get => Registry.GetValue(RegPath, "Extended", null) != null;
+            set
+            {
+                if(value)
+                {
+                    if(TryProtectOpenItem) return;
+                    Registry.SetValue(RegPath, "Extended", "");
+                }
+                else RegistryEx.DeleteValue(RegPath, "Extended");
+            }
+        }
+
+        private bool NoWorkingDirectory
+        {
+            get => Registry.GetValue(RegPath, "NoWorkingDirectory", null) != null;
+            set
+            {
+                if(value) Registry.SetValue(RegPath, "NoWorkingDirectory", "");
+                else RegistryEx.DeleteValue(RegPath, "NoWorkingDirectory");
+            }
+        }
+
+        private Positions ItemPosition
+        {
+            get
+            {
+                string value = Registry.GetValue(RegPath, "Position", null)?.ToString()?.ToLower();
+                switch(value)
+                {
+                    case "top":
+                        return Positions.Top;
+                    case "bottom":
+                        return Positions.Bottom;
+                    default:
+                        return Positions.Default;
+                }
+            }
+            set
+            {
+                switch(value)
+                {
+                    case Positions.Top:
+                        Registry.SetValue(RegPath, "Position", "top");
+                        break;
+                    case Positions.Bottom:
+                        Registry.SetValue(RegPath, "Position", "bottom");
+                        break;
+                    case Positions.Default:
+                        RegistryEx.DeleteValue(RegPath, "Position");
+                        break;
+                }
+            }
+        }
+
+        public bool ItemVisible
+        {
+            get
+            {
+                if(Convert.ToInt32(Registry.GetValue(RegPath, "HideBasedOnVelocityId", null)) == 0x639bc8) return false;
+                if(!IsSubItem)
+                {
+                    if(Registry.GetValue(RegPath, "LegacyDisable", null) != null) return false;
+                    if(Registry.GetValue(RegPath, "ProgrammaticAccessOnly", null) != null) return false;
+                }
+                return true;
+            }
+            set
+            {
+                if(value)
+                {
+                    RegistryEx.DeleteValue(RegPath, "HideBasedOnVelocityId");
+                    RegistryEx.DeleteValue(RegPath, "LegacyDisable");
+                    RegistryEx.DeleteValue(RegPath, "ProgrammaticAccessOnly");
+                }
+                else
+                {
+                    if(TryProtectOpenItem) return;
+                    Registry.SetValue(RegPath, "HideBasedOnVelocityId", 0x639bc8);
+                }
+            }
+        }
+
+        public string ItemText
+        {
+            get
+            {
+                string name;
+                //菜单名称优先级别:MUIVerb > 默认值 > 特殊键值名 > 项名
+                List<string> valueNames = new List<string> { "MUIVerb" };
+                if(!IsMultiItem) valueNames.Add("");//多级母菜单不支持使用默认值作为名称
+                foreach(string valueName in valueNames)
+                {
+                    name = Registry.GetValue(RegPath, valueName, null)?.ToString();
+                    name = ResourceString.GetDirectString(name);
+                    if(!string.IsNullOrEmpty(name)) return name;
+                }
+                if(DefaultNames.TryGetValue(KeyName, out name)) return name;
+                else return KeyName;
+            }
+            set
+            {
+                //MUIVerb长度不可超过80,超过80系统会隐藏该菜单项目
+                if(ResourceString.GetDirectString(value).Length >= 80)
+                    MessageBoxEx.Show(AppString.MessageBox_TextLengthCannotExceed80);
+                else Registry.SetValue(RegPath, "MUIVerb", value);
+            }
+        }
+
+        public string ItemCommand
+        {
+            get
+            {
+                if(IsMultiItem) return null;
+                else return Registry.GetValue(CommandPath, "", null)?.ToString();
+            }
+            set
+            {
+                if(TryProtectOpenItem) return;
+                Registry.SetValue(CommandPath, "", value);
+                if(!this.HasIcon) this.Image = this.ItemIcon.ToBitmap().ToTransparent();
+            }
+        }
+
+        private bool HasLUAShield
+        {
+            get => Registry.GetValue(RegPath, "HasLUAShield", null) != null;
+            set
+            {
+                if(value) Registry.SetValue(RegPath, "HasLUAShield", "");
+                else RegistryEx.DeleteValue(RegPath, "HasLUAShield");
+            }
+        }
+
+        public string IconLocation
+        {
+            get => Registry.GetValue(RegPath, "Icon", null)?.ToString();
+            set
+            {
+                if(value != null) Registry.SetValue(RegPath, "Icon", value);
+                else RegistryEx.DeleteValue(RegPath, "Icon");
+            }
+        }
+
+        public string IconPath { get; set; }
+        public int IconIndex { get; set; }
+        public Icon ItemIcon
+        {
+            get
+            {
+                //菜单图标优先级别:Icon > HasLUAShield
+                //只要有Icon键值,不论数据是否为空,HasLUAShield键值就不起作用
+                Icon icon;
+                string iconPath;
+                int iconIndex;
+                if(IconLocation != null)
+                {
+                    icon = ResourceIcon.GetIcon(IconLocation, out iconPath, out iconIndex);
+                    if(icon == null && Path.GetExtension(iconPath).ToLower() == ".exe")
+                        icon = ResourceIcon.GetIcon(iconPath = "imageres.dll", iconIndex = 11);//文件为不存在的或没有图标的exe文件,不含图标的默认exe图标
+                }
+                else if(HasLUAShield)
+                    icon = ResourceIcon.GetIcon(iconPath = "imageres.dll", iconIndex = 73);//管理员小盾牌图标
+                else icon = ResourceIcon.GetIcon(iconPath = ItemFilePath, iconIndex = 0);//文件第一个图标
+                if(icon == null) icon = ResourceIcon.GetIcon(iconPath = "imageres.dll", iconIndex = 2);//图标资源不存在,白纸图标
+                IconPath = iconPath;
+                IconIndex = iconIndex;
+                return icon;
+            }
+        }
+
+        private Guid Guid
+        {
+            get
+            {
+                string value = Registry.GetValue(CommandPath, "DelegateExecute", null)?.ToString();
+                if(GuidInfo.TryGetGuid(value, out Guid guid)) return guid;
+                else return Guid.Empty;
+            }
+        }
+
+        public VisibleCheckBox ChkVisible { get; set; }
+        public MenuButton BtnShowMenu { get; set; }
+        public ChangeTextMenuItem TsiChangeText { get; set; }
+        public ChangeIconMenuItem TsiChangeIcon { get; set; }
+        public ChangeCommandMenuItem TsiChangeCommand { get; set; }
+        public WebSearchMenuItem TsiSearch { get; set; }
+        public FilePropertiesMenuItem TsiFileProperties { get; set; }
+        public FileLocationMenuItem TsiFileLocation { get; set; }
+        public RegLocationMenuItem TsiRegLocation { get; set; }
+        public DeleteMeMenuItem TsiDeleteMe { get; set; }
+
+        protected readonly ToolStripMenuItem TsiOtherAttributes = new ToolStripMenuItem(AppString.Menu_OtherAttributes);
+        readonly ToolStripMenuItem TsiItemIcon = new ToolStripMenuItem(AppString.Menu_ItemIcon);
+        readonly ToolStripMenuItem TsiDeleteIcon = new ToolStripMenuItem(AppString.Menu_DeleteIcon);
+        readonly ToolStripMenuItem TsiPosition = new ToolStripMenuItem(AppString.Menu_ItemPosition);
+        readonly ToolStripMenuItem TsiDefault = new ToolStripMenuItem(AppString.Menu_SetDefault);
+        readonly ToolStripMenuItem TsiTop = new ToolStripMenuItem(AppString.Menu_SetTop);
+        readonly ToolStripMenuItem TsiBottom = new ToolStripMenuItem(AppString.Menu_SetBottom);
+        readonly ToolStripMenuItem TsiShift = new ToolStripMenuItem(AppString.Menu_OnlyWithShift);
+        readonly ToolStripMenuItem TsiExplorer = new ToolStripMenuItem(AppString.Menu_OnlyInExplorer);
+        readonly ToolStripMenuItem TsiNoWorkDir = new ToolStripMenuItem(AppString.Menu_NoWorkingDirectory);
+        readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu_Details);
+        readonly PictureButton BtnSubItems = new PictureButton(AppImage.SubItems);
+
+        private void InitializeComponents()
+        {
+            BtnShowMenu = new MenuButton(this);
+            ChkVisible = new VisibleCheckBox(this);
+            TsiChangeText = new ChangeTextMenuItem(this);
+            TsiChangeCommand = new ChangeCommandMenuItem(this);
+            TsiChangeIcon = new ChangeIconMenuItem(this);
+            TsiSearch = new WebSearchMenuItem(this);
+            TsiFileLocation = new FileLocationMenuItem(this);
+            TsiFileProperties = new FilePropertiesMenuItem(this);
+            TsiRegLocation = new RegLocationMenuItem(this);
+            TsiDeleteMe = new DeleteMeMenuItem(this);
+
+            ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText, new ToolStripSeparator(), TsiItemIcon,
+                TsiPosition, TsiOtherAttributes, new ToolStripSeparator(), TsiDetails, new ToolStripSeparator(), TsiDeleteMe});
+
+            TsiItemIcon.DropDownItems.AddRange(new ToolStripItem[] { TsiChangeIcon, TsiDeleteIcon });
+
+            TsiPosition.DropDownItems.AddRange(new ToolStripItem[] { TsiDefault, TsiTop, TsiBottom });
+
+            TsiOtherAttributes.DropDownItems.AddRange(new ToolStripItem[] { TsiShift, TsiExplorer, TsiNoWorkDir });
+
+            TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(),
+                TsiChangeCommand, TsiFileProperties, TsiFileLocation, TsiRegLocation});
+
+            TsiDeleteIcon.Click += (sender, e) => DeleteIcon();
+            TsiTop.Click += (sender, e) => this.ItemPosition = Positions.Top;
+            TsiBottom.Click += (sender, e) => this.ItemPosition = Positions.Bottom;
+            TsiDefault.Click += (sender, e) => this.ItemPosition = Positions.Default;
+            TsiExplorer.Click += (sender, e) => this.OnlyInExplorer = !TsiExplorer.Checked;
+            TsiShift.Click += (sender, e) => this.OnlyWithShift = !TsiShift.Checked;
+            TsiNoWorkDir.Click += (sender, e) => this.NoWorkingDirectory = !TsiNoWorkDir.Checked;
+            ContextMenuStrip.Opening += (sender, e) => RefreshMenuItem();
+            BtnSubItems.MouseDown += (sender, e) => ShowSubItems();
+            MyToolTip.SetToolTip(BtnSubItems, AppString.Tip_EditSubItems);
+            this.AddCtr(BtnSubItems);
+        }
+
+        private void DeleteIcon()
+        {
+            this.IconLocation = null;
+            this.HasLUAShield = false;
+            this.Image = this.Image.ToTransparent();
+        }
+
+        private void RefreshMenuItem()
+        {
+            if(this.HasIcon)
+            {
+                TsiChangeIcon.Text = AppString.Menu_ChangeIcon;
+                TsiDeleteIcon.Visible = true;
+            }
+            else
+            {
+                TsiChangeIcon.Text = AppString.Menu_AddIcon;
+                TsiDeleteIcon.Visible = false;
+            }
+            TsiDeleteMe.Enabled = !IsOpenItem;
+            TsiChangeCommand.Visible = !IsMultiItem && Guid.Equals(Guid.Empty);
+            TsiPosition.Visible = TsiExplorer.Visible = TsiShift.Visible = !IsSubItem;
+            TsiNoWorkDir.Checked = this.NoWorkingDirectory;
+            if(!this.IsSubItem)
+            {
+                TsiShift.Checked = this.OnlyWithShift;
+                TsiExplorer.Checked = this.OnlyInExplorer;
+                TsiDefault.Checked = TsiTop.Checked = TsiBottom.Checked = false;
+                switch(this.ItemPosition)
+                {
+                    case Positions.Default:
+                        TsiDefault.Checked = true;
+                        break;
+                    case Positions.Top:
+                        TsiTop.Checked = true;
+                        break;
+                    case Positions.Bottom:
+                        TsiBottom.Checked = true;
+                        break;
+                }
+            }
+        }
+
+        private void ShowSubItems()
+        {
+            using(ShellSubMenuDialog dlg = new ShellSubMenuDialog())
+            {
+                dlg.Text = AppString.Text_EditSubItems.Replace("%s", this.Text);
+                dlg.Icon = ResourceIcon.GetIcon(IconPath, IconIndex);
+                dlg.ShowDialog(this.RegPath);
+            }
+        }
+
+        public virtual void DeleteMe()
+        {
+            RegistryEx.DeleteKeyTree(this.RegPath);
+            this.Dispose();
+        }
+    }
+}

+ 279 - 0
ContextMenuManager/Controls/ShellList.cs

@@ -0,0 +1,279 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class ShellList : MyList
+    {
+        public const string MENUPATH_FILE = @"HKEY_CLASSES_ROOT\*";//文件
+        public const string MENUPATH_FOLDER = @"HKEY_CLASSES_ROOT\Folder";//文件夹
+        public const string MENUPATH_DIRECTORY = @"HKEY_CLASSES_ROOT\Directory";//目录
+        public const string MENUPATH_BACKGROUND = @"HKEY_CLASSES_ROOT\Directory\Background";//目录背景
+        public const string MENUPATH_DESKTOP = @"HKEY_CLASSES_ROOT\DesktopBackground";//桌面背景
+        public const string MENUPATH_DRIVE = @"HKEY_CLASSES_ROOT\Drive";//磁盘分区
+        public const string MENUPATH_ALLOBJECTS = @"HKEY_CLASSES_ROOT\AllFilesystemObjects";//所有对象
+        public const string MENUPATH_COMPUTER = @"HKEY_CLASSES_ROOT\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}";//此电脑
+        public const string MENUPATH_RECYCLEBIN = @"HKEY_CLASSES_ROOT\CLSID\{645FF040-5081-101B-9F08-00AA002F954E}";//回收站
+        public const string MENUPATH_LIBRARY = @"HKEY_CLASSES_ROOT\LibraryFolder";//库
+        public const string MENUPATH_LIBRARY_BACKGROUND = @"HKEY_CLASSES_ROOT\LibraryFolder\Background";//库背景
+        public const string MENUPATH_LIBRARY_USER = @"HKEY_CLASSES_ROOT\UserLibraryFolder";//用户库
+        public const string MENUPATH_LNKFILE = @"HKEY_CLASSES_ROOT\lnkfile";//快捷方式
+        public const string MENUPATH_EXEFILE = @"HKEY_CLASSES_ROOT\exefile";//可执行文件
+        public const string MENUPATH_SYSLNKFILE = @"HKEY_CLASSES_ROOT\SystemFileAssociations\.lnk";
+        public const string MENUPATH_SYSEXEFILE = @"HKEY_CLASSES_ROOT\SystemFileAssociations\.exe";
+        public const string MENUPATH_UNKNOWN = @"HKEY_CLASSES_ROOT\Unknown";//未知格式
+        public const string MENUPATH_TEXT = @"HKEY_CLASSES_ROOT\SystemFileAssociations\text";//通用文本文件
+        public const string MENUPATH_IMAGE = @"HKEY_CLASSES_ROOT\SystemFileAssociations\image";//通用图像文件
+        public const string MENUPATH_VIDEO = @"HKEY_CLASSES_ROOT\SystemFileAssociations\video";//通用视频文件
+        public const string MENUPATH_AUDIO = @"HKEY_CLASSES_ROOT\SystemFileAssociations\audio";//通用音频文件
+        public const string MENUPATH_DIRECTORY_IMAGE = @"HKEY_CLASSES_ROOT\SystemFileAssociations\Directory.Image";//通用图像文件目录
+        public const string MENUPATH_DIRECTORY_VIDEO = @"HKEY_CLASSES_ROOT\SystemFileAssociations\Directory.Video";//通用视频文件目录
+        public const string MENUPATH_DIRECTORY_AUDIO = @"HKEY_CLASSES_ROOT\SystemFileAssociations\Directory.Video";//通用音频文件目录
+        public const string SYSFILEASSPATH = @"HKEY_CLASSES_ROOT\SystemFileAssociations";//系统扩展名注册表父项路径
+
+        public enum Scenes
+        {
+            File, Folder, Directory, Background, Desktop, Drive, AllObjects,
+            Computer, RecycleBin, Library, LnkFile, ExeFile, Text, Image, Video, Audio,
+            ImageDirectory, VideoDirectory, AudioDirectory, Unknown, CustomExtension
+        }
+
+        private Scenes scene;
+        public Scenes Scene
+        {
+            get => scene;
+            set
+            {
+                scene = value;
+                LoadItems();
+            }
+        }
+
+        private static string GetShellPath(string scenePath) => $@"{scenePath}\shell";
+        private static string GetShellExPath(string scenePath) => $@"{scenePath}\shellEx";
+
+        public ShellList()
+        {
+            TypeItem.ExtensionChanged += (sender, e) => this.Scene = Scenes.CustomExtension;
+        }
+
+        private void LoadItems()
+        {
+            this.ClearItems();
+            string scenePath = null;
+            switch(Scene)
+            {
+                case Scenes.File:
+                    scenePath = MENUPATH_FILE; break;
+                case Scenes.Folder:
+                    scenePath = MENUPATH_FOLDER; break;
+                case Scenes.Directory:
+                    scenePath = MENUPATH_DIRECTORY; break;
+                case Scenes.Background:
+                    scenePath = MENUPATH_BACKGROUND; break;
+                case Scenes.Desktop:
+                    scenePath = MENUPATH_DESKTOP; break;
+                case Scenes.Drive:
+                    scenePath = MENUPATH_DRIVE; break;
+                case Scenes.AllObjects:
+                    scenePath = MENUPATH_ALLOBJECTS; break;
+                case Scenes.Computer:
+                    scenePath = MENUPATH_COMPUTER; break;
+                case Scenes.RecycleBin:
+                    scenePath = MENUPATH_RECYCLEBIN; break;
+                case Scenes.Library:
+                    scenePath = MENUPATH_LIBRARY; break;
+                case Scenes.LnkFile:
+                    scenePath = MENUPATH_LNKFILE; break;
+                case Scenes.ExeFile:
+                    scenePath = MENUPATH_EXEFILE; break;
+                case Scenes.Text:
+                    scenePath = MENUPATH_TEXT; break;
+                case Scenes.Image:
+                    scenePath = MENUPATH_IMAGE; break;
+                case Scenes.Video:
+                    scenePath = MENUPATH_VIDEO; break;
+                case Scenes.Audio:
+                    scenePath = MENUPATH_AUDIO; break;
+                case Scenes.ImageDirectory:
+                    scenePath = MENUPATH_DIRECTORY_IMAGE; break;
+                case Scenes.VideoDirectory:
+                    scenePath = MENUPATH_DIRECTORY_VIDEO; break;
+                case Scenes.AudioDirectory:
+                    scenePath = MENUPATH_DIRECTORY_AUDIO; break;
+                case Scenes.Unknown:
+                    scenePath = MENUPATH_UNKNOWN; break;
+                case Scenes.CustomExtension:
+                    scenePath = TypeItem.SysAssExtPath; break;
+            }
+            this.AddNewItem(scenePath);
+            this.AddItems(scenePath);
+
+            switch(scene)
+            {
+                case Scenes.Background:
+                    this.AddItem(new RegRuleItem(RegRuleItem.CustomFolder) { MarginRight = RegRuleItem.SysMarginRignt });
+                    break;
+                case Scenes.Computer:
+                    this.AddItem(new RegRuleItem(RegRuleItem.NetworkDrive) { MarginRight = RegRuleItem.SysMarginRignt });
+                    break;
+                case Scenes.RecycleBin:
+                    this.AddItem(new RegRuleItem(RegRuleItem.RecycleBinProperties) { MarginRight = RegRuleItem.SysMarginRignt });
+                    break;
+                case Scenes.Library:
+                    this.AddItems(MENUPATH_LIBRARY_BACKGROUND);
+                    this.AddItems(MENUPATH_LIBRARY_USER);
+                    break;
+                case Scenes.LnkFile:
+                    this.AddItems(MENUPATH_SYSLNKFILE);
+                    break;
+                case Scenes.ExeFile:
+                    this.AddItems(MENUPATH_SYSEXEFILE);
+                    break;
+                case Scenes.CustomExtension:
+                    this.InsertItem(new TypeItem(), 0);
+                    this.AddItems(TypeItem.AssExtPath);
+                    break;
+            }
+        }
+
+        private void AddItems(string scenePath)
+        {
+            if(this.Scene == Scenes.CustomExtension && TypeItem.Extension == null) return;
+            this.AddShellItems(GetShellPath(scenePath));
+            this.AddShellExItems(GetShellExPath(scenePath));
+        }
+
+        private void AddShellItems(string shellPath)
+        {
+            using(RegistryKey shellKey = RegistryEx.GetRegistryKey(shellPath))
+            {
+                if(shellKey == null) return;
+                RegTrustedInstaller.TakeRegTreeOwnerShip(shellKey.Name);
+                Array.ForEach(shellKey.GetSubKeyNames(), keyName =>
+                {
+                    this.AddItem(new ShellItem($@"{shellPath}\{keyName}"));
+                });
+            }
+        }
+
+        private void AddShellExItems(string shellExPath)
+        {
+            List<Guid> guids = new List<Guid> { Guid.Empty };
+            using(RegistryKey shellExKey = RegistryEx.GetRegistryKey(shellExPath))
+            {
+                if(shellExKey == null) return;
+                RegTrustedInstaller.TakeRegTreeOwnerShip(shellExKey.Name);
+                Dictionary<string, Guid> dic = ShellExItem.GetPathAndGuids(shellExPath);
+                foreach(string path in dic.Keys)
+                {
+                    Guid guid = dic[path];
+                    if(!guids.Contains(guid))
+                    {
+                        this.AddItem(new ShellExItem(guid, path));
+                        guids.Add(guid);
+                    }
+                }
+            }
+        }
+
+        private void AddNewItem(string scenePath)
+        {
+            string shellPath = GetShellPath(scenePath);
+            string shellExPath = GetShellExPath(scenePath);
+            NewItem newItem = new NewItem();
+            AddCommonButton btnAddCommon = new AddCommonButton();
+            newItem.AddCtr(btnAddCommon);
+            this.AddItem(newItem);
+            if(this.Scene == Scenes.CustomExtension)
+            {
+                newItem.Visible = TypeItem.Extension != null;
+                TypeItem.ExtensionChanged += (sender, e) => newItem.Visible = TypeItem.Extension != null;
+            }
+            newItem.NewItemAdd += (sender, e) =>
+            {
+                using(NewShellDialog dlg = new NewShellDialog
+                {
+                    ScenePath = scenePath,
+                    ShellPath = shellPath
+                })
+                {
+                    if(dlg.ShowDialog() == DialogResult.OK)
+                        this.InsertItem(new ShellItem(dlg.NewItemRegPath), GetItemIndex(newItem) + 1);
+                }
+            };
+            btnAddCommon.MouseDown += (sender, e) =>
+            {
+                using(ShellCommonDialog dlg = new ShellCommonDialog
+                {
+                    ScenePath = scenePath,
+                    ShellPath = shellPath,
+                    ShellExPath = shellExPath
+                })
+                {
+                    if(dlg.ShowDialog() == DialogResult.OK)
+                    {
+                        foreach(string path in dlg.SelectedShellExPathAndGuids.Keys)
+                        {
+                            this.InsertItem(new ShellExItem(dlg.SelectedShellExPathAndGuids[path], path), 1);
+                        }
+                        dlg.SelectedShellPaths.ForEach(path => this.InsertItem(new ShellItem(path), 1));
+                    }
+                }
+            };
+        }
+
+        sealed class TypeItem : MyListItem
+        {
+
+            static string extension;
+            public static string Extension
+            {
+                get => extension;
+                set
+                {
+                    extension = value;
+                    ExtensionChanged?.Invoke(null, null);
+                }
+            }
+
+            const string HKCR = "HKEY_CLASSES_ROOT";
+            public static string SysAssExtPath => Extension == null ? null : $@"{SYSFILEASSPATH}\{Extension}";
+            public static string AssExtPath => Extension == null ? null : $@"{HKCR}\{FileExtensionDialog.GetTypeName(Extension)}";
+
+            public static event EventHandler ExtensionChanged;
+
+            public TypeItem()
+            {
+                GetImageAndText();
+                this.AddCtr(BtnType);
+                BtnType.MouseDown += (sender, e) =>
+                {
+                    using(FileExtensionDialog dlg = new FileExtensionDialog())
+                        if(dlg.ShowDialog() == DialogResult.OK) Extension = dlg.Extension;
+                };
+                ExtensionChanged += (sender, e) => GetImageAndText();
+            }
+
+            readonly PictureButton BtnType = new PictureButton(AppImage.Types);
+
+            private void GetImageAndText()
+            {
+                if(Extension == null)
+                {
+                    this.Image = AppImage.CustomType;
+                    this.Text = AppString.Text_SelectExtension;
+                }
+                else
+                {
+                    this.Image = ResourceIcon.GetExtensionIcon(Extension)?.ToBitmap() ?? AppImage.NotFound;
+                    this.Text = $"{AppString.Text_CurrentExtension}{Extension}";
+                }
+            }
+        }
+    }
+}

+ 213 - 0
ContextMenuManager/Controls/ShellNewItem.cs

@@ -0,0 +1,213 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using Microsoft.Win32;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class ShellNewItem : MyListItem, IChkVisibleItem, ITsiTextItem, IBtnShowMenuItem,
+        ITsiIconItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiRegPathItem, ITsiDeleteItem
+    {
+        public static readonly string[] SnParts = { "ShellNew", "-ShellNew" };
+
+        public ShellNewItem(string regPath)
+        {
+            InitializeComponents();
+            this.RegPath = regPath;
+        }
+
+        private string regPath;
+        public string RegPath
+        {
+            get => regPath;
+            set
+            {
+                regPath = value;
+                this.Text = this.ItemText;
+                this.Image = this.ItemIcon.ToBitmap();
+                ChkVisible.Checked = this.ItemVisible;
+            }
+        }
+
+        public string SearchText => $"{AppString.SideBar_New} {Text}";
+        private string Extension => RegPath.Split('\\')[1];
+        private string SnKeyName => RegistryEx.GetKeyName(RegPath);
+        private string BuckupPath => $@"{RegistryEx.GetParentPath(RegPath)}\{(ItemVisible ? SnParts[1] : SnParts[0])}";
+
+        private const string HKCR = "HKEY_CLASSES_ROOT";
+        private string TypePath => $@"{HKCR}\{FileExtensionDialog.GetTypeName(Extension)}";//关联类型路径
+        private string DefaultTypePath => $@"{HKCR}\{FileExtensionDialog.GetTypeName(Extension, false)}";//默认关联类型路径
+        private string TypeDefaultIcon => Registry.GetValue($@"{TypePath}\DefaultIcon", "", null)?.ToString();//关联类型默认图标路径
+        private string DefaultTypeDefaultIcon => Registry.GetValue($@"{DefaultTypePath}\DefaultIcon", "", null)?.ToString();//默认关联类型默认图标路径
+        private bool IsFolderItem => Registry.GetValue(RegPath, "Directory", null) != null;
+
+        private bool CanEditData
+        {
+            get
+            {
+                using(RegistryKey key = RegistryEx.GetRegistryKey(RegPath))
+                {
+                    foreach(string valueName in new[] { "Directory", "FileName", "Handler" })
+                        if(key.GetValue(valueName) != null) return false;
+                    if(key.GetValue("Data") != null && key.GetValueKind("Data") != RegistryValueKind.String) return false;
+                    else return true;
+                }
+            }
+        }
+
+        public string ItemFilePath
+        {
+            get
+            {
+                string filePath = null;
+                using(RegistryKey key = RegistryEx.GetRegistryKey(DefaultTypePath))
+                {
+                    if(key == null) return filePath;
+                    string value = key.OpenSubKey(@"shell\open\command")?.GetValue("")?.ToString();
+                    filePath = ObjectPath.ExtractFilePath(value);
+                    if(filePath != null) return filePath;
+
+                    value = key.OpenSubKey("CLSID")?.GetValue("")?.ToString();
+                    if(Guid.TryParse(value, out Guid guid))
+                    {
+                        filePath = GuidInfo.GetFilePath(guid);
+                        if(filePath != null) return filePath;
+                    }
+                }
+                return filePath;
+            }
+        }
+
+        public bool ItemVisible
+        {
+            get => SnKeyName.Equals(SnParts[0], StringComparison.OrdinalIgnoreCase);
+            set
+            {
+                using(RegistryKey srcKey = RegistryEx.GetRegistryKey(RegPath))
+                using(RegistryKey dstkey = RegistryEx.GetRegistryKey(BuckupPath, true, true))
+                    srcKey.CopyTo(dstkey);
+                RegistryEx.DeleteKeyTree(RegPath);
+                this.RegPath = BuckupPath;
+            }
+        }
+
+        public string ItemText
+        {
+            get
+            {
+                string name = Registry.GetValue(RegPath, "MenuText", null)?.ToString();
+                name = ResourceString.GetDirectString(name);
+                if(!string.IsNullOrEmpty(name)) return name;
+                name = Registry.GetValue(DefaultTypePath, "FriendlyTypeName", null)?.ToString();
+                name = ResourceString.GetDirectString(name);
+                if(!string.IsNullOrEmpty(name)) return name;
+
+                name = Registry.GetValue(DefaultTypePath, "", null)?.ToString();
+                if(!string.IsNullOrEmpty(name)) return name;
+                return null;
+            }
+            set
+            {
+                RegistryEx.DeleteValue(RegPath, "MenuText");
+                Registry.SetValue(DefaultTypePath, "FriendlyTypeName", value);
+            }
+        }
+
+        public string IconLocation
+        {
+            get => Registry.GetValue(RegPath, "IconPath", null)?.ToString();
+            set => Registry.SetValue(RegPath, "IconPath", value);
+        }
+
+        public Icon ItemIcon
+        {
+            get
+            {
+                if(TypeDefaultIcon != null && TypeDefaultIcon.StartsWith("@"))
+                    return ResourceIcon.GetExtensionIcon(Extension);
+
+                Icon icon;
+                string iconPath;
+                int iconIndex;
+                string value = IconLocation;
+                if(string.IsNullOrWhiteSpace(value)) value = DefaultTypeDefaultIcon;
+                if(!string.IsNullOrEmpty(value)) icon = ResourceIcon.GetIcon(value, out iconPath, out iconIndex);
+                else icon = ResourceIcon.GetIcon(iconPath = ItemFilePath, iconIndex = 0);
+                if(icon == null) icon = ResourceIcon.GetIcon(iconPath = "imageres.dll", iconIndex = 2);//图标资源不存在
+                IconPath = iconPath; IconIndex = iconIndex;
+                return icon;
+            }
+        }
+
+        public string IconPath { get; set; }
+        public int IconIndex { get; set; }
+
+        private object InitialData
+        {
+            get => Registry.GetValue(RegPath, "Data", null);
+            set => Registry.SetValue(RegPath, "Data", value);
+        }
+
+        public VisibleCheckBox ChkVisible { get; set; }
+        public MenuButton BtnShowMenu { get; set; }
+        public ChangeTextMenuItem TsiChangeText { get; set; }
+        public ChangeIconMenuItem TsiChangeIcon { get; set; }
+        public WebSearchMenuItem TsiSearch { get; set; }
+        public FilePropertiesMenuItem TsiFileProperties { get; set; }
+        public FileLocationMenuItem TsiFileLocation { get; set; }
+        public RegLocationMenuItem TsiRegLocation { get; set; }
+        public DeleteMeMenuItem TsiDeleteMe { get; set; }
+        readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu_Details);
+        readonly ToolStripMenuItem TsiEditData = new ToolStripMenuItem(AppString.Menu_InitialData);
+
+        private void InitializeComponents()
+        {
+            BtnShowMenu = new MenuButton(this);
+            ChkVisible = new VisibleCheckBox(this);
+            TsiSearch = new WebSearchMenuItem(this);
+            TsiChangeText = new ChangeTextMenuItem(this);
+            TsiChangeIcon = new ChangeIconMenuItem(this);
+            TsiFileLocation = new FileLocationMenuItem(this);
+            TsiFileProperties = new FilePropertiesMenuItem(this);
+            TsiRegLocation = new RegLocationMenuItem(this);
+            TsiDeleteMe = new DeleteMeMenuItem(this);
+
+            ContextMenuStrip.Items.AddRange(new ToolStripItem[] {TsiChangeText,
+                new ToolStripSeparator(), TsiChangeIcon, new ToolStripSeparator(),
+                TsiDetails, new ToolStripSeparator(), TsiDeleteMe });
+
+            TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(),
+                TsiEditData, TsiFileProperties, TsiFileLocation, TsiRegLocation });
+
+            TsiEditData.Click += (sender, e) => EditInitialData();
+            ContextMenuStrip.Opening += (sender, e) =>
+            {
+                TsiEditData.Visible = CanEditData;
+                TsiDeleteMe.Enabled = !IsFolderItem;
+            };
+        }
+
+        private void EditInitialData()
+        {
+            if(MessageBoxEx.Show(AppString.MessageBox_EditInitialData,
+                MessageBoxButtons.YesNo) != DialogResult.Yes) return;
+            using(InputDialog dlg = new InputDialog
+            {
+                Title = AppString.Menu_InitialData,
+                Text = this.InitialData?.ToString()
+            })
+            {
+                if(dlg.ShowDialog() == DialogResult.OK) this.InitialData = dlg.Text;
+            }
+        }
+
+        public void DeleteMe()
+        {
+            RegistryEx.DeleteKeyTree(this.RegPath);
+            RegistryEx.DeleteKeyTree(this.BuckupPath);
+            this.Dispose();
+        }
+    }
+}

+ 77 - 0
ContextMenuManager/Controls/ShellNewList.cs

@@ -0,0 +1,77 @@
+using BulePointLilac.Controls;
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class ShellNewList : MyList
+    {
+        private static readonly string[] ValueNames = { "NullFile", "Data", "FileName", "Directory" };
+
+        public void LoadItems()
+        {
+            this.ClearItems();
+            this.AddNewItem();
+            this.LoadCommonItems();
+        }
+
+        private void LoadCommonItems()
+        {
+            List<string> extensions = new List<string> { "Folder" };
+            using(RegistryKey root = Registry.ClassesRoot)
+            {
+                extensions.AddRange(Array.FindAll(root.GetSubKeyNames(), keyName => keyName.StartsWith(".")));
+                foreach(string extension in extensions)
+                {
+                    string typeName = FileExtensionDialog.GetTypeName(extension, false);
+                    if(typeName == null) continue;
+                    using(RegistryKey extKey = root.OpenSubKey(extension))
+                    using(RegistryKey tKey = extKey.OpenSubKey(typeName))
+                    {
+                        foreach(string part in ShellNewItem.SnParts)
+                        {
+                            string snPart = part;
+                            if(tKey != null) snPart = $@"{typeName}\{snPart}";
+                            using(RegistryKey snKey = extKey.OpenSubKey(snPart))
+                            {
+                                if(ValueNames.Any(valueName => snKey?.GetValue(valueName) != null))
+                                {
+                                    ShellNewItem item = new ShellNewItem(snKey.Name);
+                                    if(item.ItemText != null) { this.AddItem(item); break; }
+                                    else item.Dispose();
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        private void AddNewItem()
+        {
+            NewItem newItem = new NewItem();
+            this.AddItem(newItem);
+            newItem.NewItemAdd += (sender, e) =>
+            {
+                using(FileExtensionDialog dlg = new FileExtensionDialog())
+                {
+                    if(dlg.ShowDialog() != DialogResult.OK) return;
+                    string extension = dlg.Extension;
+                    string typeName = FileExtensionDialog.GetTypeName(extension, false);
+                    using(RegistryKey exKey = Registry.ClassesRoot.OpenSubKey(extension, true))
+                    {
+                        exKey.SetValue("", typeName);
+                        using(RegistryKey snKey = exKey.CreateSubKey("ShellNew", true))
+                        {
+                            snKey.SetValue("NullFile", string.Empty);
+                            this.InsertItem(new ShellNewItem(snKey.Name), GetItemIndex(newItem) + 1);
+                        }
+                    }
+                }
+            };
+        }
+    }
+}

+ 129 - 0
ContextMenuManager/Controls/ShellStoreDialog.cs

@@ -0,0 +1,129 @@
+using BulePointLilac.Methods;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class ShellStoreDialog : CommonDialog
+    {
+        public List<string> SelectedKeyNames { get; private set; }
+        public override void Reset() { }
+
+        protected override bool RunDialog(IntPtr hwndOwner)
+        {
+            using(ShellStoreForm frm = new ShellStoreForm())
+            {
+                bool flag = frm.ShowDialog() == DialogResult.OK;
+                if(flag) this.SelectedKeyNames = frm.SelectedItemNames;
+                return flag;
+            }
+        }
+
+        sealed class ShellStoreForm : SelectItemsForm
+        {
+            /// <summary>系统原有项目</summary>
+            private static readonly string[] SystemItemNames = { "Windows.aboutWindows", "Windows.AddColumns",
+                "Windows.AddDevice", "Windows.AddMediaServer", "Windows.AddNetworkLocation", "Windows.AddPrinter",
+                "Windows.AddRemovePrograms", "Windows.AddToFavorites", "Windows.Autoplay", "Windows.Backup",
+                "Windows.BitLocker", "Windows.BitLocker.Encrypt", "Windows.BitLocker.Manage",
+                "Windows.BitLocker.ResetPasswordPin", "Windows.burn", "Windows.Burn.Action", "Windows.change-passphrase",
+                "Windows.change-pin", "Windows.ChangeIndexedLocations", "Windows.ChooseColumns", "Windows.CleanUp",
+                "Windows.ClearAddressBarHistory", "Windows.ClearFrequentHistory", "Windows.clearRecentDocs",
+                "Windows.closewindow", "Windows.cmd", "Windows.cmdPromptAsAdministrator", "Windows.CompressedFile.extract",
+                "Windows.CompressedFile.ExtractTo", "Windows.CompressedFolder.extract", "Windows.CompressedItem.extract",
+                "Windows.Computer.Manage", "Windows.connectNetworkDrive", "Windows.copy", "Windows.copyaspath",
+                "Windows.CopyToBrowser", "Windows.CopyToMenu", "Windows.CscSync", "Windows.CscWorkOfflineOnline",
+                "Windows.cut", "Windows.Defragment", "Windows.delete", "Windows.Dialog.DisconnectNetworkDrive",
+                "Windows.DiscImage.burn", "Windows.DisconnectNetworkDrive", "Windows.DiskFormat",
+                "Windows.DriveFolder.DisconnectNetworkDrive", "Windows.edit", "Windows.Eject", "Windows.email",
+                "Windows.encrypt-bde", "Windows.encrypt-bde-elev", "Windows.Enqueue", "Windows.EraseDisc",
+                "Windows.EraseDisc.Action", "Windows.fax", "Windows.FinishBurn", "Windows.folderoptions",
+                "Windows.GroupByColumn", "Windows.help", "Windows.HideSelected", "Windows.HistoryVaultRestore",
+                "Windows.HomeGroupCPL", "Windows.HomeGroupJoin", "Windows.HomeGroupPassword", "Windows.HomeGroupSharing",
+                "Windows.HomeGroupTroubleshooter", "Windows.IconSize", "Windows.includeinlibrary", "Windows.invertselection",
+                "Windows.layout", "Windows.LibraryChangeIcon", "Windows.LibraryDefaultSaveLocation",
+                "Windows.LibraryIncludeInLibrary", "Windows.LibraryManageLibrary", "Windows.LibraryOptimizeLibraryFor",
+                "Windows.LibraryPublicSaveLocation", "Windows.LibraryRestoreDefaults", "Windows.LibrarySelChangeIcon",
+                "Windows.LibrarySelDefaultSaveLocation", "Windows.LibrarySelManageLibrary", "Windows.LibrarySelOptimizeLibraryFor",
+                "Windows.LibrarySelPublicSaveLocation", "Windows.LibrarySelRestoreDefaults", "Windows.LibrarySelShowInNavPane",
+                "Windows.LibraryShowInNavPane", "Windows.location.cmd", "Windows.location.cmdPromptAsAdministrator",
+                "Windows.location.opennewprocess", "Windows.location.opennewtab", "Windows.location.opennewwindow",
+                "Windows.location.Powershell", "Windows.location.PowershellAsAdmin", "Windows.manage-bde",
+                "Windows.manage-bde-elev", "Windows.MapNetworkDrive", "Windows.menubar", "Windows.ModernShare",
+                "Windows.mount", "Windows.MoveToBrowser", "Windows.MoveToMenu", "Windows.MultiVerb.cmd",
+                "Windows.MultiVerb.cmdPromptAsAdministrator", "Windows.MultiVerb.opennewprocess", "Windows.MultiVerb.opennewtab",
+                "Windows.MultiVerb.opennewwindow", "Windows.MultiVerb.Powershell", "Windows.MultiVerb.PowershellAsAdmin",
+                "Windows.navpane", "Windows.NavPaneExpandToCurrentFolder", "Windows.NavPaneShowAllFolders",
+                "Windows.NavPaneShowLibraries", "Windows.NetworkAndSharing", "Windows.NetworkViewDeviceWebpage",
+                "Windows.newfolder", "Windows.newitem", "Windows.open", "Windows.OpenContainingFolder.opencontaining",
+                "Windows.OpenControlPanel", "Windows.opennewprocess", "Windows.opennewtab", "Windows.opennewwindow",
+                "Windows.OpenPrinterServerProperty", "Windows.OpenPrintQueue", "Windows.OpenSearch.openfilelocation",
+                "Windows.OpenSearchViewSite", "Windows.OpenWith", "Windows.organize", "Windows.paste", "Windows.pastelink",
+                "Windows.PermanentDelete", "Windows.PinToHome", "Windows.pintostartscreen", "Windows.play", "Windows.playall",
+                "Windows.playmusic", "Windows.PowershellAsAdmin", "Windows.previewpane", "Windows.print", "Windows.properties",
+                "Windows.readingpane", "Windows.recycle", "Windows.RecycleBin.Empty", "Windows.RecycleBin.Location.properties",
+                "Windows.RecycleBin.properties", "Windows.RecycleBin.RestoreAll", "Windows.RecycleBin.RestoreItems",
+                "Windows.RecycleBin.Selection.properties", "Windows.redo", "Windows.remotedesktop", "Windows.RemoveMediaServer",
+                "Windows.removeproperties", "Windows.rename", "Windows.RibbonDelete", "Windows.RibbonPermissionsDialog",
+                "Windows.RibbonShare", "Windows.RibbonSync.MakeAvailableOffline", "Windows.RibbonSync.SyncThisFolder",
+                "Windows.RibbonSync.WorkOfflineOnline", "Windows.rotate270", "Windows.rotate90", "Windows.runas",
+                "Windows.runasuser", "Windows.SearchActiveDirectory", "Windows.SearchClearMru", "Windows.SearchCloseTab",
+                "Windows.SearchFilterDate", "Windows.SearchFilterKind", "Windows.SearchFilterMoreProperties",
+                "Windows.SearchFilterSize", "Windows.SearchMru", "Windows.SearchOpenLocation", "Windows.SearchOptionCompressed",
+                "Windows.SearchOptionContents", "Windows.SearchOptionDeep", "Windows.SearchOptionShallow",
+                "Windows.SearchOptionSystem", "Windows.SearchSave", "Windows.SearchSendTo", "Windows.SearchSendToComputer",
+                "Windows.selectall", "Windows.SelectionCheckboxes", "Windows.selectMode", "Windows.selectnone",
+                "Windows.separator", "Windows.setdesktopwallpaper", "Windows.Share", "Windows.SharePrivate",
+                "Windows.ShareSpecificUsers", "Windows.Shortcut.opencontaining", "Windows.ShowFileExtensions",
+                "Windows.ShowHiddenFiles", "Windows.SizeAllColumns", "Windows.slideshow", "Windows.SortAscending",
+                "Windows.SortByColumn", "Windows.SortDescending", "Windows.SortGroupsAscending", "Windows.SortGroupsDescending",
+                "Windows.StartScan", "Windows.statusbar", "Windows.Sync", "Windows.SystemProperties", "Windows.taskbarpin",
+                "Windows.ToggleRecycleConfirmations", "Windows.topviewrestoredefault", "Windows.Troubleshoot", "Windows.undo",
+                "Windows.UpdatePrinterDriver", "Windows.v2.Powershell", "Windows.View.OptionsGallery",
+                "Windows.ViewRemotePrinters", "Windows.zip", "Windows.Zip.Action"
+            };
+
+            public List<string> SelectedItemNames { get; private set; } = new List<string>();
+
+            public ShellStoreForm()
+            {
+                this.Text = AppString.Text_CheckReference;
+                btnOk.Click += (sender, e) => GetSelectedItems();
+                LoadItems();
+            }
+
+            private void LoadItems()
+            {
+                using(var shellKey = RegistryEx.GetRegistryKey(ShellItem.CommandStorePath))
+                {
+                    Array.ForEach(Array.FindAll(shellKey.GetSubKeyNames(), itemName =>
+                        !SystemItemNames.Contains(itemName, StringComparer.OrdinalIgnoreCase)), itemName =>
+                        {
+                            string regPath = $@"{ShellItem.CommandStorePath}\{itemName}";
+                            list.AddItem(new StoreShellItem(regPath));
+                            RegTrustedInstaller.TakeRegTreeOwnerShip(regPath);
+                        });
+                }
+            }
+
+            private void GetSelectedItems()
+            {
+                foreach(StoreShellItem item in list.Controls)
+                    if(item.IsSelected) SelectedItemNames.Add(item.KeyName);
+            }
+
+            sealed class StoreShellItem : ShellItem
+            {
+                public StoreShellItem(string regPath) : base(regPath)
+                {
+                    this.AddCtr(chkSelected);
+                    this.SetCtrIndex(chkSelected, 2);
+                }
+                public bool IsSelected => chkSelected.Checked;
+                readonly CheckBox chkSelected = new CheckBox { AutoSize = true };
+            }
+        }
+    }
+}

+ 432 - 0
ContextMenuManager/Controls/ShellSubMenuDialog.cs

@@ -0,0 +1,432 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+using static Microsoft.Win32.Registry;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class ShellSubMenuDialog : CommonDialog
+    {
+        public Icon Icon { get; set; }
+        public string Text { get; set; }
+        public override void Reset() { }
+        protected override bool RunDialog(IntPtr hwndOwner) { return false; }
+
+        /// <param name="parentPath">子菜单的父菜单的注册表路径</param>
+        public void ShowDialog(string parentPath)
+        {
+            using(ShellSubMenuForm frm = new ShellSubMenuForm(parentPath))
+            {
+                frm.Text = this.Text;
+                frm.Icon = this.Icon;
+                frm.ShowDialog();
+            }
+        }
+
+        sealed class ShellSubMenuForm : Form
+        {
+            /// <param name="parentPath">子菜单的父菜单的注册表路径</param>
+            public ShellSubMenuForm(string parentPath)
+            {
+                this.ShowInTaskbar = false;
+                this.StartPosition = FormStartPosition.CenterParent;
+                this.MinimumSize = this.Size = new Size(646, 389).DpiZoom();
+                LstSubItems = new MyListBox { Dock = DockStyle.Fill, Parent = this };
+                string value = GetValue(parentPath, "SubCommands", null)?.ToString();
+                if(string.IsNullOrWhiteSpace(value))
+                {
+                    using(var shellKey = RegistryEx.GetRegistryKey($@"{parentPath}\shell"))
+                    {
+                        if(shellKey != null && shellKey.GetSubKeyNames().Length > 0)
+                        {
+                            new MultiItemsList(LstSubItems).LoadItems(parentPath);
+                            return;
+                        }
+                        else
+                        {
+                            using(SubMenuModeForm frm = new SubMenuModeForm())
+                            {
+                                frm.ShowDialog();
+                                if(frm.SubMenuMode == 1)
+                                {
+                                    new MultiItemsList(LstSubItems).LoadItems(parentPath);
+                                    return;
+                                }
+                            }
+                        }
+                    }
+                }
+                new CommonMultiItemsList(LstSubItems).LoadItems(parentPath);
+            }
+
+            readonly MyListBox LstSubItems;
+
+            sealed class CommonMultiItemsList : MyList
+            {
+                readonly List<string> SubKeyNames = new List<string>();
+                /// <summary>子菜单的父菜单的注册表路径</summary>
+                private string ParentPath { get; set; }
+                /// <summary>菜单所处环境注册表路径</summary>
+                private string ScenePath => RegistryEx.GetParentPath(RegistryEx.GetParentPath(ParentPath));
+
+                readonly NewItem newItem = new NewItem();
+                readonly AddCommonButton btnAddCommon = new AddCommonButton();
+                readonly PictureButton btnAddExisting = new PictureButton(AppImage.AddExisting);
+                readonly PictureButton btnAddSeparator = new PictureButton(AppImage.AddSeparator);
+
+                public CommonMultiItemsList(MyListBox owner) : base(owner)
+                {
+                    this.AddItem(newItem);
+                    newItem.AddCtrs(new[] { btnAddCommon, btnAddExisting, btnAddSeparator });
+                    MyToolTip.SetToolTip(btnAddExisting, AppString.Tip_AddExistingItems);
+                    MyToolTip.SetToolTip(btnAddSeparator, AppString.Tip_AddSeparator);
+                    newItem.NewItemAdd += (sender, e) => AddNewItem();
+                    btnAddCommon.MouseDown += (sender, e) => AddCommonItems();
+                    btnAddExisting.MouseDown += (sender, e) => AddExistingItems();
+                    btnAddSeparator.MouseDown += (sender, e) => AddSeparator();
+                }
+
+                /// <param name="parentPath">子菜单的父菜单的注册表路径</param>
+                public void LoadItems(string parentPath)
+                {
+                    this.ParentPath = parentPath;
+                    string value = GetValue(ParentPath, "SubCommands", null)?.ToString();
+                    Array.ForEach(value.Split(';'), cmd => SubKeyNames.Add(cmd.TrimStart()));
+                    SubKeyNames.RemoveAll(string.IsNullOrEmpty);
+
+                    using(var shellKey = RegistryEx.GetRegistryKey(ShellItem.CommandStorePath, false, true))
+                    {
+                        foreach(string keyName in SubKeyNames)
+                        {
+                            using(var key = shellKey.OpenSubKey(keyName))
+                            {
+                                MyListItem item;
+                                if(key != null) item = new SubShellItem(this, keyName);
+                                else if(keyName == "|") item = new SeparatorItem(this);
+                                else item = new InvalidItem(this, keyName);
+                                this.AddItem(item);
+                            }
+                        }
+                    }
+                }
+
+                private void AddNewItem()
+                {
+                    if(!CanAddMore()) return;
+                    using(NewShellDialog dlg = new NewShellDialog
+                    {
+                        ScenePath = this.ScenePath,
+                        ShellPath = ShellItem.CommandStorePath
+                    })
+                    {
+                        if(dlg.ShowDialog() != DialogResult.OK) return;
+                        SubKeyNames.Add(dlg.NewItemKeyName);
+                        WriteRegistry();
+                        SubShellItem item = new SubShellItem(this, dlg.NewItemKeyName);
+                        this.AddItem(item);
+                        this.HoveredItem = item;
+                    }
+                }
+
+                private void AddCommonItems()
+                {
+                    if(!CanAddMore()) return;
+                    using(ShellCommonDialog dlg = new ShellCommonDialog
+                    {
+                        ScenePath = this.ScenePath,
+                        ShellPath = ShellItem.CommandStorePath
+                    })
+                    {
+                        if(dlg.ShowDialog() == DialogResult.OK)
+                        {
+                            dlg.SelectedShellPaths.ForEach(path =>
+                            {
+                                string keyName = RegistryEx.GetKeyName(path);
+                                this.AddItem(new SubShellItem(this, keyName));
+                                SubKeyNames.Add(keyName);
+                            });
+                            WriteRegistry();
+                        }
+                    }
+                }
+
+                private void AddExistingItems()
+                {
+                    if(!CanAddMore()) return;
+                    using(ShellStoreDialog dlg = new ShellStoreDialog())
+                    {
+                        if(dlg.ShowDialog() != DialogResult.OK) return;
+                        dlg.SelectedKeyNames.ForEach(keyName =>
+                        {
+                            SubShellItem item = new SubShellItem(this, keyName);
+                            this.AddItem(item);
+                            this.SubKeyNames.Add(keyName);
+                            WriteRegistry();
+                        });
+                        this.HoveredItem = (MyListItem)Controls[Controls.Count - 1];
+                    }
+                }
+
+                private void AddSeparator()
+                {
+                    this.SubKeyNames.Add("|");
+                    WriteRegistry();
+                    SeparatorItem item = new SeparatorItem(this);
+                    this.AddItem(item);
+                    this.HoveredItem = item;
+                }
+
+                private bool CanAddMore()
+                {
+                    int count = 0;
+                    foreach(Control item in Controls)
+                    {
+                        if(item.GetType() == typeof(SubShellItem)) count++;
+                    }
+                    bool flag = count < 16;
+                    if(!flag) MessageBoxEx.Show(AppString.MessageBox_CannotAddNewItem);
+                    return flag;
+                }
+
+                private void WriteRegistry()
+                {
+                    SetValue(ParentPath, "SubCommands", string.Join(";", SubKeyNames));
+                }
+
+                private static void MoveItem(MyListItem item, CommonMultiItemsList list, bool isUp)
+                {
+                    int index = list.GetItemIndex(item);
+                    if(isUp)
+                    {
+                        if(index > 1)
+                        {
+                            list.SetItemIndex(item, index - 1);
+                            list.SubKeyNames.Reverse(index - 2, 2);
+                        }
+                    }
+                    else
+                    {
+                        if(index < list.Controls.Count - 1)
+                        {
+                            list.SetItemIndex(item, index + 1);
+                            list.SubKeyNames.Reverse(index - 1, 2);
+                        }
+                    }
+                    list.WriteRegistry();
+                }
+
+                private static void RemoveItem(CommonMultiItemsList list, MyListItem item)
+                {
+                    int index = list.GetItemIndex(item);
+                    list.Controls.Remove(item);
+                    list.Controls[index - 1].Focus();
+                    list.SubKeyNames.RemoveAt(index - 1);
+                    list.WriteRegistry();
+                    item.Dispose();
+                }
+
+                sealed class SubShellItem : ShellItem, IBtnMoveUpDownItem
+                {
+                    public SubShellItem(CommonMultiItemsList list, string keyName) : base($@"{CommandStorePath}\{keyName}")
+                    {
+                        this.Owner = list;
+                        BtnMoveDown = new MoveButton(this, false);
+                        BtnMoveUp = new MoveButton(this, true);
+                        BtnMoveUp.MouseDown += (sender, e) => MoveItem(this, Owner, true);
+                        BtnMoveDown.MouseDown += (sender, e) => MoveItem(this, Owner, false);
+                        ContextMenuStrip.Items.Remove(TsiDeleteMe);
+                        ContextMenuStrip.Items.Add(TsiDeleteRef);
+                        TsiDeleteRef.Click += (sender, e) => DeleteReference();
+                    }
+
+                    protected override bool IsSubItem => true;
+
+                    readonly ToolStripMenuItem TsiDeleteRef = new ToolStripMenuItem(AppString.Menu_DeleteReference);
+                    public CommonMultiItemsList Owner { get; private set; }
+                    public MoveButton BtnMoveUp { get; set; }
+                    public MoveButton BtnMoveDown { get; set; }
+
+                    private void DeleteReference()
+                    {
+                        if(MessageBoxEx.Show(AppString.MessageBox_ConfirmDeleteReference,
+                            MessageBoxButtons.YesNo) == DialogResult.Yes)
+                        {
+                            RemoveItem(Owner, this);
+                        }
+                    }
+
+                    public override void DeleteMe()
+                    {
+                        if(MessageBoxEx.Show(AppString.MessageBox_ConfirmDeleteReferenced,
+                            MessageBoxButtons.YesNo) == DialogResult.Yes) base.DeleteMe();
+                    }
+                }
+
+                sealed class SeparatorItem : MyListItem, IBtnDeleteItem, IBtnMoveUpDownItem
+                {
+                    public SeparatorItem(CommonMultiItemsList list)
+                    {
+                        this.Owner = list;
+                        this.Text = AppString.Text_Separator;
+                        this.Image = AppImage.Separator;
+                        BtnDelete = new DeleteButton(this);
+                        BtnMoveDown = new MoveButton(this, false);
+                        BtnMoveUp = new MoveButton(this, true);
+                        BtnMoveUp.MouseDown += (sender, e) => MoveItem(this, Owner, true);
+                        BtnMoveDown.MouseDown += (sender, e) => MoveItem(this, Owner, false);
+                        MyToolTip.SetToolTip(BtnDelete, AppString.Tip_Separator);
+                    }
+
+                    public DeleteButton BtnDelete { get; set; }
+
+                    public CommonMultiItemsList Owner { get; private set; }
+                    public MoveButton BtnMoveUp { get; set; }
+                    public MoveButton BtnMoveDown { get; set; }
+
+                    public void DeleteMe()
+                    {
+                        RemoveItem(Owner, this);
+                        this.Dispose();
+                    }
+                }
+
+                sealed class InvalidItem : MyListItem, IBtnDeleteItem, IBtnMoveUpDownItem
+                {
+                    public InvalidItem(CommonMultiItemsList list, string keyName)
+                    {
+                        this.Owner = list;
+                        this.Text = $"{AppString.Text_InvalidItem} {keyName}";
+                        this.Image = AppImage.NotFound.ToTransparent();
+                        BtnDelete = new DeleteButton(this);
+                        BtnMoveDown = new MoveButton(this, false);
+                        BtnMoveUp = new MoveButton(this, true);
+                        BtnMoveUp.MouseDown += (sender, e) => MoveItem(this, Owner, true);
+                        BtnMoveDown.MouseDown += (sender, e) => MoveItem(this, Owner, false);
+                        MyToolTip.SetToolTip(BtnDelete, AppString.Tip_InvalidItem);
+                    }
+
+                    public DeleteButton BtnDelete { get; set; }
+                    public CommonMultiItemsList Owner { get; private set; }
+                    public MoveButton BtnMoveUp { get; set; }
+                    public MoveButton BtnMoveDown { get; set; }
+
+                    public void DeleteMe()
+                    {
+                        RemoveItem(Owner, this);
+                        this.Dispose();
+                    }
+                }
+            }
+
+            sealed class MultiItemsList : MyList
+            {
+                public MultiItemsList(MyListBox owner) : base(owner)
+                {
+                    this.AddItem(newItem);
+                    newItem.AddCtr(btnAddCommon);
+                    newItem.NewItemAdd += (sender, e) => AddNewItem();
+                    btnAddCommon.MouseDown += (sender, e) => AddCommonItems();
+                }
+
+                readonly NewItem newItem = new NewItem();
+                readonly AddCommonButton btnAddCommon = new AddCommonButton();
+                /// <summary>子菜单的父菜单的注册表路径</summary>
+                public string ParentPath { get; set; }
+                /// <summary>子菜单的Shell项注册表路径</summary>
+                private string ShellPath => $@"{ParentPath}\shell";
+                /// <summary>菜单所处环境注册表路径</summary>
+                private string ScenePath => RegistryEx.GetParentPath(RegistryEx.GetParentPath(ParentPath));
+
+
+                public void LoadItems(string parentPath)
+                {
+                    this.ParentPath = parentPath;
+                    using(var shellKey = RegistryEx.GetRegistryKey(ShellPath))
+                    {
+                        if(shellKey == null) return;
+                        RegTrustedInstaller.TakeRegTreeOwnerShip(shellKey.Name);
+                        Array.ForEach(shellKey.GetSubKeyNames(), keyName =>
+                        {
+                            this.AddItem(new SubShellItem($@"{ShellPath}\{keyName}"));
+                        });
+                    }
+                }
+
+                private void AddNewItem()
+                {
+                    if(!CanAddMore()) return;
+                    using(NewShellDialog dlg = new NewShellDialog
+                    {
+                        ScenePath = this.ScenePath,
+                        ShellPath = this.ShellPath
+                    })
+                    {
+                        if(dlg.ShowDialog() == DialogResult.OK)
+                            this.InsertItem(new SubShellItem(dlg.NewItemRegPath), GetItemIndex(newItem) + 1);
+                    }
+                }
+
+                private void AddCommonItems()
+                {
+                    if(!CanAddMore()) return;
+                    using(ShellCommonDialog dlg = new ShellCommonDialog
+                    {
+                        ScenePath = this.ScenePath,
+                        ShellPath = this.ShellPath
+                    })
+                    {
+                        if(dlg.ShowDialog() == DialogResult.OK)
+                        {
+                            dlg.SelectedShellPaths.ForEach(path => this.AddItem(new SubShellItem(path)));
+                            this.SortItemByText();
+                            this.SetItemIndex(newItem, 0);
+                        }
+                    }
+                }
+
+                private bool CanAddMore()
+                {
+                    int count = 0;
+                    foreach(Control item in Controls)
+                    {
+                        if(item.GetType() == typeof(SubShellItem)) count++;
+                    }
+                    bool flag = count < 16;
+                    if(!flag) MessageBoxEx.Show(AppString.MessageBox_CannotAddNewItem);
+                    return flag;
+                }
+
+                sealed class SubShellItem : ShellItem
+                {
+                    public SubShellItem(string regPath) : base(regPath)
+                    {
+                        TsiOtherAttributes.DropDownItems.Add(tsiShowSeparator);
+                        tsiShowSeparator.Click += (sender, e) => ShowSeparator = !tsiShowSeparator.Checked;
+                        ContextMenuStrip.Opening += (sender, e) => tsiShowSeparator.Checked = ShowSeparator;
+                    }
+
+                    protected override bool IsSubItem => true;
+
+                    private bool ShowSeparator
+                    {
+                        get
+                        {
+                            int value = Convert.ToInt32(GetValue(RegPath, "CommandFlags", 0)) % 64;
+                            return value >= 32 && value < 56;
+                        }
+                        set
+                        {
+                            if(value) SetValue(RegPath, "CommandFlags", 32, Microsoft.Win32.RegistryValueKind.DWord);
+                            else RegistryEx.DeleteValue(RegPath, "CommandFlags");
+                        }
+                    }
+
+                    readonly ToolStripMenuItem tsiShowSeparator = new ToolStripMenuItem(AppString.Menu_ShowSeparator);
+                }
+            }
+        }
+    }
+}

+ 57 - 0
ContextMenuManager/Controls/SubMenuModeForm.cs

@@ -0,0 +1,57 @@
+using BulePointLilac.Methods;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    class SubMenuModeForm : Form
+    {
+        public SubMenuModeForm()
+        {
+            this.Text = AppString.General_AppName;
+            this.FormBorderStyle = FormBorderStyle.FixedSingle;
+            this.StartPosition = FormStartPosition.CenterParent;
+            this.ControlBox = this.ShowIcon = this.ShowInTaskbar = false;
+            this.Font = new Font(SystemFonts.MessageBoxFont.FontFamily, 9F);
+            this.Controls.AddRange(new Control[] { pnlTop, btnOne, btnTwo });
+            pnlTop.Controls.Add(lblInfo);
+            int a = 20.DpiZoom();
+            this.ClientSize = new Size(lblInfo.Width + 2 * a, lblInfo.Height + btnOne.Height + 3 * a);
+            lblInfo.Location = new Point(a, a);
+            pnlTop.Height = lblInfo.Bottom + a;
+            btnOne.Top = btnTwo.Top = pnlTop.Bottom + a / 2;
+            btnTwo.Left = pnlTop.Width - btnTwo.Width - a;
+            btnOne.Left = btnTwo.Left - btnOne.Width - a;
+            btnOne.Click += (sender, e) => SubMenuMode = 1;
+            btnTwo.Click += (sender, e) => SubMenuMode = 2;
+        }
+
+        public int SubMenuMode { get; private set; }
+
+        readonly Label lblInfo = new Label
+        {
+            AutoSize = true,
+            Text = AppString.Text_SelectSubMenuMode,
+        };
+
+        readonly Panel pnlTop = new Panel
+        {
+            BackColor = Color.White,
+            Dock = DockStyle.Top
+        };
+
+        readonly Button btnOne = new Button
+        {
+            DialogResult = DialogResult.OK,
+            AutoSize = true,
+            Text = "①",
+        };
+
+        readonly Button btnTwo = new Button
+        {
+            DialogResult = DialogResult.OK,
+            AutoSize = true,
+            Text = "②",
+        };
+    }
+}

+ 100 - 0
ContextMenuManager/Controls/ThirdRulesList.cs

@@ -0,0 +1,100 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using Microsoft.Win32;
+using System;
+using System.IO;
+using System.Text;
+using System.Xml;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class ThirdRulesList : MyList
+    {
+        public void LoadItems()
+        {
+            this.ClearItems();
+            XmlDocument doc1 = new XmlDocument();
+            if(!File.Exists(Program.AppDataThirdRulesDicPath))
+            {
+                File.WriteAllText(Program.AppDataThirdRulesDicPath, Properties.Resources.ThirdRulesDic, Encoding.UTF8);
+            }
+            doc1.Load(Program.AppDataThirdRulesDicPath);
+            if(File.Exists(Program.ThirdRulesDicPath))
+            {
+                XmlDocument doc2 = new XmlDocument();
+                doc2.Load(Program.ThirdRulesDicPath);
+                foreach(XmlNode xn in doc2.DocumentElement.ChildNodes)
+                {
+                    XmlNode node = doc1.ImportNode(xn, true);
+                    doc1.DocumentElement.AppendChild(node);
+                }
+            }
+            foreach(XmlElement groupXE in doc1.DocumentElement.ChildNodes)
+            {
+                if(!GuidInfo.TryGetGuid(groupXE.GetAttribute("Guid"), out Guid guid)
+                    && !groupXE.HasAttribute("Common")) continue;
+
+                GroupPathItem groupItem = new GroupPathItem
+                {
+                    Text = groupXE.GetAttribute("Text"),
+                    TargetPath = groupXE.GetAttribute("RegPath"),
+                    PathType = ObjectPath.PathType.Registry,
+                    Image = GuidInfo.GetImage(guid),
+                };
+                if(string.IsNullOrWhiteSpace(groupItem.Text)) groupItem.Text = GuidInfo.GetText(guid);
+                this.AddItem(groupItem);
+
+                foreach(XmlElement itemXE in groupXE.ChildNodes)
+                {
+                    RegRuleItem.ItemInfo itemInfo = new RegRuleItem.ItemInfo
+                    {
+                        Text = itemXE.GetAttribute("Text"),
+                        Tip = itemXE.GetAttribute("Tip"),
+                        RestartExplorer = itemXE.HasAttribute("RestartExplorer"),
+                    };
+
+                    XmlNodeList ruleXNList = itemXE.GetElementsByTagName("Rule");//Rules
+                    RegRuleItem.RegRule[] rules = new RegRuleItem.RegRule[ruleXNList.Count];
+                    for(int i = 0; i < ruleXNList.Count; i++)
+                    {
+                        XmlElement ruleXE = (XmlElement)ruleXNList[i];
+                        rules[i] = new RegRuleItem.RegRule
+                        {
+                            RegPath = ruleXE.GetAttribute("RegPath"),
+                            ValueName = ruleXE.GetAttribute("ValueName"),
+                            TurnOnValue = ruleXE.GetAttribute("On"),
+                            TurnOffValue = ruleXE.GetAttribute("Off"),
+                            ValueKind = GetValueKind(ruleXE.GetAttribute("ValueKind"))
+                        };
+                        if(string.IsNullOrEmpty(rules[i].RegPath)) rules[i].RegPath = groupItem.TargetPath;
+                        else if(rules[i].RegPath.StartsWith("\\")) rules[i].RegPath = groupItem.TargetPath + rules[i].RegPath;
+                    }
+
+                    this.AddItem(new RegRuleItem(rules, itemInfo) { FoldGroupItem = groupItem });
+                }
+                groupItem.IsFold = true;
+            }
+        }
+
+        private static RegistryValueKind GetValueKind(string data)
+        {
+            switch(data.ToUpper())
+            {
+                case "REG_SZ":
+                    return RegistryValueKind.String;
+                case "REG_BINARY":
+                    return RegistryValueKind.Binary;
+                case "REG_DWORD":
+                    return RegistryValueKind.DWord;
+                case "REG_QWORD":
+                    return RegistryValueKind.QWord;
+                case "REG_MULTI_SZ":
+                    return RegistryValueKind.MultiString;
+                case "REG_EXPAND_SZ":
+                    return RegistryValueKind.ExpandString;
+                default:
+                    return RegistryValueKind.DWord;
+            }
+        }
+    }
+}

+ 121 - 0
ContextMenuManager/Controls/WinXItem.cs

@@ -0,0 +1,121 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class WinXItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem,
+        ITsiTextItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiDeleteItem, IFoldSubItem
+    {
+        private static readonly IWshRuntimeLibrary.WshShell WshShell = new IWshRuntimeLibrary.WshShell();
+
+        public WinXItem(string filePath, IFoldGroupItem group)
+        {
+            InitializeComponents();
+            this.FoldGroupItem = group;
+            this.FilePath = filePath;
+        }
+
+        private string filePath;
+        public string FilePath
+        {
+            get => filePath;
+            set
+            {
+                filePath = value;
+                this.Shortcut = WshShell.CreateShortcut(value);
+                this.Text = this.ItemText;
+                this.Image = this.ItemIcon.ToBitmap();
+                ChkVisible.Checked = this.ItemVisible;
+            }
+        }
+
+        public string ItemText
+        {
+            get
+            {
+                string name = Shortcut.Description.Trim();
+                if(name == string.Empty) name = WinXList.GetMenuName(FilePath);
+                if(name == string.Empty) name = Path.GetFileNameWithoutExtension(FilePath);
+                return name;
+            }
+            set
+            {
+                Shortcut.Description = value;
+                Shortcut.Save();
+                ExplorerRestarter.NeedRestart = true;
+            }
+        }
+
+        public bool ItemVisible
+        {
+            get => (File.GetAttributes(FilePath) & FileAttributes.Hidden) != FileAttributes.Hidden;
+            set
+            {
+                FileAttributes attributes = File.GetAttributes(FilePath);
+                if(value) attributes &= ~FileAttributes.Hidden;
+                else attributes |= FileAttributes.Hidden;
+                File.SetAttributes(FilePath, attributes);
+                ExplorerRestarter.NeedRestart = true;
+            }
+        }
+
+        private string IconLocation
+        {
+            get
+            {
+                if(Shortcut.IconLocation.StartsWith(","))
+                    Shortcut.IconLocation = $"{Shortcut.TargetPath}{Shortcut.IconLocation}";
+                return Shortcut.IconLocation;
+            }
+        }
+
+        private IWshRuntimeLibrary.IWshShortcut Shortcut;
+        private Icon ItemIcon => ResourceIcon.GetIcon(IconLocation) ?? Icon.ExtractAssociatedIcon(Shortcut.TargetPath);
+        public string SearchText => $"{AppString.SideBar_WinX} {Text}";
+
+        public string ItemFilePath
+        {
+            get
+            {
+                if(File.Exists(Shortcut.TargetPath)) return Shortcut.TargetPath;
+                else return FilePath;
+            }
+        }
+
+        public IFoldGroupItem FoldGroupItem { get; set; }
+        public VisibleCheckBox ChkVisible { get; set; }
+        public MenuButton BtnShowMenu { get; set; }
+        public ChangeTextMenuItem TsiChangeText { get; set; }
+        public WebSearchMenuItem TsiSearch { get; set; }
+        public FilePropertiesMenuItem TsiFileProperties { get; set; }
+        public FileLocationMenuItem TsiFileLocation { get; set; }
+        public DeleteMeMenuItem TsiDeleteMe { get; set; }
+        readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu_Details);
+
+        private void InitializeComponents()
+        {
+            BtnShowMenu = new MenuButton(this);
+            ChkVisible = new VisibleCheckBox(this);
+            TsiChangeText = new ChangeTextMenuItem(this);
+            TsiSearch = new WebSearchMenuItem(this);
+            TsiFileLocation = new FileLocationMenuItem(this);
+            TsiFileProperties = new FilePropertiesMenuItem(this);
+            TsiDeleteMe = new DeleteMeMenuItem(this);
+
+            ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText,
+                new ToolStripSeparator(), TsiDetails, new ToolStripSeparator(), TsiDeleteMe });
+
+            TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch,
+                new ToolStripSeparator(), TsiFileProperties, TsiFileLocation });
+        }
+
+        public void DeleteMe()
+        {
+            File.Delete(this.FilePath);
+            this.Dispose();
+        }
+    }
+}

+ 52 - 0
ContextMenuManager/Controls/WinXList.cs

@@ -0,0 +1,52 @@
+using BulePointLilac.Controls;
+using BulePointLilac.Methods;
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class WinXList : MyList
+    {
+        public static readonly string WinXPath = Environment.ExpandEnvironmentVariables(@"%LocalAppData%\Microsoft\Windows\WinX");
+        private static readonly Dictionary<string, IniReader> DesktopIniReaders = new Dictionary<string, IniReader>();
+
+        public static string GetMenuName(string filePath)
+        {
+            string dirPath = Path.GetDirectoryName(filePath);
+            string fileName = Path.GetFileName(filePath);
+            if(DesktopIniReaders.TryGetValue(dirPath, out IniReader reader))
+            {
+                string name = reader.GetValue("LocalizedFileNames", fileName);
+                name = ResourceString.GetDirectString(name);
+                return name;
+            }
+            else return string.Empty;
+        }
+
+        public void LoadItems()
+        {
+            this.ClearItems();
+            DesktopIniReaders.Clear();
+            Array.ForEach(new DirectoryInfo(WinXPath).GetDirectories(), di => LoadSubDirItems(di));
+        }
+
+        private void LoadSubDirItems(DirectoryInfo di)
+        {
+            GroupPathItem groupItem = new GroupPathItem
+            {
+                TargetPath = di.FullName,
+                Text = Path.GetFileNameWithoutExtension(di.FullName),
+                Image = ResourceIcon.GetFolderIcon(di.FullName).ToBitmap(),
+                PathType = ObjectPath.PathType.Directory
+            };
+            this.AddItem(groupItem);
+            string iniPath = $@"{di.FullName}\desktop.ini";
+            DesktopIniReaders.Add(di.FullName, new IniReader(iniPath));
+            Array.ForEach(di.GetFiles(), fi =>
+            {
+                if(fi.Extension.ToLower() == ".lnk") this.AddItem(new WinXItem(fi.FullName, groupItem));
+            });
+        }
+    }
+}

+ 190 - 0
ContextMenuManager/GuidInfo.cs

@@ -0,0 +1,190 @@
+using BulePointLilac.Methods;
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Text;
+
+namespace ContextMenuManager
+{
+    static class GuidInfo
+    {
+        public static readonly string[] ClsidPaths = {
+            @"HKEY_CLASSES_ROOT\CLSID",
+            @"HKEY_CLASSES_ROOT\WOW6432Node\CLSID",
+            @"HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\CLSID",
+        };
+
+        public struct IconLocation
+        {
+            public string IconPath { get; set; }
+            public int IconIndex { get; set; }
+        }
+
+        private static IniReader AppDic;
+        private static readonly IniReader UserDic = new IniReader(Program.GuidInfosDicPath);
+        private static readonly Dictionary<Guid, IconLocation> IconLocationDic = new Dictionary<Guid, IconLocation>();
+        private static readonly Dictionary<Guid, string> FilePathDic = new Dictionary<Guid, string>();
+        private static readonly Dictionary<Guid, string> ItemTextDic = new Dictionary<Guid, string>();
+
+        private static readonly Dictionary<Guid, Image> ItemImageDic = new Dictionary<Guid, Image>();
+
+        public static bool TryGetGuid(string value, out Guid guid) => TryGetGuid(value, out guid, out _);
+
+        public static bool TryGetGuid(string value, out Guid guid, out string guidPath)
+        {
+            guidPath = null;
+            if(!Guid.TryParse(value, out guid)) return false;
+            foreach(string path in ClsidPaths)
+            {
+                using(RegistryKey key = RegistryEx.GetRegistryKey($@"{path}\{guid:B}"))
+                {
+                    if(key != null)
+                    {
+                        guidPath = key.Name;
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        private static bool TryGetValue(string section, string key, out string value)
+        {
+            if(!File.Exists(Program.AppDataGuidInfosDicPath))
+                File.WriteAllText(Program.AppDataGuidInfosDicPath, Properties.Resources.GuidInfosDic, Encoding.UTF8);
+            if(AppDic == null)
+                AppDic = new IniReader(Program.AppDataGuidInfosDicPath);
+            if(AppDic.TryGetValue(section, key, out value)) return true;
+            if(UserDic != null && UserDic.TryGetValue(section, key, out value)) return true;
+            return false;
+        }
+
+        public static string GetFilePath(Guid guid)
+        {
+            string filePath = null;
+            if(guid.Equals(Guid.Empty)) return filePath;
+            if(FilePathDic.ContainsKey(guid)) filePath = FilePathDic[guid];
+            else
+            {
+                foreach(string clsidPath in ClsidPaths)
+                {
+                    using(RegistryKey guidKey = RegistryEx.GetRegistryKey($@"{clsidPath}\{guid:B}"))
+                    {
+                        if(guidKey == null) continue;
+                        foreach(string keyName in new[] { "InprocServer32", "LocalServer32" })
+                        {
+                            using(RegistryKey key = guidKey.OpenSubKey(keyName))
+                            {
+                                string value = key?.GetValue("")?.ToString();
+                                filePath = ObjectPath.ExtractFilePath(value);
+                                if(File.Exists(filePath)) break;
+                            }
+                        }
+                    }
+                }
+                FilePathDic.Add(guid, filePath);
+            }
+            return filePath;
+        }
+
+        public static string GetText(Guid guid)
+        {
+            string itemText = null;
+            if(guid.Equals(Guid.Empty)) return itemText;
+            if(ItemTextDic.ContainsKey(guid)) itemText = ItemTextDic[guid];
+            else
+            {
+                if(TryGetValue(guid.ToString(), "Text", out itemText))
+                {
+                    itemText = GetAbsStr(guid, itemText, true);
+                }
+                if(string.IsNullOrWhiteSpace(itemText))
+                {
+                    foreach(string clsidPath in ClsidPaths)
+                    {
+                        foreach(string value in new[] { "LocalizedString", "InfoTip", "" })
+                        {
+                            itemText = Registry.GetValue($@"{clsidPath}\{guid:B}", value, null)?.ToString();
+                            if(!string.IsNullOrEmpty(itemText)) break;
+                        }
+                    }
+                }
+                itemText = ResourceString.GetDirectString(itemText);
+                if(string.IsNullOrWhiteSpace(itemText)) itemText = null;
+                ItemTextDic.Add(guid, itemText);
+            }
+            return itemText;
+        }
+
+        public static Image GetImage(Guid guid)
+        {
+            if(ItemImageDic.TryGetValue(guid, out Image image)) return image;
+            IconLocation location = GetIconLocation(guid);
+            string iconPath = location.IconPath;
+            int iconIndex = location.IconIndex;
+            if(iconPath == null && iconIndex == 0) image = AppImage.DllDefaultIcon;
+            else if(Path.GetFileName(iconPath).ToLower() == "shell32.dll" && iconIndex == 0) image = AppImage.DllDefaultIcon;
+            else image = ResourceIcon.GetIcon(iconPath, iconIndex)?.ToBitmap() ?? AppImage.DllDefaultIcon;
+            ItemImageDic.Add(guid, image);
+            return image;
+        }
+
+        public static IconLocation GetIconLocation(Guid guid)
+        {
+            IconLocation location = new IconLocation();
+            if(guid.Equals(Guid.Empty)) return location;
+            if(IconLocationDic.ContainsKey(guid)) location = IconLocationDic[guid];
+            else
+            {
+                if(TryGetValue(guid.ToString(), "Icon", out string value))
+                {
+                    value = GetAbsStr(guid, value, false);
+                    int index = value.LastIndexOf(',');
+                    if(int.TryParse(value.Substring(index + 1), out int iconIndex))
+                    {
+                        location.IconPath = value.Substring(0, index);
+                        location.IconIndex = iconIndex;
+                    }
+                    else location.IconPath = value;
+                }
+                else location.IconPath = GetFilePath(guid);
+                IconLocationDic.Add(guid, location);
+            }
+            return location;
+        }
+
+        private static string GetAbsStr(Guid guid, string relStr, bool isName)
+        {
+            string absStr = relStr;
+            if(isName)
+            {
+                if(!relStr.StartsWith("@")) return absStr;
+                else absStr = relStr.Substring(1);
+            }
+            string filePath = GetFilePath(guid);
+            if(filePath == null) return absStr;
+            string dirPath = Path.GetDirectoryName(filePath);
+            if(absStr.StartsWith("*"))
+            {
+                absStr = filePath + absStr.Substring(1);
+            }
+            else if(absStr.StartsWith(".\\"))
+            {
+                absStr = dirPath + absStr.Substring(1);
+            }
+            else if(absStr.StartsWith("..\\"))
+            {
+                do
+                {
+                    dirPath = Path.GetDirectoryName(dirPath);
+                    absStr = absStr.Substring(3);
+                } while(absStr.StartsWith("..\\"));
+                absStr = dirPath + "\\" + absStr;
+            }
+            if(isName) absStr = "@" + absStr;
+            return absStr;
+        }
+    }
+}

+ 287 - 0
ContextMenuManager/MainForm.cs

@@ -0,0 +1,287 @@
+using BulePointLilac.Controls;
+using ContextMenuManager.Controls;
+
+namespace ContextMenuManager
+{
+    sealed class MainForm : MyMainForm
+    {
+        public MainForm()
+        {
+            this.Text = AppString.General_AppName;
+            this.Controls.Add(new ExplorerRestarter());
+            shellList.Owner = shellNewList.Owner = sendToList.Owner = openWithList.Owner
+                = winXList.Owner = guidBlockedList.Owner = thirdRuleList.Owner = MainBody;
+            donateBox.Parent = aboutMeBox.Parent = dictionariesBox.Parent = languagesBox.Parent = MainBody;
+            SideBar.SelectIndexChanged += (sender, e) => SwitchItem();
+            SideBar.HoverIndexChanged += (sender, e) => ShowItemInfo();
+            ToolBar.SelectedButtonChanged += (sender, e) => SwitchTab();
+            ToolBar.AddButtons(ToolBarButtons);
+            ToolBar.SelectedIndex = 0;
+        }
+
+        readonly MyToolBarButton[] ToolBarButtons = new MyToolBarButton[] {
+            new MyToolBarButton(AppImage.Home, AppString.ToolBar_Home),//主页
+            new MyToolBarButton(AppImage.Type, AppString.ToolBar_Type),//文件类型
+            new MyToolBarButton(AppImage.Star, AppString.ToolBar_Rule),//其他规则
+            new MyToolBarButton(AppImage.About, AppString.ToolBar_About)//关于
+        };
+        readonly ShellList shellList = new ShellList();
+        readonly ShellNewList shellNewList = new ShellNewList();
+        readonly SendToList sendToList = new SendToList();
+        readonly OpenWithList openWithList = new OpenWithList();
+        readonly WinXList winXList = new WinXList();
+        readonly GuidBlockedList guidBlockedList = new GuidBlockedList();
+        readonly ThirdRulesList thirdRuleList = new ThirdRulesList();
+        readonly AboutAppBox aboutMeBox = new AboutAppBox
+        {
+            Text = AppString.Text_AboutApp
+        };
+        readonly DonateBox donateBox = new DonateBox();
+        readonly LanguagesBox languagesBox = new LanguagesBox();
+        readonly DictionariesBox dictionariesBox = new DictionariesBox();
+
+        static readonly string[] GeneralItems = {
+            AppString.SideBar_File,
+            AppString.SideBar_Folder,
+            AppString.SideBar_Directory,
+            AppString.SideBar_Background,
+            AppString.SideBar_Desktop,
+            AppString.SideBar_Drive,
+            AppString.SideBar_AllObjects,
+            AppString.SideBar_Computer,
+            AppString.SideBar_RecycleBin,
+            AppString.SideBar_Library,
+            null,
+            AppString.SideBar_New,
+            AppString.SideBar_SendTo,
+            AppString.SideBar_OpenWith,
+            null,
+            AppString.SideBar_WinX
+        };
+        static readonly string[] GeneralItemInfos = {
+            AppString.StatusBar_File,
+            AppString.StatusBar_Folder,
+            AppString.StatusBar_Directory,
+            AppString.StatusBar_Background,
+            AppString.StatusBar_Desktop,
+            AppString.StatusBar_Drive,
+            AppString.StatusBar_AllObjects,
+            AppString.StatusBar_Computer,
+            AppString.StatusBar_RecycleBin,
+            AppString.StatusBar_Library,
+            null,
+            AppString.StatusBar_New,
+            AppString.StatusBar_SendTo,
+            AppString.StatusBar_OpenWith,
+            null,
+            AppString.StatusBar_WinX
+        };
+
+        static readonly string[] TypeItems = {
+            AppString.SideBar_LnkFile,
+            AppString.SideBar_ExeFile,
+            null,
+            AppString.SideBar_TextFile,
+            AppString.SideBar_ImageFile,
+            AppString.SideBar_VideoFile,
+            AppString.SideBar_AudioFile,
+            null,
+            AppString.SideBar_ImageDirectory,
+            AppString.SideBar_VideoDirectory,
+            AppString.SideBar_AudioDirectory,
+            null,
+            AppString.SideBar_UnknownType,
+            null,
+            AppString.SideBar_CustomType
+        };
+        static readonly string[] TypeItemInfos = {
+            AppString.StatusBar_LnkFile,
+            AppString.StatusBar_ExeFile,
+            null,
+            AppString.StatusBar_TextFile,
+            AppString.StatusBar_ImageFile,
+            AppString.StatusBar_VideoFile,
+            AppString.StatusBar_AudioFile,
+            null,
+            AppString.StatusBar_ImageDirectory,
+            AppString.StatusBar_VideoDirectory,
+            AppString.StatusBar_AudioDirectory,
+            null,
+            AppString.StatusBar_UnknownType,
+            null,
+            AppString.StatusBar_CustomType
+        };
+
+        static readonly string[] OtherRuleItems = {
+            AppString.SideBar_GuidBlocked,
+            AppString.SideBar_ThirdRules
+        };
+        static readonly string[] OtherRuleItemInfos = {
+            AppString.StatusBar_GuidBlocked,
+            AppString.StatusBar_ThirdRules
+        };
+
+        static readonly string[] AboutItems = {
+            AppString.SideBar_AboutApp,
+            AppString.SideBar_Dictionaries,
+            AppString.SideBar_AppLanguage,
+            AppString.SideBar_Donate
+        };
+
+        private void HideAllParts()
+        {
+            shellList.Visible = shellNewList.Visible = sendToList.Visible = openWithList.Visible
+                = winXList.Visible = guidBlockedList.Visible = thirdRuleList.Visible
+                = donateBox.Visible = aboutMeBox.Visible = dictionariesBox.Visible = languagesBox.Visible = false;
+        }
+
+        private void SwitchTab()
+        {
+            switch(ToolBar.SelectedIndex)
+            {
+                case 0:
+                    SideBar.ItemNames = GeneralItems; break;
+                case 1:
+                    SideBar.ItemNames = TypeItems; break;
+                case 2:
+                    SideBar.ItemNames = OtherRuleItems; break;
+                case 3:
+                    SideBar.ItemNames = AboutItems; break;
+            }
+            SideBar.SelectIndex = 0;
+        }
+
+        private void SwitchItem()
+        {
+            HideAllParts();
+            if(SideBar.SelectIndex == -1) return;
+            switch(ToolBar.SelectedIndex)
+            {
+                case 0:
+                    SwitchGeneralItem(); return;
+                case 1:
+                    SwitchTypeItem(); return;
+                case 2:
+                    SwitchOtherRuleItem(); return;
+                case 3:
+                    SwitchAboutItem(); return;
+            }
+        }
+
+        private void ShowItemInfo()
+        {
+            if(SideBar.HoverIndex >= 0)
+            {
+                int i = SideBar.HoverIndex;
+                switch(ToolBar.SelectedIndex)
+                {
+                    case 0:
+                        StatusBar.Text = GeneralItemInfos[i]; return;
+                    case 1:
+                        StatusBar.Text = TypeItemInfos[i]; return;
+                    case 2:
+                        StatusBar.Text = OtherRuleItemInfos[i]; return;
+                }
+            }
+            StatusBar.Text = MyStatusBar.DefaultText;
+        }
+
+        private void SwitchGeneralItem()
+        {
+            switch(SideBar.SelectIndex)
+            {
+                case 0:
+                    shellList.Scene = ShellList.Scenes.File; break;
+                case 1:
+                    shellList.Scene = ShellList.Scenes.Folder; break;
+                case 2:
+                    shellList.Scene = ShellList.Scenes.Directory; break;
+                case 3:
+                    shellList.Scene = ShellList.Scenes.Background; break;
+                case 4:
+                    shellList.Scene = ShellList.Scenes.Desktop; break;
+                case 5:
+                    shellList.Scene = ShellList.Scenes.Drive; break;
+                case 6:
+                    shellList.Scene = ShellList.Scenes.AllObjects; break;
+                case 7:
+                    shellList.Scene = ShellList.Scenes.Computer; break;
+                case 8:
+                    shellList.Scene = ShellList.Scenes.RecycleBin; break;
+                case 9:
+                    shellList.Scene = ShellList.Scenes.Library; break;
+                case 11:
+                    shellNewList.LoadItems(); shellNewList.Visible = true; break;
+                case 12:
+                    sendToList.LoadItems(); sendToList.Visible = true; break;
+                case 13:
+                    openWithList.LoadItems(); openWithList.Visible = true; break;
+                case 15:
+                    winXList.LoadItems(); winXList.Visible = true; break;
+            }
+            shellList.Visible = SideBar.SelectIndex <= 9;
+        }
+
+        private void SwitchTypeItem()
+        {
+            switch(SideBar.SelectIndex)
+            {
+                case 0:
+                    shellList.Scene = ShellList.Scenes.LnkFile; break;
+                case 1:
+                    shellList.Scene = ShellList.Scenes.ExeFile; break;
+                case 3:
+                    shellList.Scene = ShellList.Scenes.Text; break;
+                case 4:
+                    shellList.Scene = ShellList.Scenes.Image; break;
+                case 5:
+                    shellList.Scene = ShellList.Scenes.Video; break;
+                case 6:
+                    shellList.Scene = ShellList.Scenes.Audio; break;
+                case 8:
+                    shellList.Scene = ShellList.Scenes.ImageDirectory; break;
+                case 9:
+                    shellList.Scene = ShellList.Scenes.VideoDirectory; break;
+                case 10:
+                    shellList.Scene = ShellList.Scenes.AudioDirectory; break;
+                case 12:
+                    shellList.Scene = ShellList.Scenes.Unknown; break;
+                case 14:
+                    shellList.Scene = ShellList.Scenes.CustomExtension; break;
+            }
+            shellList.Visible = true;
+        }
+
+        private void SwitchOtherRuleItem()
+        {
+            switch(SideBar.SelectIndex)
+            {
+                case 0:
+                    guidBlockedList.LoadItems(); guidBlockedList.Visible = true; return;
+                case 1:
+                    thirdRuleList.LoadItems(); thirdRuleList.Visible = true; return;
+            }
+        }
+
+        private void SwitchAboutItem()
+        {
+            switch(SideBar.SelectIndex)
+            {
+                case 0:
+                    aboutMeBox.Visible = true;
+                    return;
+                case 1:
+                    dictionariesBox.Visible = true;
+                    dictionariesBox.LoadTexts();
+                    return;
+                case 2:
+                    languagesBox.LoadLanguages();
+                    languagesBox.Visible = true;
+                    break;
+                case 3:
+                    donateBox.Visible = true;
+                    return;
+            }
+        }
+    }
+}

+ 72 - 0
ContextMenuManager/Program.cs

@@ -0,0 +1,72 @@
+using BulePointLilac.Methods;
+using System;
+using System.Globalization;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace ContextMenuManager
+{
+    static class Program
+    {
+        public const string ZH_CNINI = "zh-cn.ini";
+        public const string CONFIGINI = "config.ini";
+        public const string GUIDINFOSINI = "GuidInfos.ini";
+        public const string ThIRDRULESDICXML = "ThirdRulesDic.xml";
+        public const string SHELLCOMMONDICXML = "ShellCommonDic.xml";
+        public static readonly string ConfigDir = $@"{Application.StartupPath}\config";
+        public static readonly string LanguagesDir = $@"{ConfigDir}\languages";
+        public static readonly string AppDataConfigDir = $@"{Environment.ExpandEnvironmentVariables("%AppData%")}\ContextMenuManager\config";
+        public static readonly string ConfigIniPath = $@"{AppDataConfigDir}\{CONFIGINI}";
+        public static readonly string GuidInfosDicPath = $@"{ConfigDir}\{GUIDINFOSINI}";
+        public static readonly string AppDataGuidInfosDicPath = $@"{AppDataConfigDir}\{GUIDINFOSINI}";
+        public static readonly string ThirdRulesDicPath = $@"{ConfigDir}\{ThIRDRULESDICXML}";
+        public static readonly string AppDataThirdRulesDicPath = $@"{AppDataConfigDir}\{ThIRDRULESDICXML}";
+        public static readonly string ShellCommonDicPath = $@"{ConfigDir}\{SHELLCOMMONDICXML}";
+        public static readonly string AppDataShellCommonDicPath = $@"{AppDataConfigDir}\{SHELLCOMMONDICXML}";
+        private static readonly IniReader ConfigReader = new IniReader(ConfigIniPath);
+        public static string LanguageFilePath
+        {
+            get
+            {
+                string value = ConfigReader.GetValue("General", "Language");
+                DirectoryInfo di = new DirectoryInfo(LanguagesDir);
+                if(value.Equals(string.Empty) && di.Exists)
+                {
+                    foreach(FileInfo fi in di.GetFiles())
+                    {
+                        if(Path.GetFileNameWithoutExtension(fi.Name).Equals(new CultureInfo(GetUserDefaultUILanguage()).Name, StringComparison.OrdinalIgnoreCase))
+                        {
+                            value = fi.FullName; break;
+                        }
+                    }
+                }
+                return value;
+            }
+        }
+        public static readonly DateTime LastCheckUpdateTime;
+
+        [STAThread]
+        static void Main()
+        {
+            Application.EnableVisualStyles();
+            Application.SetCompatibleTextRenderingDefault(false);
+            Application.Run(new MainForm());
+        }
+
+        [DllImport("kernel32.dll")]
+        private static extern ushort GetUserDefaultUILanguage();
+
+        static Program()
+        {
+            Directory.CreateDirectory(AppDataConfigDir);
+            if(!DateTime.TryParse(ConfigReader.GetValue("General", "LastCheckUpdateTime"), out LastCheckUpdateTime))
+                LastCheckUpdateTime = DateTime.Today.AddMonths(-2);
+            if(LastCheckUpdateTime.AddMonths(1).CompareTo(DateTime.Today) < 0)
+            {
+                Updater.CheckUpdate();
+                new IniFileHelper(ConfigIniPath).SetValue("General", "LastCheckUpdateTime", DateTime.Today.ToShortDateString());
+            }
+        }
+    }
+}

+ 25 - 0
ContextMenuManager/Properties/App.manifest

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
+  <assemblyIdentity version="1.0.0.0" name="MyApplication.app" />
+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+    <security>
+      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
+        <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
+      </requestedPrivileges>
+      <applicationRequestMinimum>
+        <defaultAssemblyRequest permissionSetReference="Custom" />
+        <PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
+      </applicationRequestMinimum>
+    </security>
+  </trustInfo>
+  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+    <application>
+      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
+    </application>
+  </compatibility>
+  <application xmlns="urn:schemas-microsoft-com:asm.v3">
+    <windowsSettings>
+      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
+    </windowsSettings>
+  </application>
+</assembly>

BIN
ContextMenuManager/Properties/AppIcon.ico


+ 15 - 0
ContextMenuManager/Properties/AssemblyInfo.cs

@@ -0,0 +1,15 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+[assembly: AssemblyTitle("Windows右键管理")]
+[assembly: AssemblyDescription("Windows右键管理")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("蓝点llilac")]
+[assembly: AssemblyProduct("Windows右键管理")]
+[assembly: AssemblyCopyright("Copyright @ 2020 蓝点lilac")]
+[assembly: AssemblyTrademark("蓝点llilac")]
+[assembly: AssemblyCulture("")]
+[assembly: ComVisible(false)]
+[assembly: Guid("35190ec1-2515-488d-a2e9-825d6ff67aa2")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 405 - 0
ContextMenuManager/Properties/Resources.Designer.cs

@@ -0,0 +1,405 @@
+namespace ContextMenuManager.Properties
+{
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources
+    {
+        private static global::System.Resources.ResourceManager resourceMan;
+        private static global::System.Globalization.CultureInfo resourceCulture;
+
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() { }
+
+        /// <summary>
+        ///   返回此类使用的缓存的 ResourceManager 实例。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager
+        {
+            get
+            {
+                if(object.ReferenceEquals(resourceMan, null))
+                {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ContextMenuManager.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+
+        /// <summary>
+        ///   重写当前线程的 CurrentUICulture 属性
+        ///   重写当前线程的 CurrentUICulture 属性。
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture
+        {
+            get
+            {
+                return resourceCulture;
+            }
+            set
+            {
+                resourceCulture = value;
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap About
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("About", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Add
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("Add", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap AddCommon
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("AddCommon", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap AddExisting
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("AddExisting", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap AddSeparator
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("AddSeparator", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找类似 ;此文件为ContextMenuManager程序的显示文本字典, 字典内换行符使用\n转义
+        ///;如果你想要帮助作者为此程序添加其他语言字典, 可在.\config\languages文件夹内制作字典文件, 
+        ///;比如英语字典保存为en-us.ini, 并给[General]\General_Language赋值 en-us 英语
+        ///
+        ///[General]
+        ///General_Language = zh-cn 简体中文
+        ///General_AppName = Windows右键管理
+        ///
+        ///[ToolBar]
+        ///ToolBar_Home = 主页
+        ///ToolBar_Type = 文件类型
+        ///ToolBar_Rule = 其他规则
+        ///ToolBar_About = 关于
+        ///
+        ///[SideBar]
+        ///SideBar_File = 文件
+        ///SideBar_Folder = 文件夹
+        ///SideBar_Directory = 目录
+        ///SideBar_Background = 目录背景
+        ///SideBar_Desktop = 桌面背景
+        ///SideBar_Drive = 磁盘分区
+        ///SideBar_AllObjects = 所有对 [字符串的其余部分被截断]&quot;; 的本地化字符串。
+        /// </summary>
+        internal static string AppLanguageDic
+        {
+            get
+            {
+                return ResourceManager.GetString("AppLanguageDic", resourceCulture);
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap CustomType
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("CustomType", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Delete
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("Delete", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Donate
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("Donate", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找类似 ;&lt;说明&gt;:
+        ///;由于ShellEx类型右键菜单的菜单名称和图标无法直接获取,只能通过制作字典给用户更直观的体验
+        ///;此文件为依赖&lt;GUID&gt;的ShellEx类型右键菜单项目的名称和图标字典,用户可自行添加字典
+        ///;&lt;GUID&gt;可通过右键程序内ShellEx项目&quot;复制guid&quot;获取
+        ///;不带括号的&lt;GUID&gt;为字典索引
+        ///
+        ///;&lt;Text&gt;:
+        ///;Text为菜单项目名称
+        ///;可以赋值为引用资源文件字符串资源的本地化字符串,
+        ///;格式为&quot;@&lt;资源文件路径&gt;,-&lt;字符串资源索引&gt;&quot;,如赋值为&quot;@shell32.dll,-3576&quot;
+        ///;也可以赋值为直接显示名称,如赋值为&quot;使用XXX打开&quot;
+        ///
+        ///;&lt;Icon&gt;:
+        ///;Icon为菜单项目图标资源位置
+        ///;格式为&quot;&lt;资源文件路径&gt;,&lt;图标序号&gt;&quot;,如赋值为&quot;C:Windows\System32\imageres.dll,203&quot;
+        ///;&lt;图标序号&gt;为负数则为图标资源索引,为非负数则为图标资源顺序序号
+        ///;Icon为空时默认提取文件第一个图标,没有图标则使用dll文件默认图标
+        ///
+        ///;&lt;资源文件路径&gt;:
+        ///;&lt;Text&gt;和&lt;Icon&gt;中的&lt;资源文件路径&gt;一般使用相对路径
+        /// [字符串的其余部分被截断]&quot;; 的本地化字符串。
+        /// </summary>
+        internal static string GuidInfosDic
+        {
+            get
+            {
+                return ResourceManager.GetString("GuidInfosDic", resourceCulture);
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Home
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("Home", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap MicrosoftStore
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("MicrosoftStore", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap NewItem
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("NewItem", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Open
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("Open", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap SeparatorItem
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("SeparatorItem", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Setting
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("Setting", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找类似 &lt;?xml version=&apos;1.0&apos; encoding=&apos;utf-8&apos; ?&gt;
+        ///&lt;!--此文件为常用右键菜单字典,如果想要添加字典,请保存为.\config\CommonItemsDic.xml后再进行添加--&gt;
+        ///
+        ///&lt;Data&gt;
+        ///  &lt;Group RegPath=&apos;HKEY_CLASSES_ROOT\*&apos;&gt;
+        ///    &lt;Shell&gt;
+        ///      &lt;Item KeyName=&apos;CopyContent&apos; Tip=&apos;不需打开文件直接复制文件文本内容&amp;#x000A;非UTF-16 LE(或带BOM)编码会乱码&apos;&gt;
+        ///        &lt;Value&gt;
+        ///          &lt;REG_SZ MUIVerb=&apos;复制内容到剪切板&apos; Icon=&apos;DxpTaskSync.dll,-52&apos;/&gt;
+        ///        &lt;/Value&gt;
+        ///        &lt;SubKey&gt;
+        ///          &lt;Command Default=&apos;cmd /c clip &amp;lt; &quot;%1&quot;&apos;/&gt;
+        ///        &lt;/SubKey&gt;
+        ///      &lt;/Item&gt;
+        ///      &lt;Item KeyName=&apos;TakeOwnerShip&apos;&gt;
+        ///     [字符串的其余部分被截断]&quot;; 的本地化字符串。
+        /// </summary>
+        internal static string ShellCommonDic
+        {
+            get
+            {
+                return ResourceManager.GetString("ShellCommonDic", resourceCulture);
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Star
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("Star", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap SubItems
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("SubItems", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找类似 &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
+        ///&lt;!--每个程序为一个Group,Text为Group项显示文本,Guid用于判断用户是否安装此程序并决定是否显示该Group,当菜单项为常驻菜单时添加属性Common,RegPath为程序相关注册表主路径;
+        ///其相关菜单项目设置作为一个Item子元素,Item的Text为该Item项显示文本,Tag属性为鼠标悬浮在开关上时状态栏显示的相关提示信息,需要重启资源管理器生效则添加属性RestartExplorer;
+        ///Item的子元素Rule为相关注册表内容,RegPath省略则默认为Group主路径,以\开头则为Group主路径的子项路径;ValueName为相关键名,On为启用键值,Off为禁用键值;
+        ///每个Item可能受多个注册表Rule影响,按照顺序进行键值判定;程序优先判定为On,即只要所有Rule不匹配Off键值就判定为On,键值类型不符时也判定为On;
+        ///ValueKind为键值类型,默认键值类型ValueKind为REG_DWORD,为默认值时可省略,键值类型有:REG_SZ、REG_BINARY、REG_DWOR [字符串的其余部分被截断]&quot;; 的本地化字符串。
+        /// </summary>
+        internal static string ThirdRulesDic
+        {
+            get
+            {
+                return ResourceManager.GetString("ThirdRulesDic", resourceCulture);
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap TurnOff
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("TurnOff", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap TurnOn
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("TurnOn", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Type
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("Type", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Types
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("Types", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap Up
+        {
+            get
+            {
+                object obj = ResourceManager.GetObject("Up", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+    }
+}

+ 196 - 0
ContextMenuManager/Properties/Resources.resx

@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="About" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\about.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Add" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\add.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="AddCommon" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\addcommon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="AddExisting" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\addexisting.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="AddSeparator" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\addseparator.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="AppLanguageDic" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\texts\applanguagedic.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
+  </data>
+  <data name="CustomType" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\customtype.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\delete.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Donate" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\donate.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="GuidInfosDic" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\texts\guidinfosdic.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
+  </data>
+  <data name="Home" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\home.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="MicrosoftStore" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\microsoftstore.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="NewItem" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\newitem.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Open" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\open.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="SeparatorItem" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\separatoritem.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Setting" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\setting.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="ShellCommonDic" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\texts\shellcommondic.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
+  </data>
+  <data name="Star" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\star.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="SubItems" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\subitems.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="ThirdRulesDic" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\texts\thirdrulesdic.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
+  </data>
+  <data name="TurnOff" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\turnoff.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="TurnOn" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\turnon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Type" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\type.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Types" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\types.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+  <data name="Up" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\up.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
+  </data>
+</root>

BIN
ContextMenuManager/Properties/Resources/Images/About.png


BIN
ContextMenuManager/Properties/Resources/Images/Add.png


BIN
ContextMenuManager/Properties/Resources/Images/AddCommon.png


BIN
ContextMenuManager/Properties/Resources/Images/AddExisting.png


BIN
ContextMenuManager/Properties/Resources/Images/AddSeparator.png


BIN
ContextMenuManager/Properties/Resources/Images/CustomType.png


BIN
ContextMenuManager/Properties/Resources/Images/Delete.png


BIN
ContextMenuManager/Properties/Resources/Images/Donate.png


BIN
ContextMenuManager/Properties/Resources/Images/Home.png


BIN
ContextMenuManager/Properties/Resources/Images/MicrosoftStore.png


BIN
ContextMenuManager/Properties/Resources/Images/NewItem.png


BIN
ContextMenuManager/Properties/Resources/Images/Open.png


BIN
ContextMenuManager/Properties/Resources/Images/SeparatorItem.png


BIN
ContextMenuManager/Properties/Resources/Images/Setting.png


BIN
ContextMenuManager/Properties/Resources/Images/Star.png


BIN
ContextMenuManager/Properties/Resources/Images/SubItems.png


BIN
ContextMenuManager/Properties/Resources/Images/TurnOff.png


BIN
ContextMenuManager/Properties/Resources/Images/TurnOn.png


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels