蓝点lilac 4 жил өмнө
parent
commit
e4b6e851c0
100 өөрчлөгдсөн 3965 нэмэгдсэн , 1761 устгасан
  1. 19 37
      ContextMenuManager/AppConfig.cs
  2. 7 7
      ContextMenuManager/AppImage.cs
  3. 48 30
      ContextMenuManager/AppString.cs
  4. 1 1
      ContextMenuManager/BluePointLilac.Controls/IconDialog.cs
  5. 2 2
      ContextMenuManager/BluePointLilac.Controls/InputDialog.cs
  6. 2 1
      ContextMenuManager/BluePointLilac.Controls/MyCheckBox.cs
  7. 6 5
      ContextMenuManager/BluePointLilac.Controls/MyListBox.cs
  8. 2 2
      ContextMenuManager/BluePointLilac.Controls/MyMainForm.cs
  9. 6 6
      ContextMenuManager/BluePointLilac.Controls/MySideBar.cs
  10. 18 6
      ContextMenuManager/BluePointLilac.Controls/MyStatusBar.cs
  11. 12 5
      ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs
  12. 3 3
      ContextMenuManager/BluePointLilac.Controls/MyToolTip.cs
  13. 1 1
      ContextMenuManager/BluePointLilac.Controls/PictureButton.cs
  14. 5 5
      ContextMenuManager/BluePointLilac.Controls/ReadOnlyRichTextBox.cs
  15. 2 2
      ContextMenuManager/BluePointLilac.Controls/ResizbleForm.cs
  16. 1 1
      ContextMenuManager/BluePointLilac.Methods/ControlExtension.cs
  17. 34 0
      ContextMenuManager/BluePointLilac.Methods/DesktopIni.cs
  18. 1 1
      ContextMenuManager/BluePointLilac.Methods/DirectoryEx.cs
  19. 6 11
      ContextMenuManager/BluePointLilac.Methods/EncodingType.cs
  20. 177 0
      ContextMenuManager/BluePointLilac.Methods/ExternalProgram.cs
  21. 1 1
      ContextMenuManager/BluePointLilac.Methods/FileExtension.cs
  22. 11 3
      ContextMenuManager/BluePointLilac.Methods/GuidEx.cs
  23. 1 1
      ContextMenuManager/BluePointLilac.Methods/HighDpi.cs
  24. 1 1
      ContextMenuManager/BluePointLilac.Methods/ImageExtension.cs
  25. 2 1
      ContextMenuManager/BluePointLilac.Methods/IniReader.cs
  26. 3 3
      ContextMenuManager/BluePointLilac.Methods/IniWriter.cs
  27. 1 1
      ContextMenuManager/BluePointLilac.Methods/MessageBoxEx.cs
  28. 53 45
      ContextMenuManager/BluePointLilac.Methods/ObjectPath.cs
  29. 14 5
      ContextMenuManager/BluePointLilac.Methods/RegTrustedInstaller.cs
  30. 3 4
      ContextMenuManager/BluePointLilac.Methods/RegistryEx.cs
  31. 1 1
      ContextMenuManager/BluePointLilac.Methods/ResourceIcon.cs
  32. 1 1
      ContextMenuManager/BluePointLilac.Methods/ResourceString.cs
  33. 1 1
      ContextMenuManager/BluePointLilac.Methods/RichTextBoxExtension.cs
  34. 51 0
      ContextMenuManager/BluePointLilac.Methods/SingleInstance.cs
  35. 1 1
      ContextMenuManager/BluePointLilac.Methods/StringExtension.cs
  36. 1 1
      ContextMenuManager/BluePointLilac.Methods/TextBoxExtension.cs
  37. 1 1
      ContextMenuManager/BluePointLilac.Methods/WindowsOsVersion.cs
  38. 202 0
      ContextMenuManager/BluePointLilac.Methods/WshShortcut.cs
  39. 0 56
      ContextMenuManager/BulePointLilac.Methods/PropertiesDialog.cs
  40. 0 91
      ContextMenuManager/BulePointLilac.Methods/WshShortcut.cs
  41. 71 53
      ContextMenuManager/ContextMenuManager.csproj
  42. 68 41
      ContextMenuManager/Controls/AboutApp.cs
  43. 2 2
      ContextMenuManager/Controls/AddGuidDicDialog.cs
  44. 0 107
      ContextMenuManager/Controls/CommandDialog.cs
  45. 58 4
      ContextMenuManager/Controls/EnhanceMenusItem.cs
  46. 117 78
      ContextMenuManager/Controls/EnhanceMenusList.cs
  47. 6 37
      ContextMenuManager/Controls/ExplorerRestarter.cs
  48. 0 89
      ContextMenuManager/Controls/FileExtensionDialog.cs
  49. 3 3
      ContextMenuManager/Controls/GuidBlockedItem.cs
  50. 5 13
      ContextMenuManager/Controls/GuidBlockedList.cs
  51. 122 0
      ContextMenuManager/Controls/IEItem.cs
  52. 63 0
      ContextMenuManager/Controls/IEList.cs
  53. 2 2
      ContextMenuManager/Controls/Interfaces/IBtnDeleteItem.cs
  54. 1 1
      ContextMenuManager/Controls/Interfaces/IBtnMoveUpDownItem.cs
  55. 16 3
      ContextMenuManager/Controls/Interfaces/IBtnOpenPathItem.cs
  56. 1 1
      ContextMenuManager/Controls/Interfaces/IBtnShowMenuItem.cs
  57. 1 1
      ContextMenuManager/Controls/Interfaces/IChkVisibleItem.cs
  58. 18 5
      ContextMenuManager/Controls/Interfaces/IFoldGroupItem.cs
  59. 46 0
      ContextMenuManager/Controls/Interfaces/ITsiAdministratorItem.cs
  60. 2 2
      ContextMenuManager/Controls/Interfaces/ITsiCommandItem.cs
  61. 13 14
      ContextMenuManager/Controls/Interfaces/ITsiDeleteItem.cs
  62. 5 12
      ContextMenuManager/Controls/Interfaces/ITsiFilePathItem.cs
  63. 2 2
      ContextMenuManager/Controls/Interfaces/ITsiIconItem.cs
  64. 1 1
      ContextMenuManager/Controls/Interfaces/ITsiRegExportItem.cs
  65. 4 5
      ContextMenuManager/Controls/Interfaces/ITsiRegPathItem.cs
  66. 138 0
      ContextMenuManager/Controls/Interfaces/ITsiShortcutCommandItem.cs
  67. 2 2
      ContextMenuManager/Controls/Interfaces/ITsiTextItem.cs
  68. 6 9
      ContextMenuManager/Controls/Interfaces/ITsiWebSearchItem.cs
  69. 0 103
      ContextMenuManager/Controls/LockNewItem.cs
  70. 64 0
      ContextMenuManager/Controls/NewIEDialog.cs
  71. 8 5
      ContextMenuManager/Controls/NewItem.cs
  72. 15 13
      ContextMenuManager/Controls/NewItemForm.cs
  73. 52 37
      ContextMenuManager/Controls/NewLnkFileDialog.cs
  74. 26 16
      ContextMenuManager/Controls/NewOpenWithDialog.cs
  75. 39 18
      ContextMenuManager/Controls/NewShellDialog.cs
  76. 9 4
      ContextMenuManager/Controls/OpenWithItem.cs
  77. 5 5
      ContextMenuManager/Controls/OpenWithList.cs
  78. 0 230
      ContextMenuManager/Controls/RegRuleItem.cs
  79. 498 0
      ContextMenuManager/Controls/RuleItem.cs
  80. 147 0
      ContextMenuManager/Controls/SelectDialog.cs
  81. 49 39
      ContextMenuManager/Controls/SendToItem.cs
  82. 22 16
      ContextMenuManager/Controls/SendToList.cs
  83. 37 14
      ContextMenuManager/Controls/ShellExItem.cs
  84. 162 0
      ContextMenuManager/Controls/ShellExecuteDialog.cs
  85. 87 22
      ContextMenuManager/Controls/ShellItem.cs
  86. 325 144
      ContextMenuManager/Controls/ShellList.cs
  87. 66 11
      ContextMenuManager/Controls/ShellNewItem.cs
  88. 143 43
      ContextMenuManager/Controls/ShellNewList.cs
  89. 23 20
      ContextMenuManager/Controls/ShellStoreDialog.cs
  90. 47 18
      ContextMenuManager/Controls/ShellSubMenuDialog.cs
  91. 120 24
      ContextMenuManager/Controls/ThirdRulesList.cs
  92. 111 0
      ContextMenuManager/Controls/WinXGroupItem.cs
  93. 129 23
      ContextMenuManager/Controls/WinXItem.cs
  94. 175 26
      ContextMenuManager/Controls/WinXList.cs
  95. 2 2
      ContextMenuManager/GuidInfo.cs
  96. 56 52
      ContextMenuManager/MainForm.cs
  97. 5 2
      ContextMenuManager/Program.cs
  98. 3 3
      ContextMenuManager/Properties/AssemblyInfo.cs
  99. 52 33
      ContextMenuManager/Properties/Resources.Designer.cs
  100. 13 7
      ContextMenuManager/Properties/Resources.resx

+ 19 - 37
ContextMenuManager/AppConfig.cs

@@ -3,7 +3,7 @@ using System.Globalization;
 using System.IO;
 using System.Runtime.InteropServices;
 using System.Windows.Forms;
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 
 namespace ContextMenuManager
 {
@@ -11,8 +11,10 @@ namespace ContextMenuManager
     {
         static AppConfig()
         {
-            CreateDirs();
-            DeleteOldFiles();
+            foreach(string dirPath in new[] { ConfigDir, ProgramsDir, BackupDir, LangsDir, DicsDir, WebDicsDir, UserDicsDir })
+            {
+                Directory.CreateDirectory(dirPath);
+            }
         }
 
         [DllImport("kernel32.dll")]
@@ -25,6 +27,7 @@ namespace ContextMenuManager
         public static string ConfigIni = $@"{ConfigDir}\Config.ini";
         public static string BackupDir = $@"{ConfigDir}\Backup";
         public static string LangsDir = $@"{ConfigDir}\Languages";
+        public static string ProgramsDir = $@"{ConfigDir}\Programs";
         public static string DicsDir = $@"{ConfigDir}\Dictionaries";
         public static string WebDicsDir = $@"{DicsDir}\Web";
         public static string UserDicsDir = $@"{DicsDir}\User";
@@ -34,7 +37,7 @@ namespace ContextMenuManager
         public static string UserThirdRulesDic = $@"{UserDicsDir}\{ThIRDRULESDICXML}";
         public static string WebEnhanceMenusDic = $@"{WebDicsDir}\{ENHANCEMENUSICXML}";
         public static string UserEnhanceMenusDic = $@"{UserDicsDir}\{ENHANCEMENUSICXML}";
-
+        public static string HashLnkExePath = $@"{ProgramsDir}\HashLnk.exe";
         public const string ZH_CNINI = "zh-CN.ini";
         public const string GUIDINFOSDICINI = "GuidInfosDic.ini";
         public const string ThIRDRULESDICXML = "ThirdRulesDic.xml";
@@ -120,43 +123,22 @@ namespace ContextMenuManager
             }
         }
 
-        private static void CreateDirs()
+        public static bool ShowFilePath
         {
-            foreach(string dirPath in new[] { ConfigDir, BackupDir, LangsDir, DicsDir, WebDicsDir, UserDicsDir })
-            {
-                Directory.CreateDirectory(dirPath);
-            }
+            get => ConfigWriter.GetValue("General", "ShowFilePath") == "1";
+            set => ConfigWriter.SetValue("General", "ShowFilePath", (value ? 1 : 0).ToString());
         }
 
-        private static void DeleteOldFiles()
+        public static bool WinXSortable
         {
-            DirectoryInfo configDi = new DirectoryInfo(ConfigDir);
-            foreach(DirectoryInfo di in configDi.GetDirectories())
-            {
-                bool isOther = true;
-                foreach(string path in new[] { BackupDir, LangsDir, DicsDir })
-                {
-                    if(di.FullName.Equals(path, StringComparison.OrdinalIgnoreCase))
-                    {
-                        isOther = false;
-                        break;
-                    }
-                }
-                if(isOther) Directory.Delete(di.FullName);
-            }
-            foreach(FileInfo fi in configDi.GetFiles())
-            {
-                bool isOther = true;
-                foreach(string path in new[] { ConfigIni })
-                {
-                    if(fi.FullName.Equals(path, StringComparison.OrdinalIgnoreCase))
-                    {
-                        isOther = false;
-                        break;
-                    }
-                }
-                if(isOther) File.Delete(fi.FullName);
-            }
+            get => ConfigWriter.GetValue("General", "WinXSortable") == "1";
+            set => ConfigWriter.SetValue("General", "WinXSortable", (value ? 1 : 0).ToString());
+        }
+
+        public static bool OpenMoreRegedit
+        {
+            get => ConfigWriter.GetValue("General", "OpenMoreRegedit") == "1";
+            set => ConfigWriter.SetValue("General", "OpenMoreRegedit", (value ? 1 : 0).ToString());
         }
     }
 }

+ 7 - 7
ContextMenuManager/AppImage.cs

@@ -1,4 +1,4 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using ContextMenuManager.Properties;
 using System.Drawing;
 
@@ -41,12 +41,12 @@ namespace ContextMenuManager
         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>新建文件夹图标</summary>
+        public static readonly Image NewFolder = Resources.NewFolder.ResizeImage(Scale);
+        ///<summary>自定义图标</summary>
+        public static readonly Image Custom = Resources.Custom.ResizeImage(Scale);
+        ///<summary>选择图标</summary>
+        public static readonly Image Select = Resources.Select.ResizeImage(Scale);
         ///<summary>Microsoft Store图标</summary>
         public static readonly Image MicrosoftStore = Resources.MicrosoftStore.ResizeImage(Scale);
         ///<summary>Skype图标</summary>

+ 48 - 30
ContextMenuManager/AppString.cs

@@ -1,4 +1,4 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System.Text;
 
 namespace ContextMenuManager
@@ -55,20 +55,16 @@ namespace ContextMenuManager
             public static string LnkFile => GetValue("LnkFile");
             public static string UwpLnk => GetValue("UwpLnk");
             public static string ExeFile => GetValue("ExeFile");
-            public static string TextFile => GetValue("TextFile");
-            public static string DocumentFile => GetValue("DocumentFile");
-            public static string ImageFile => GetValue("ImageFile");
-            public static string VideoFile => GetValue("VideoFile");
-            public static string AudioFile => GetValue("AudioFile");
-            public static string ImageDirectory => GetValue("ImageDirectory");
-            public static string VideoDirectory => GetValue("VideoDirectory");
-            public static string AudioDirectory => GetValue("AudioDirectory");
             public static string UnknownType => GetValue("UnknownType");
-            public static string CustomType => GetValue("CustomType");
+            public static string CustomExtension => GetValue("CustomExtension");
+            public static string PerceivedType => GetValue("PerceivedType");
+            public static string DirectoryType => GetValue("DirectoryType");
             public static string EnhanceMenu => GetValue("EnhanceMenu");
             public static string ThirdRules => GetValue("ThirdRules");
             public static string GuidBlocked => GetValue("GuidBlocked");
+            public static string DragDrop => GetValue("DragDrop");
             public static string PublicReferences => GetValue("PublicReferences");
+            public static string IEMenu => GetValue("IEMenu");
             public static string AppSetting => GetValue("AppSetting");
             public static string AboutApp => GetValue("AboutApp");
             public static string Dictionaries => GetValue("Dictionaries");
@@ -97,23 +93,19 @@ namespace ContextMenuManager
             public static string LnkFile => GetValue("LnkFile");
             public static string UwpLnk => GetValue("UwpLnk");
             public static string ExeFile => GetValue("ExeFile");
-            public static string TextFile => GetValue("TextFile");
-            public static string DocumentFile => GetValue("DocumentFile");
-            public static string ImageFile => GetValue("ImageFile");
-            public static string VideoFile => GetValue("VideoFile");
-            public static string AudioFile => GetValue("AudioFile");
-            public static string ImageDirectory => GetValue("ImageDirectory");
-            public static string VideoDirectory => GetValue("VideoDirectory");
-            public static string AudioDirectory => GetValue("AudioDirectory");
             public static string UnknownType => GetValue("UnknownType");
-            public static string CustomType => GetValue("CustomType");
+            public static string CustomExtension => GetValue("CustomExtension");
+            public static string PerceivedType => GetValue("PerceivedType");
+            public static string DirectoryType => GetValue("DirectoryType");
             public static string EnhanceMenu => GetValue("EnhanceMenu");
             public static string ThirdRules => GetValue("ThirdRules");
             public static string GuidBlocked => GetValue("GuidBlocked");
+            public static string DragDrop => GetValue("DragDrop");
             public static string PublicReferences => GetValue("PublicReferences");
+            public static string IEMenu => GetValue("IEMenu");
         }
 
-        /// <summary>菜单</summary>
+        /// <summary>程序内右键菜单</summary>
         public static class Menu
         {
             private static string GetValue(string key) => GetStringValue("Menu", key);
@@ -132,9 +124,11 @@ namespace ContextMenuManager
             public static string OnlyInExplorer => GetValue("OnlyInExplorer");
             public static string NoWorkingDirectory => GetValue("NoWorkingDirectory");
             public static string NeverDefault => GetValue("NeverDefault");
+            public static string ShowAsDisabledIfHidden => GetValue("ShowAsDisabledIfHidden");
             public static string Details => GetValue("Details");
             public static string WebSearch => GetValue("WebSearch");
             public static string ChangeCommand => GetValue("ChangeCommand");
+            public static string RunAsAdministrator => GetValue("RunAsAdministrator");
             public static string FileProperties => GetValue("FileProperties");
             public static string FileLocation => GetValue("FileLocation");
             public static string RegistryLocation => GetValue("RegistryLocation");
@@ -146,6 +140,9 @@ namespace ContextMenuManager
             public static string BlockGuid => GetValue("BlockGuid");
             public static string AddGuidDic => GetValue("AddGuidDic");
             public static string InitialData => GetValue("InitialData");
+            public static string BeforeSeparator => GetValue("BeforeSeparator");
+            public static string ChangeGroup => GetValue("ChangeGroup");
+            public static string RestoreDefault => GetValue("RestoreDefault");
             public static string Edit => GetValue("Edit");
             public static string Save => GetValue("Save");
         }
@@ -172,12 +169,19 @@ namespace ContextMenuManager
             public static string ShareWithSkype => GetValue("ShareWithSkype");
             public static string NewItem => GetValue("NewItem");
             public static string AddGuidBlockedItem => GetValue("AddGuidBlockedItem");
+            public static string SelectExtension => GetValue("SelectExtension");
+            public static string SelectPerceivedType => GetValue("SelectPerceivedType");
+            public static string SelectDirectoryType => GetValue("SelectDirectoryType");
             public static string CurrentExtension => GetValue("CurrentExtension");
-            public static string SetPerceivedType => GetValue("SetPerceivedType");
+            public static string CurrentPerceivedType => GetValue("CurrentPerceivedType");
+            public static string CurrentDirectoryType => GetValue("CurrentDirectoryType");
             public static string EditSubItems => GetValue("EditSubItems");
             public static string InvalidItem => GetValue("InvalidItem");
             public static string Separator => GetValue("Separator");
             public static string LockNewMenu => GetValue("LockNewMenu");
+            public static string WinXSortable => GetValue("WinXSortable");
+            public static string ShowFilePath => GetValue("ShowFilePath");
+            public static string OpenMoreRegedit => GetValue("OpenMoreRegedit");
         }
 
         public static class Dialog
@@ -187,9 +191,6 @@ namespace ContextMenuManager
             public static string Cancel => GetValue("Cancel");
             public static string Browse => GetValue("Browse");
             public static string Program => GetValue("Program");
-            public static string NewShellItem => GetValue("NewShellItem");
-            public static string NewSendToItem => GetValue("NewSendToItem");
-            public static string NewOpenWithItem => GetValue("NewOpenWithItem");
             public static string ItemText => GetValue("ItemText");
             public static string ItemCommand => GetValue("ItemCommand");
             public static string CommandArguments => GetValue("CommandArguments");
@@ -199,14 +200,26 @@ namespace ContextMenuManager
             public static string MultiMenu => GetValue("MultiMenu");
             public static string Public => GetValue("Public");
             public static string Private => GetValue("Private");
+            public static string Administrator => GetValue("Administrator");
             public static string InputGuid => GetValue("InputGuid");
             public static string AddGuidDic => GetValue("AddGuidDic");
             public static string DeleteGuidDic => GetValue("DeleteGuidDic");
-            public static string SelectExtension => GetValue("SelectExtension");
+            public static string TextFile => GetValue("TextFile");
+            public static string DocumentFile => GetValue("DocumentFile");
+            public static string ImageFile => GetValue("ImageFile");
+            public static string VideoFile => GetValue("VideoFile");
+            public static string AudioFile => GetValue("AudioFile");
+            public static string CompressedFile => GetValue("CompressedFile");
+            public static string SystemFile => GetValue("SystemFile");
+            public static string DocumentDirectory => GetValue("DocumentDirectory");
+            public static string ImageDirectory => GetValue("ImageDirectory");
+            public static string VideoDirectory => GetValue("VideoDirectory");
+            public static string AudioDirectory => GetValue("AudioDirectory");
             public static string CheckReference => GetValue("CheckReference");
             public static string CheckCopy => GetValue("CheckCopy");
             public static string SelectSubMenuMode => GetValue("SelectSubMenuMode");
             public static string RegistryFile => GetValue("RegistryFile");
+            public static string SelectGroup => GetValue("SelectGroup");
         }
 
         /// <summary>消息框</summary>
@@ -218,6 +231,7 @@ namespace ContextMenuManager
             public static string StringParsingFailed => GetValue("StringParsingFailed");
             public static string TextLengthCannotExceed80 => GetValue("TextLengthCannotExceed80");
             public static string ConfirmDeletePermanently => GetValue("ConfirmDeletePermanently");
+            public static string DeleteButCanRestore => GetValue("DeleteButCanRestore");
             public static string ConfirmDeleteReference => GetValue("ConfirmDeleteReference");
             public static string ConfirmDelete => GetValue("ConfirmDelete");
             public static string ConfirmDeleteReferenced => GetValue("ConfirmDeleteReferenced");
@@ -238,6 +252,9 @@ namespace ContextMenuManager
             public static string FolderNotExists => GetValue("FolderNotExists");
             public static string NoUpdateDetected => GetValue("NoUpdateDetected");
             public static string AuthorityProtection => GetValue("AuthorityProtection");
+            public static string WinXSorted => GetValue("WinXSorted");
+            public static string RestoreDefault => GetValue("RestoreDefault");
+            public static string DeleteGroup => GetValue("DeleteGroup");
         }
 
         /// <summary>其他文本</summary>
@@ -257,9 +274,9 @@ namespace ContextMenuManager
             public static string Dictionaries => GetValue("Dictionaries");
             public static string Donate => GetValue("Donate");
             public static string DonationList => GetValue("DonationList");
-            public static string ConfigFile => GetValue("ConfigFile");
-            public static string SaveToAppData => GetValue("SaveToAppData");
-            public static string SaveToAppDir => GetValue("SaveToAppDir");
+            public static string ConfigPath => GetValue("ConfigPath");
+            public static string AppDataDir => GetValue("AppDataDir");
+            public static string AppDir => GetValue("AppDir");
             public static string OpenConfigDir => GetValue("OpenConfigDir");
             public static string AutoBackup => GetValue("AutoBackup");
             public static string OpenBackupDir => GetValue("OpenBackupDir");
@@ -282,7 +299,6 @@ namespace ContextMenuManager
             public static string EditSubItems => GetValue("EditSubItems");
             public static string InvalidItem => GetValue("InvalidItem");
             public static string AddSeparator => GetValue("AddSeparator");
-            public static string Separator => GetValue("Separator");
             public static string AddReference => GetValue("AddReference");
             public static string AddFromParentMenu => GetValue("AddFromParentMenu");
             public static string DeleteGuidDic => GetValue("DeleteGuidDic");
@@ -291,7 +307,9 @@ namespace ContextMenuManager
             public static string LastCheckUpdateTime => GetValue("LastCheckUpdateTime");
             public static string OpenLanguagesDir => GetValue("OpenLanguagesDir");
             public static string OpenDictionariesDir => GetValue("OpenDictionariesDir");
-            public static string ShareWithSkype => GetValue("ShareWithSkype");
+            public static string ConfigPath => GetValue("ConfigPath");
+            public static string CommandFiles => GetValue("CommandFiles");
+            public static string CreateGroup => GetValue("CreateGroup");
         }
     }
 }

+ 1 - 1
ContextMenuManager/BulePointLilac.Controls/IconDialog.cs → ContextMenuManager/BluePointLilac.Controls/IconDialog.cs

@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
 using System.Text;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Controls
+namespace BluePointLilac.Controls
 {
     public sealed class IconDialog : CommonDialog
     {

+ 2 - 2
ContextMenuManager/BulePointLilac.Controls/InputDialog.cs → ContextMenuManager/BluePointLilac.Controls/InputDialog.cs

@@ -1,10 +1,10 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using ContextMenuManager;
 using System;
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Controls
+namespace BluePointLilac.Controls
 {
     public sealed class InputDialog : CommonDialog
     {

+ 2 - 1
ContextMenuManager/BulePointLilac.Controls/MyCheckBox.cs → ContextMenuManager/BluePointLilac.Controls/MyCheckBox.cs

@@ -1,7 +1,7 @@
 using ContextMenuManager;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Controls
+namespace BluePointLilac.Controls
 {
     public class MyCheckBox : PictureBox
     {
@@ -18,6 +18,7 @@ namespace BulePointLilac.Controls
 
         public MyCheckBox()
         {
+            this.Checked = false;
             this.Cursor = Cursors.Hand;
             this.SizeMode = PictureBoxSizeMode.AutoSize;
         }

+ 6 - 5
ContextMenuManager/BulePointLilac.Controls/MyListBox.cs → ContextMenuManager/BluePointLilac.Controls/MyListBox.cs

@@ -1,10 +1,10 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System;
 using System.Collections.Generic;
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Controls
+namespace BluePointLilac.Controls
 {
     public class MyListBox : Panel
     {
@@ -58,12 +58,14 @@ namespace BulePointLilac.Controls
                 value.ForeColor = Color.FromArgb(0, 138, 217);
                 //value.BackColor = Color.FromArgb(200, 230, 250);
                 value.Focus();
+                HoveredItemChanged?.Invoke(null, null);
             }
         }
 
+        public event EventHandler HoveredItemChanged;
+
         public void AddItem(MyListItem item)
         {
-            if(item.Parent == this) return;
             item.Parent = this;
             item.Width = Owner.Width - item.Margin.Horizontal;
             Owner.Resize += (sender, e) => item.Width = Owner.Width - item.Margin.Horizontal;
@@ -136,7 +138,6 @@ namespace BulePointLilac.Controls
             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);
@@ -204,7 +205,7 @@ namespace BulePointLilac.Controls
         };
         private readonly Label lblSeparator = new Label
         {
-            BackColor = Color.FromArgb(200, 200, 200),
+            BackColor = Color.FromArgb(220, 220, 220),
             Dock = DockStyle.Bottom,
             Height = 1
         };

+ 2 - 2
ContextMenuManager/BulePointLilac.Controls/MyMainForm.cs → ContextMenuManager/BluePointLilac.Controls/MyMainForm.cs

@@ -1,9 +1,9 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System;
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Controls
+namespace BluePointLilac.Controls
 {
     public class MyMainForm : Form
     {

+ 6 - 6
ContextMenuManager/BulePointLilac.Controls/MySideBar.cs → ContextMenuManager/BluePointLilac.Controls/MySideBar.cs

@@ -1,11 +1,11 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System;
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Controls
+namespace BluePointLilac.Controls
 {
-    public class MySideBar : Panel
+    public sealed class MySideBar : Panel
     {
         public MySideBar()
         {
@@ -92,7 +92,7 @@ namespace BulePointLilac.Controls
         };
         readonly Label LblSeparator = new Label
         {
-            BackColor = Color.FromArgb(200, 200, 200),
+            BackColor = Color.FromArgb(220, 220, 220),
             Dock = DockStyle.Right,
             Width = 1,
         };
@@ -106,7 +106,7 @@ namespace BulePointLilac.Controls
         /// <summary>绘制所有项目作为底图</summary>
         private void PaintItems()
         {
-            this.BackgroundImage = new Bitmap(Width, Height);
+            this.BackgroundImage = new Bitmap(Width, ItemHeight * ItemNames.Length);
             using(Graphics g = Graphics.FromImage(BackgroundImage))
             {
                 g.Clear(BackColor);
@@ -132,6 +132,7 @@ namespace BulePointLilac.Controls
         /// <summary>刷新选中的项目</summary>
         private void RefreshItem(Panel panel, int index)
         {
+            panel.CreateGraphics().Clear(panel.BackColor);
             panel.Top = index < 0 ? -ItemHeight : (TopSpace + index * ItemHeight);
             panel.Text = index < 0 ? null : ItemNames[index];
             panel.Refresh();
@@ -141,7 +142,6 @@ namespace BulePointLilac.Controls
         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));

+ 18 - 6
ContextMenuManager/BulePointLilac.Controls/MyStatusBar.cs → ContextMenuManager/BluePointLilac.Controls/MyStatusBar.cs

@@ -1,11 +1,11 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System;
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Controls
+namespace BluePointLilac.Controls
 {
-    public class MyStatusBar : Panel
+    public sealed class MyStatusBar : Panel
     {
         public static readonly string DefaultText = $"Ver: {new Version(Application.ProductVersion).ToString(2)}    {Application.CompanyName}";
 
@@ -22,17 +22,29 @@ namespace BulePointLilac.Controls
         public new string Text
         {
             get => base.Text;
-            set { base.Text = value; Refresh(); }
+            set
+            {
+                if(base.Text == value) return;
+                base.Text = value; Refresh();
+            }
         }
         public new Font Font
         {
             get => base.Font;
-            set { base.Font = value; Refresh(); }
+            set
+            {
+                if(base.Font == value) return;
+                base.Font = value; Refresh();
+            }
         }
         public new Color ForeColor
         {
             get => base.ForeColor;
-            set { base.ForeColor = value; Refresh(); }
+            set
+            {
+                if(base.ForeColor == value) return;
+                base.ForeColor = value; Refresh();
+            }
         }
 
         protected override void OnPaint(PaintEventArgs e)

+ 12 - 5
ContextMenuManager/BulePointLilac.Controls/MyToolBar.cs → ContextMenuManager/BluePointLilac.Controls/MyToolBar.cs

@@ -1,11 +1,11 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System;
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Controls
+namespace BluePointLilac.Controls
 {
-    public class MyToolBar : FlowLayoutPanel
+    public sealed class MyToolBar : FlowLayoutPanel
     {
         public MyToolBar()
         {
@@ -43,7 +43,7 @@ namespace BulePointLilac.Controls
             button.Margin = new Padding(12.DpiZoom(), 4.DpiZoom(), 0, 0);
             button.MouseDown += (sender, e) =>
             {
-                if(e.Button == MouseButtons.Left) { SelectedButton = button; button.Cursor = Cursors.Default; }
+                if(button.CanBeSelected) { SelectedButton = button; button.Cursor = Cursors.Default; }
             };
             button.MouseEnter += (sender, e) =>
             {
@@ -61,7 +61,7 @@ namespace BulePointLilac.Controls
         }
     }
 
-    public class MyToolBarButton : Panel
+    public sealed class MyToolBarButton : Panel
     {
         public MyToolBarButton(Image image, string text)
         {
@@ -73,6 +73,7 @@ namespace BulePointLilac.Controls
             lblText.Resize += (sender, e) => lblText.Left = (Width - lblText.Width) / 2;
             this.Image = image;
             this.Text = text;
+            this.CanBeSelected = true;
             MyToolTip.SetToolTip(this, text);
             lblText.SetEnabled(false);
         }
@@ -108,5 +109,11 @@ namespace BulePointLilac.Controls
             get => BackColor.A / 255;
             set => BackColor = Color.FromArgb((int)(value * 255), Color.White);
         }
+        public bool CanBeSelected { get; set; }
+
+        protected override void OnMouseDown(MouseEventArgs e)
+        {
+            if(e.Button == MouseButtons.Left) base.OnMouseDown(e);
+        }
     }
 }

+ 3 - 3
ContextMenuManager/BulePointLilac.Controls/MyToolTip.cs → ContextMenuManager/BluePointLilac.Controls/MyToolTip.cs

@@ -1,9 +1,9 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Controls
+namespace BluePointLilac.Controls
 {
-    public sealed class MyToolTip
+    public static class MyToolTip
     {
         public static void SetToolTip(Control ctr, string tip)
         {

+ 1 - 1
ContextMenuManager/BulePointLilac.Controls/PictureButton.cs → ContextMenuManager/BluePointLilac.Controls/PictureButton.cs

@@ -2,7 +2,7 @@
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Controls
+namespace BluePointLilac.Controls
 {
     public class PictureButton : PictureBox
     {

+ 5 - 5
ContextMenuManager/BulePointLilac.Controls/ReadOnlyRichTextBox.cs → ContextMenuManager/BluePointLilac.Controls/ReadOnlyRichTextBox.cs

@@ -1,10 +1,10 @@
-using System.Diagnostics;
-using System.Drawing;
+using System.Drawing;
 using System.Windows.Forms;
+using BluePointLilac.Methods;
 
-namespace BulePointLilac.Controls
+namespace BluePointLilac.Controls
 {
-    sealed class ReadOnlyRichTextBox : RichTextBox
+    public sealed class ReadOnlyRichTextBox : RichTextBox
     {
         public ReadOnlyRichTextBox()
         {
@@ -30,7 +30,7 @@ namespace BulePointLilac.Controls
 
         protected override void OnLinkClicked(LinkClickedEventArgs e)
         {
-            base.OnLinkClicked(e); Process.Start(e.LinkText);
+            base.OnLinkClicked(e); ExternalProgram.OpenUrl(e.LinkText);
         }
     }
 }

+ 2 - 2
ContextMenuManager/BulePointLilac.Controls/ResizbleForm.cs → ContextMenuManager/BluePointLilac.Controls/ResizbleForm.cs

@@ -1,10 +1,10 @@
 using System;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Controls
+namespace BluePointLilac.Controls
 {
     /// <summary>限制水平、竖直方向调整大小的窗体</summary>
-    class ResizbleForm : Form
+    public class ResizbleForm : Form
     {
         /// <summary>水平方向可调整大小</summary>
         public bool HorizontalResizable { get; set; } = true;

+ 1 - 1
ContextMenuManager/BulePointLilac.Methods/ControlExtension.cs → ContextMenuManager/BluePointLilac.Methods/ControlExtension.cs

@@ -2,7 +2,7 @@
 using System.Runtime.InteropServices;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public static class ControlExtension
     {

+ 34 - 0
ContextMenuManager/BluePointLilac.Methods/DesktopIni.cs

@@ -0,0 +1,34 @@
+using System.IO;
+
+namespace BluePointLilac.Methods
+{
+    public static class DesktopIni
+    {
+        private const string LocalizedFileNames = "LocalizedFileNames";
+
+        private static string GetIniPath(string filePath) => $@"{Path.GetDirectoryName(filePath)}\desktop.ini";
+
+        public static void DeleteLocalizedFileNames(string filePath)
+        {
+            IniWriter writer = new IniWriter(GetIniPath(filePath));
+            string fileName = Path.GetFileName(filePath);
+            writer.DeleteKey(LocalizedFileNames, fileName);
+        }
+
+        public static void SetLocalizedFileNames(string filePath, string name)
+        {
+            IniWriter writer = new IniWriter(GetIniPath(filePath));
+            string fileName = Path.GetFileName(filePath);
+            writer.SetValue(LocalizedFileNames, fileName, name);
+        }
+
+        public static string GetLocalizedFileNames(string filePath, bool translate = false)
+        {
+            IniWriter writer = new IniWriter(GetIniPath(filePath));
+            string fileName = Path.GetFileName(filePath);
+            string name = writer.GetValue(LocalizedFileNames, fileName);
+            if(translate) name = ResourceString.GetDirectString(name);
+            return name;
+        }
+    }
+}

+ 1 - 1
ContextMenuManager/BulePointLilac.Methods/DirectoryEx.cs → ContextMenuManager/BluePointLilac.Methods/DirectoryEx.cs

@@ -1,6 +1,6 @@
 using System.IO;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public static class DirectoryEx
     {

+ 6 - 11
ContextMenuManager/BulePointLilac.Methods/EncodingType.cs → ContextMenuManager/BluePointLilac.Methods/EncodingType.cs

@@ -1,9 +1,8 @@
 using System;
 using System.IO;
-using System.Linq;
 using System.Text;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     /// 获取文本文件编码类型
     /// 代码主要为转载,仅做简单改动
@@ -20,10 +19,6 @@ namespace BulePointLilac.Methods
                 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> 
@@ -33,11 +28,11 @@ namespace BulePointLilac.Methods
             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;
+            if(IsUTF8Bytes(ss)) return Encoding.UTF8;
+            if(ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF) return Encoding.UTF8;//带BOM 
+            if(ss[0] == 0xFE && ss[1] == 0xFF) return Encoding.BigEndianUnicode;     //UTF-16BE
+            if(ss[0] == 0xFF && ss[1] == 0xFE) return Encoding.Unicode;              //UTF-16LE
+            return Encoding.Default;
         }
 
         /// <summary>判断是否是不带 BOM 的 UTF8 格式</summary> 

+ 177 - 0
ContextMenuManager/BluePointLilac.Methods/ExternalProgram.cs

@@ -0,0 +1,177 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+
+namespace BluePointLilac.Methods
+{
+    public static class ExternalProgram
+    {
+        public static void JumpRegEdit(string regPath, string valueName = null, bool moreOpen = false)
+        {
+            IntPtr hMain = FindWindow("RegEdit_RegEdit", null);
+            if(moreOpen || hMain == IntPtr.Zero)
+            {
+                using(Process process = Process.Start("regedit.exe", "-m"))
+                {
+                    process.WaitForInputIdle();
+                    hMain = process.MainWindowHandle;
+                }
+            }
+
+            IntPtr hTree = FindWindowEx(hMain, IntPtr.Zero, "SysTreeView32", null);
+            IntPtr hList = FindWindowEx(hMain, IntPtr.Zero, "SysListView32", null);
+
+            SetForegroundWindow(hTree);
+            SetFocus(hTree);
+            SendMessage(hTree, WM_KEYDOWN, VK_HOME, null);
+            Thread.Sleep(50);
+            SendMessage(hTree, WM_KEYDOWN, VK_RIGHT, null);
+            foreach(char chr in Encoding.Default.GetBytes(regPath))
+            {
+                if(chr == '\\')
+                {
+                    Thread.Sleep(50);
+                    SendMessage(hTree, WM_KEYDOWN, VK_RIGHT, null);
+                }
+                else SendMessage(hTree, WM_CHAR, Convert.ToInt16(chr), null);
+            }
+
+            if(!string.IsNullOrEmpty(valueName))
+            {
+                Thread.Sleep(50);
+                SetForegroundWindow(hList);
+                SetFocus(hList);
+                SendMessage(hList, WM_KEYDOWN, VK_HOME, null);
+                foreach(char chr in Encoding.Default.GetBytes(valueName))
+                {
+                    SendMessage(hList, WM_CHAR, Convert.ToInt16(chr), null);
+                }
+            }
+        }
+
+        public static void JumpExplorer(string filePath)
+        {
+            using(Process process = new Process())
+            {
+                if(File.Exists(filePath))
+                {
+                    process.StartInfo.FileName = "explorer.exe";
+                    process.StartInfo.Arguments = $"/select,{filePath}";
+                    process.Start();
+                }
+                else if(Directory.Exists(filePath))
+                {
+                    process.StartInfo.FileName = filePath;
+                    process.Start();
+                }
+            }
+        }
+
+        public static bool ShowPropertiesDialog(string filePath)
+        {
+            SHELLEXECUTEINFO info = new SHELLEXECUTEINFO
+            {
+                lpVerb = "Properties",
+                //lpParameters = "详细信息";//显示选项卡,此处有语言差异
+                lpFile = filePath,
+                nShow = SW_SHOW,
+                fMask = SEE_MASK_INVOKEIDLIST,
+                cbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO))
+            };
+            return ShellExecuteEx(ref info);
+        }
+
+        public static void RestartExplorer()
+        {
+            using(Process process = new Process())
+            {
+                process.StartInfo = new ProcessStartInfo
+                {
+                    FileName = "taskkill.exe",
+                    Arguments = "-f -im explorer.exe",
+                    WindowStyle = ProcessWindowStyle.Hidden
+                };
+                process.Start();
+                process.WaitForExit();
+                process.StartInfo = new ProcessStartInfo("explorer.exe");
+                process.Start();
+            }
+        }
+
+        public static void OpenUrl(string url)
+        {
+            //替换网址转义符
+            url = url.Replace("%", "%25").Replace("#", "%23").Replace("&", "%26").Replace("+", "%2B");
+            using(Process process = new Process())
+            {
+                //通过explorer来调用浏览器打开链接,避免管理员权限影响
+                process.StartInfo.FileName = "explorer.exe";
+                process.StartInfo.Arguments = $"\"{url}\"";
+                process.Start();
+            }
+        }
+
+        public static void OpenNotepadWithText(string text)
+        {
+            using(Process process = Process.Start("notepad.exe"))
+            {
+                Thread.Sleep(200);
+                IntPtr handle = FindWindowEx(process.MainWindowHandle, IntPtr.Zero, "Edit", null);
+                SendMessage(handle, WM_SETTEXT, 0, text);
+            }
+        }
+
+        private const int SW_SHOW = 5;
+        private const uint SEE_MASK_INVOKEIDLIST = 12;
+        private const int WM_SETTEXT = 0xC;
+        private const int WM_KEYDOWN = 0x100;
+        private const int WM_CHAR = 0x102;
+        private const int VK_HOME = 0x24;
+        private const int VK_RIGHT = 0x27;
+
+        [DllImport("User32.dll")]
+        private static extern bool SetForegroundWindow(IntPtr hWnd);
+
+        [DllImport("User32.dll")]
+        private static extern bool SetFocus(IntPtr hWnd);
+
+        [DllImport("user32.dll")]
+        private static extern IntPtr FindWindow(string lpszClass, string lpszWindow);
+
+        [DllImport("user32.dll")]
+        private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChild, string lpszClass, string lpszWindow);
+
+        [DllImport("user32.dll")]
+        private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
+        [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;
+        }
+    }
+}

+ 1 - 1
ContextMenuManager/BulePointLilac.Methods/FileExtension.cs → ContextMenuManager/BluePointLilac.Methods/FileExtension.cs

@@ -1,6 +1,6 @@
 using Microsoft.Win32;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public static class FileExtension
     {

+ 11 - 3
ContextMenuManager/BulePointLilac.Methods/GuidEx.cs → ContextMenuManager/BluePointLilac.Methods/GuidEx.cs

@@ -1,22 +1,30 @@
 using System;
+using System.Text.RegularExpressions;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     //为兼容.Net Framework 3.5,无法引用Microsoft.CSharp.dll(中的Guid.TryParse)写了这个扩展方法
     public static class GuidEx
     {
         public static bool TryParse(string str, out Guid guid)
         {
-            try
+            if(IsGuid(str))
             {
                 guid = new Guid(str);
                 return true;
             }
-            catch
+            else
             {
                 guid = Guid.Empty;
                 return false;
             }
         }
+
+        public static bool IsGuid(string str)
+        {
+            if(string.IsNullOrEmpty(str)) return false;
+            Regex guidRegEx = new Regex(@"[a-fA-F0-9]{8}(\-[a-fA-F0-9]{4}){3}\-[a-fA-F0-9]{12}");
+            return guidRegEx.IsMatch(str);
+        }
     }
 }

+ 1 - 1
ContextMenuManager/BulePointLilac.Methods/HighDpi.cs → ContextMenuManager/BluePointLilac.Methods/HighDpi.cs

@@ -2,7 +2,7 @@
 using System.Windows;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public static class HighDpi
     {

+ 1 - 1
ContextMenuManager/BulePointLilac.Methods/ImageExtension.cs → ContextMenuManager/BluePointLilac.Methods/ImageExtension.cs

@@ -2,7 +2,7 @@
 using System.Drawing.Drawing2D;
 using System.Drawing.Imaging;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public static class ImageExtension
     {

+ 2 - 1
ContextMenuManager/BulePointLilac.Methods/IniReader.cs → ContextMenuManager/BluePointLilac.Methods/IniReader.cs

@@ -4,7 +4,7 @@ using System.IO;
 using System.Linq;
 using System.Text;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public sealed class IniReader
     {
@@ -65,6 +65,7 @@ namespace BulePointLilac.Methods
                     int k = lines[j].IndexOf('=');
                     string key = lines[j].Substring(0, k).TrimEnd();
                     string value = lines[j].Substring(k + 1).TrimStart();
+                    if(keyValues.ContainsKey(key)) continue;
                     keyValues.Add(key, value);
                 }
             }

+ 3 - 3
ContextMenuManager/BulePointLilac.Methods/IniWriter.cs → ContextMenuManager/BluePointLilac.Methods/IniWriter.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using System.IO;
 using System.Text;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public sealed class IniWriter
     {
@@ -16,7 +16,7 @@ namespace BulePointLilac.Methods
         }
 
         public string FilePath { get; set; }
-        public Encoding Encoding { get; set; } = Encoding.UTF8;
+        public Encoding Encoding { get; set; } = Encoding.Unicode;
         public bool DeleteFileWhenEmpty { get; set; }
 
         private List<string> GetLines()
@@ -85,7 +85,7 @@ namespace BulePointLilac.Methods
             {
                 if(key != null && value != null)
                 {
-                    if(lines.Count > 0) lines.Add(string.Empty);//添加空行
+                    lines.Add(string.Empty);//添加空行
                     //目标section不存在则添加到最后
                     lines.Add(sectionLine);
                     lines.Add(keyLine);

+ 1 - 1
ContextMenuManager/BulePointLilac.Methods/MessageBoxEx.cs → ContextMenuManager/BluePointLilac.Methods/MessageBoxEx.cs

@@ -1,7 +1,7 @@
 using ContextMenuManager;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public static class MessageBoxEx
     {

+ 53 - 45
ContextMenuManager/BulePointLilac.Methods/ObjectPath.cs → ContextMenuManager/BluePointLilac.Methods/ObjectPath.cs

@@ -1,20 +1,18 @@
 using Microsoft.Win32;
 using System;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.IO;
 using System.Linq;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
-    public class ObjectPath
+    public static class ObjectPath
     {
         /// <summary>路径类型</summary>
         public enum PathType { File, Directory, Registry }
-        //右键菜单仅支持%SystemRoot%\System32和%SystemRoot%两个环境变量,不考虑其他系统环境变量和用户环境变量,和Win+R命令有区别
-        private static readonly string[] EnvironmentDirectorys = { @"%SystemRoot%\System32", @"%SystemRoot%" };
         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 const string ShellExecuteCommand = "mshta vbscript:createobject(\"shell.application\").shellexecute(\"";
 
         private static readonly char[] IllegalChars = { '/', '*', '?', '\"', '<', '>', '|' };
         private static readonly List<string> IgnoreCommandParts = new List<string> { "", "%1", "%v" };
@@ -22,26 +20,28 @@ namespace BulePointLilac.Methods
         /// <summary>根据文件名获取完整的文件路径</summary>
         /// <remarks>fileName为Win+R、注册表等可直接使用的文件名</remarks>
         /// <param name="fileName">文件名</param>
-        /// <returns>成功提取返回现有文件路径,否则返回值为null</returns>
+        /// <returns>成功提取返回true, fullPath为现有文件路径; 否则返回false, fullPath为null</returns>
         public static bool GetFullFilePath(string fileName, out string fullPath)
         {
             fullPath = null;
-            if(File.Exists(fileName))
-            {
-                fullPath = fileName;
-                return true;
-            }
+            if(fileName.IsNullOrWhiteSpace()) return false;
+
             foreach(string name in new[] { fileName, $"{fileName}.exe" })
             {
-                foreach(string dir in EnvironmentDirectorys)
+                //右键菜单仅支持%SystemRoot%\System32和%SystemRoot%两个环境变量,不考虑其他系统环境变量和用户环境变量,和Win+R命令有区别
+                foreach(string dir in new[] { "", @"%SystemRoot%\System32\", @"%SystemRoot%\" })
                 {
-                    fullPath = Environment.ExpandEnvironmentVariables($@"{dir}\{name}");
+                    fullPath = Environment.ExpandEnvironmentVariables($@"{dir}{name}");
                     if(File.Exists(fullPath)) return true;
                 }
 
-                fullPath = Registry.GetValue($@"{RegAppPath}\{name}", "", null)?.ToString();
-                if(File.Exists(fullPath)) return true;
+                if(!name.Contains("\\"))
+                {
+                    fullPath = Registry.GetValue($@"{RegAppPath}\{name}", "", null)?.ToString();
+                    if(File.Exists(fullPath)) return true;
+                }
             }
+            fullPath = null;
             return false;
         }
 
@@ -54,27 +54,54 @@ namespace BulePointLilac.Methods
             command = Environment.ExpandEnvironmentVariables(command).Replace(@"\\", @"\");
             if(File.Exists(command)) return command;
 
+            if(command.StartsWith(ShellExecuteCommand, StringComparison.OrdinalIgnoreCase))
+            {
+                command = command.Remove(0, ShellExecuteCommand.Length);
+                string[] arr = command.Split(new[] { "\",\"" }, StringSplitOptions.None);
+                if(arr.Length > 0)
+                {
+                    string filePath = null;
+                    string fileName = arr[0];
+                    if(arr.Length > 1)
+                    {
+                        string arguments = arr[1];
+                        filePath = ExtractFilePath(arguments);
+                        if(filePath != null) return filePath;
+                    }
+                    if(GetFullFilePath(fileName, out filePath)) return filePath;
+                }
+            }
+
             string[] strs = Array.FindAll(command.Split(IllegalChars), str
                 => IgnoreCommandParts.Any(part => !part.Equals(str.Trim()))).Reverse().ToArray();
-            foreach(string str in strs)
+            foreach(string str1 in strs)
             {
-                int index = str.Length;
+                string str2 = str1;
+                int index = -1;
                 do
                 {
-                    string path1 = str.Substring(0, index);
-                    List<string> paths = new List<string> { path1 };
-                    if(path1.Contains(",")) paths.Add(path1.Substring(0, path1.LastIndexOf(',')));
+                    List<string> paths = new List<string>();
+                    string path1 = str2.Substring(index + 1);
+                    paths.Add(path1);
+                    if(path1.Contains(",")) paths.AddRange(path1.Split(','));
+                    if(index > 0)
+                    {
+                        string path2 = str2.Substring(0, index);
+                        paths.Add(path2);
+                        if(path2.Contains(",")) paths.AddRange(path2.Split(','));
+                    }
                     foreach(string path in paths)
                     {
                         if(GetFullFilePath(path, out string fullPath)) return fullPath;
                     }
-                    index = path1.LastIndexOf(' ');
-                } while(index != -1);
+                    str2 = path1;
+                    index = str2.IndexOf(' ');
+                }
+                while(index != -1);
             }
             return null;
         }
 
-
         /// <summary>移除文件或文件夹名称中的非法字符</summary>
         /// <param name="fileName">文件或文件夹名称</param>
         /// <returns>返回移除非法字符后的文件或文件夹名称</returns>
@@ -84,7 +111,6 @@ namespace BulePointLilac.Methods
             return fileName.Replace("\\", "").Replace(":", "");
         }
 
-
         /// <summary>判断文件或文件夹或注册表项是否存在</summary>
         /// <param name="path">文件或文件夹或注册表项路径</param>
         /// <param name="type">路径类型</param>
@@ -108,39 +134,21 @@ namespace BulePointLilac.Methods
         /// <param name="oldPath">目标路径</param>
         /// <param name="type">路径类型</param>
         /// <returns>如果目标路径不存在则返回目标路径,否则返回带序号的新路径</returns>
-        public static string GetNewPathWithIndex(string oldPath, PathType type)
+        public static string GetNewPathWithIndex(string oldPath, PathType type, int startIndex = -1)
         {
             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;
+                if(startIndex > -1) newPath += startIndex;
                 newPath += extension;
-                index++;
+                startIndex++;
             } 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;
-            }
-        }
     }
 }

+ 14 - 5
ContextMenuManager/BulePointLilac.Methods/RegTrustedInstaller.cs → ContextMenuManager/BluePointLilac.Methods/RegTrustedInstaller.cs

@@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
 using System.Security.AccessControl;
 using System.Security.Principal;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     /// 获取TrustedInstaller权限注册表项的所有权
     /// 代码主要为转载,仅做简单改动
@@ -18,18 +18,21 @@ namespace BulePointLilac.Methods
             public const string TakeOwnership = "SeTakeOwnershipPrivilege";
             public const string Restore = "SeRestorePrivilege";
 
+            [StructLayout(LayoutKind.Sequential)]
             public struct LUID
             {
                 public int lowPart;
                 public int highPart;
             }
 
+            [StructLayout(LayoutKind.Sequential)]
             public struct LUID_AND_ATTRIBUTES
             {
                 public LUID Luid;
                 public int Attributes;
             }
 
+            [StructLayout(LayoutKind.Sequential)]
             public struct TOKEN_PRIVILEGES
             {
                 public int PrivilegeCount;
@@ -37,6 +40,7 @@ namespace BulePointLilac.Methods
                 public LUID_AND_ATTRIBUTES[] Privileges;
             }
 
+            [Flags]
             public enum PrivilegeAttributes
             {
                 /// <summary>特权被禁用.</summary>
@@ -51,6 +55,7 @@ namespace BulePointLilac.Methods
                 UsedForAccess = -2147483648
             }
 
+            [Flags]
             public enum TokenAccessRights
             {
                 /// <summary>向进程附加主令牌的权限.</summary>
@@ -82,6 +87,7 @@ namespace BulePointLilac.Methods
                 Execute = AccessTypeMasks.StandardRightsExecute | Impersonate
             }
 
+            [Flags]
             private enum AccessTypeMasks
             {
                 Delete = 65536,
@@ -98,14 +104,17 @@ namespace BulePointLilac.Methods
             }
 
             [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);
+            [return: MarshalAs(UnmanagedType.Bool)]
+            private static extern bool AdjustTokenPrivileges([In] IntPtr accessTokenHandle, [In] bool disableAllPrivileges,
+                [In] ref TOKEN_PRIVILEGES newState, [In] int bufferLength, [In, Out] ref TOKEN_PRIVILEGES previousState, [In, Out] ref int returnLength);
 
             [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
-            private static extern bool LookupPrivilegeValue(string systemName, string name, ref LUID luid);
+            [return: MarshalAs(UnmanagedType.Bool)]
+            private static extern bool LookupPrivilegeValue([In] string systemName, [In] string name, [In, Out] ref LUID luid);
 
             [DllImport("advapi32.dll", SetLastError = true)]
-            private static extern bool OpenProcessToken(IntPtr processHandle, TokenAccessRights desiredAccess, ref IntPtr tokenHandle);
+            [return: MarshalAs(UnmanagedType.Bool)]
+            private static extern bool OpenProcessToken([In] IntPtr processHandle, [In] TokenAccessRights desiredAccess, [In, Out] ref IntPtr tokenHandle);
 
             [DllImport("kernel32.dll", SetLastError = true)]
             private static extern int GetLastError();

+ 3 - 4
ContextMenuManager/BulePointLilac.Methods/RegistryEx.cs → ContextMenuManager/BluePointLilac.Methods/RegistryEx.cs

@@ -4,7 +4,7 @@ using System.Diagnostics;
 using System.IO;
 using System.Security.AccessControl;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public static class RegistryEx
     {
@@ -46,7 +46,6 @@ namespace BulePointLilac.Methods
         public static RegistryKey CreateSubKey(this RegistryKey key, string subKeyName, bool writable)
         {
             key.CreateSubKey(subKeyName).Close();
-            RegTrustedInstaller.TakeRegTreeOwnerShip($@"{key.Name}\{subKeyName}");
             return key.OpenSubKey(subKeyName, writable);
         }
 
@@ -142,8 +141,8 @@ namespace BulePointLilac.Methods
         public static void Export(string regPath, string filePath)
         {
             if(File.Exists(filePath)) File.Delete(filePath);
-            Process process = Process.Start("regedit.exe", $" /e \"{filePath}\" \"{regPath}\"");
-            process.WaitForExit();
+            using(Process process = Process.Start("regedit.exe", $"/e \"{filePath}\" \"{regPath}\""))
+                process.WaitForExit();
         }
     }
 }

+ 1 - 1
ContextMenuManager/BulePointLilac.Methods/ResourceIcon.cs → ContextMenuManager/BluePointLilac.Methods/ResourceIcon.cs

@@ -5,7 +5,7 @@ using System.IO;
 using System.Runtime.InteropServices;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public static class ResourceIcon
     {

+ 1 - 1
ContextMenuManager/BulePointLilac.Methods/ResourceString.cs → ContextMenuManager/BluePointLilac.Methods/ResourceString.cs

@@ -2,7 +2,7 @@
 using System.Runtime.InteropServices;
 using System.Text;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public static class ResourceString
     {

+ 1 - 1
ContextMenuManager/BulePointLilac.Methods/RichTextBoxExtension.cs → ContextMenuManager/BluePointLilac.Methods/RichTextBoxExtension.cs

@@ -4,7 +4,7 @@ using System.Text;
 using System.Windows.Forms;
 using System.Xml.Linq;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public static class RichTextBoxExtension
     {

+ 51 - 0
ContextMenuManager/BluePointLilac.Methods/SingleInstance.cs

@@ -0,0 +1,51 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace BluePointLilac.Methods
+{
+    public static class SingleInstance
+    {
+        public static bool IsRunning()
+        {
+            using(Process current = Process.GetCurrentProcess())
+            {
+                string fileName = current.MainModule.FileName;
+                string processName = Path.GetFileNameWithoutExtension(fileName);
+                foreach(Process process in Process.GetProcessesByName(processName))
+                {
+                    using(process)
+                    {
+                        if(process.Id == current.Id) continue;
+                        if(process.MainModule.FileName == fileName)
+                        {
+                            ShowWindowAsync(process.MainWindowHandle, 1);//SW_SHOWNORMAL
+                            SetForegroundWindow(process.MainWindowHandle);
+                            return true;
+                        }
+                    }
+                }
+                return false;
+            }
+        }
+
+        public static void Restart()
+        {
+            using(Process process = new Process())
+            {
+                process.StartInfo.FileName = Application.ExecutablePath;
+                process.StartInfo.Arguments = "Restart";
+                process.Start();
+            }
+            Application.Exit();
+        }
+
+        [DllImport("User32.dll")]
+        private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
+
+        [DllImport("User32.dll")]
+        private static extern bool SetForegroundWindow(IntPtr hWnd);
+    }
+}

+ 1 - 1
ContextMenuManager/BulePointLilac.Methods/StringExtension.cs → ContextMenuManager/BluePointLilac.Methods/StringExtension.cs

@@ -1,4 +1,4 @@
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     //为兼容.Net Framework 3.5,无法引用Microsoft.CSharp.dll(中的string.IsNullOrWhiteSpace)写了这个扩展方法
     public static class StringExtension

+ 1 - 1
ContextMenuManager/BulePointLilac.Methods/TextBoxExtension.cs → ContextMenuManager/BluePointLilac.Methods/TextBoxExtension.cs

@@ -1,7 +1,7 @@
 using System.Drawing;
 using System.Windows.Forms;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     public static class TextBoxExtension
     {

+ 1 - 1
ContextMenuManager/BulePointLilac.Methods/WindowsOsVersion.cs → ContextMenuManager/BluePointLilac.Methods/WindowsOsVersion.cs

@@ -1,6 +1,6 @@
 using System;
 
-namespace BulePointLilac.Methods
+namespace BluePointLilac.Methods
 {
     //判断Windows系统版本
     public static class WindowsOsVersion

+ 202 - 0
ContextMenuManager/BluePointLilac.Methods/WshShortcut.cs

@@ -0,0 +1,202 @@
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+using System.Text;
+
+namespace BluePointLilac.Methods
+{
+    //为兼容.Net Framework 3.5无法使用dynamic和Interop.IWshRuntimeLibrary.dll专门写出此类
+    public sealed class WshShortcut : IDisposable
+    {
+        private static readonly Type ShellType = Type.GetTypeFromCLSID(new Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8"));
+        private static readonly object Shell = Activator.CreateInstance(ShellType);
+        private static readonly BindingFlags InvokeMethodFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod;
+        private static readonly BindingFlags GetPropertyFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty;
+        private static readonly BindingFlags SetPropertyFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty;
+
+        private static object CreateShortcut(string lnkPath)
+        {
+            return ShellType.InvokeMember("CreateShortcut", InvokeMethodFlag, null, Shell, new[] { lnkPath });
+        }
+
+        public WshShortcut(string lnkPath)
+        {
+            //只调用CreateShortcut方法时,TargetPath值为null,
+            //其他属性值正常,只有调用一下Save方法才能获取到TargetPath值
+            this.lnkPath = lnkPath;
+            shortcut = CreateShortcut(lnkPath);
+            shortcutType = shortcut.GetType();
+            shellLink = (IShellLinkA)new ShellLink();
+            if(File.Exists(lnkPath))
+            {
+                Save();
+                ((IPersistFile)shellLink).Load(lnkPath, 2);//STGM_READWRITE
+            }
+        }
+
+        private readonly string lnkPath;
+        private readonly object shortcut;
+        private readonly Type shortcutType;
+        private readonly IShellLinkA shellLink;
+
+        public string FullName
+        {
+            get => GetValue("FullName")?.ToString();
+        }
+        public string TargetPath
+        {
+            get => GetValue("TargetPath")?.ToString();
+            set => SetValue("TargetPath", value);
+        }
+        public string Arguments
+        {
+            get => GetValue("Arguments")?.ToString();
+            set => SetValue("Arguments", value);
+        }
+        public string WorkingDirectory
+        {
+            get => GetValue("WorkingDirectory")?.ToString();
+            set => SetValue("WorkingDirectory", value);
+        }
+        public string IconLocation
+        {
+            get => GetValue("IconLocation")?.ToString();
+            set => SetValue("IconLocation", value);
+        }
+        public string Description
+        {
+            get => GetValue("Description")?.ToString();
+            set => SetValue("Description", value);
+        }
+        public string Hotkey
+        {
+            get => GetValue("Hotkey")?.ToString();
+            set => SetValue("Hotkey", value);
+        }
+        public int WindowStyle
+        {
+            get => Convert.ToInt32(GetValue("WindowStyle"));
+            set => SetValue("WindowStyle", value);
+        }
+        public bool RunAsAdministrator
+        {
+            get
+            {
+                ((IShellLinkDataList)shellLink).GetFlags(out ShellLinkDataFlags flags);
+                return (flags & ShellLinkDataFlags.RunasUser) == ShellLinkDataFlags.RunasUser;
+            }
+            set
+            {
+                ((IShellLinkDataList)shellLink).GetFlags(out ShellLinkDataFlags flags);
+                if(value) flags |= ShellLinkDataFlags.RunasUser;
+                else flags &= ~ShellLinkDataFlags.RunasUser;
+                ((IShellLinkDataList)shellLink).SetFlags(flags);
+                ((IPersistFile)shellLink).Save(lnkPath, true);
+            }
+        }
+
+        private object GetValue(string name)
+        {
+            try { return shortcutType.InvokeMember(name, GetPropertyFlag, null, shortcut, null); }
+            catch { return null; }
+        }
+
+        private void SetValue(string name, object value)
+        {
+            shortcutType.InvokeMember(name, SetPropertyFlag, null, shortcut, new[] { value });
+        }
+
+        public void Save()
+        {
+            //存储快捷方式为写入文件行为,如果没有权限会报错
+            shortcutType.InvokeMember("Save", InvokeMethodFlag, null, shortcut, null);
+        }
+
+        public void Dispose()
+        {
+            Marshal.ReleaseComObject(shortcut);
+            Marshal.ReleaseComObject(shellLink);
+        }
+
+        ~WshShortcut() { Dispose(); }
+
+
+        [ComImport]
+        [Guid("00021401-0000-0000-C000-000000000046")]
+        internal class ShellLink { }
+
+        [ComImport]
+        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+        [Guid("000214EE-0000-0000-C000-000000000046")]
+        public interface IShellLinkA
+        {
+            void GetPath([Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder pszFile, int cchMaxPath, out IntPtr pfd, int fFlags);
+            void GetIDList(out IntPtr ppidl);
+            void SetIDList(IntPtr pidl);
+            void GetDescription([Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder pszName, int cchMaxName);
+            void SetDescription([MarshalAs(UnmanagedType.LPStr)] string pszName);
+            void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder pszDir, int cchMaxPath);
+            void SetWorkingDirectory([MarshalAs(UnmanagedType.LPStr)] string pszDir);
+            void GetArguments([Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder pszArgs, int cchMaxPath);
+            void SetArguments([MarshalAs(UnmanagedType.LPStr)] string pszArgs);
+            void GetHotkey(out short pwHotkey);
+            void SetHotkey(short wHotkey);
+            void GetShowCmd(out int piShowCmd);
+            void SetShowCmd(int iShowCmd);
+            void GetIconLocation([Out, MarshalAs(UnmanagedType.LPStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);
+            void SetIconLocation([MarshalAs(UnmanagedType.LPStr)] string pszIconPath, int iIcon);
+            void SetRelativePath([MarshalAs(UnmanagedType.LPStr)] string pszPathRel, int dwReserved);
+            void Resolve(IntPtr hwnd, int fFlags);
+            void SetPath([MarshalAs(UnmanagedType.LPStr)] string pszFile);
+        }
+
+        [ComImport]
+        [Guid("45e2b4ae-b1c3-11d0-b92f-00a0c90312e1")]
+        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
+        interface IShellLinkDataList
+        {
+            void AddDataBlock(IntPtr pDataBlock);
+            void CopyDataBlock(uint dwSig, out IntPtr ppDataBlock);
+            void RemoveDataBlock(uint dwSig);
+            void GetFlags(out ShellLinkDataFlags pdwFlags);
+            void SetFlags(ShellLinkDataFlags dwFlags);
+        }
+
+        [Flags] // SHELL_LINK_DATA_FLAGS
+        public enum ShellLinkDataFlags : uint
+        {
+            Default = 0x00000000,
+            HasIdList = 0x00000001,
+            HasLinkInfo = 0x00000002,
+            HasName = 0x00000004,
+            HasRelpath = 0x00000008,
+            HasWorkingdir = 0x00000010,
+            HasArgs = 0x00000020,
+            HasIconLocation = 0x00000040,
+            Unicode = 0x00000080,
+            ForceNoLinkInfo = 0x00000100,
+            HasExpSz = 0x00000200,
+            RunInSeparate = 0x00000400,
+            HasLogo3Id = 0x00000800,
+            HasDarwinId = 0x00001000,
+            RunasUser = 0x00002000,
+            HasExpIconSz = 0x00004000,
+            NoPidlAlias = 0x00008000,
+            ForceUncname = 0x00010000,
+            RunWithShimlayer = 0x00020000,
+            ForceNoLinktrack = 0x00040000,
+            EnableTargetMetadata = 0x00080000,
+            DisableLinkPathTracking = 0x00100000,
+            DisableKnownfolderRelativeTracking = 0x00200000,
+            NoKFAlias = 0x00400000,
+            AllowLinkToLink = 0x00800000,
+            UnaliasOnSave = 0x01000000,
+            PreferEnvironmentPath = 0x02000000,
+            KeepLocalIdListForUncTarget = 0x04000000,
+            Valid = 0x07fff7ff,
+            Reserved = 0x80000000
+        }
+    }
+}

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

@@ -1,56 +0,0 @@
-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;
-        }
-    }
-}

+ 0 - 91
ContextMenuManager/BulePointLilac.Methods/WshShortcut.cs

@@ -1,91 +0,0 @@
-using System;
-using System.Reflection;
-
-namespace BulePointLilac.Methods
-{
-    //为兼容.Net Framework 3.5无法使用dynamic和Interop.IWshRuntimeLibrary.dll专门写出此类
-    sealed class WshShortcut
-    {
-        private static readonly Type ShellType = Type.GetTypeFromProgID("WScript.Shell");
-        private static readonly object Shell = Activator.CreateInstance(ShellType);
-        private static readonly BindingFlags InvokeMethodFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod;
-        private static readonly BindingFlags GetPropertyFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty;
-        private static readonly BindingFlags SetPropertyFlag = BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty;
-
-        private static object CreateShortcut(string lnkPath)
-        {
-            return ShellType.InvokeMember("CreateShortcut", InvokeMethodFlag, null, Shell, new[] { lnkPath });
-        }
-
-        private Type ShortcutType;
-        private object Shortcut;
-
-        private string fullName;
-        public string FullName
-        {
-            get => fullName;
-            set => Load(value);
-        }
-
-        public string TargetPath
-        {
-            get => GetValue("TargetPath").ToString();
-            set => SetValue("TargetPath", value);
-        }
-        public string Arguments
-        {
-            get => GetValue("Arguments").ToString();
-            set => SetValue("Arguments", value);
-        }
-        public string WorkingDirectory
-        {
-            get => GetValue("WorkingDirectory").ToString();
-            set => SetValue("WorkingDirectory", value);
-        }
-        public string IconLocation
-        {
-            get => GetValue("IconLocation").ToString();
-            set => SetValue("IconLocation", value);
-        }
-        public string Description
-        {
-            get => GetValue("Description").ToString();
-            set => SetValue("Description", value);
-        }
-        public string Hotkey
-        {
-            get => GetValue("Hotkey").ToString();
-            set => SetValue("Hotkey", value);
-        }
-        public int WindowStyle
-        {
-            get => Convert.ToInt32(GetValue("WindowStyle"));
-            set => SetValue("WindowStyle", value);
-        }
-        //public string RelativePath { get; set; }//暂时不知道这是啥
-
-        private object GetValue(string name)
-        {
-            return ShortcutType.InvokeMember(name, GetPropertyFlag, null, Shortcut, null);
-        }
-
-        private void SetValue(string name, object value)
-        {
-            ShortcutType.InvokeMember(name, SetPropertyFlag, null, Shortcut, new[] { value });
-        }
-
-        public void Load(string lnkPath)
-        {
-            fullName = lnkPath;
-            Shortcut = CreateShortcut(lnkPath);
-            ShortcutType = Shortcut.GetType();
-            Save();
-        }
-
-        public void Save()
-        {
-            //存储快捷方式为写入文件行为,如果没有权限会报错
-            ShortcutType.InvokeMember("Save", InvokeMethodFlag, null, Shortcut, null);
-        }
-    }
-}

+ 71 - 53
ContextMenuManager/ContextMenuManager.csproj

@@ -110,46 +110,67 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="AppConfig.cs" />
-    <Compile Include="BulePointLilac.Controls\MyToolTip.cs" />
-    <Compile Include="BulePointLilac.Controls\ReadOnlyRichTextBox.cs">
+    <Compile Include="BluePointLilac.Controls\MyToolTip.cs" />
+    <Compile Include="BluePointLilac.Controls\ReadOnlyRichTextBox.cs">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="BulePointLilac.Controls\ResizbleForm.cs">
+    <Compile Include="BluePointLilac.Controls\ResizbleForm.cs">
       <SubType>Form</SubType>
     </Compile>
-    <Compile Include="BulePointLilac.Controls\MyToolBar.cs">
+    <Compile Include="BluePointLilac.Controls\MyToolBar.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="BluePointLilac.Methods\DesktopIni.cs" />
+    <Compile Include="BluePointLilac.Methods\DirectoryEx.cs" />
+    <Compile Include="BluePointLilac.Methods\FileExtension.cs" />
+    <Compile Include="BluePointLilac.Methods\GuidEx.cs" />
+    <Compile Include="BluePointLilac.Methods\IniReader.cs" />
+    <Compile Include="BluePointLilac.Methods\IniWriter.cs" />
+    <Compile Include="BluePointLilac.Methods\MessageBoxEx.cs" />
+    <Compile Include="BluePointLilac.Methods\ExternalProgram.cs" />
+    <Compile Include="BluePointLilac.Methods\RichTextBoxExtension.cs" />
+    <Compile Include="BluePointLilac.Methods\SingleInstance.cs" />
+    <Compile Include="BluePointLilac.Methods\StringExtension.cs" />
+    <Compile Include="BluePointLilac.Methods\TextBoxExtension.cs" />
+    <Compile Include="BluePointLilac.Methods\WindowsOsVersion.cs" />
+    <Compile Include="BluePointLilac.Methods\WshShortcut.cs" />
+    <Compile Include="Controls\AddGuidDicDialog.cs">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="BulePointLilac.Methods\DirectoryEx.cs" />
-    <Compile Include="BulePointLilac.Methods\FileExtension.cs" />
-    <Compile Include="BulePointLilac.Methods\GuidEx.cs" />
-    <Compile Include="BulePointLilac.Methods\IniReader.cs" />
-    <Compile Include="BulePointLilac.Methods\IniWriter.cs" />
-    <Compile Include="BulePointLilac.Methods\MessageBoxEx.cs" />
-    <Compile Include="BulePointLilac.Methods\RichTextBoxExtension.cs" />
-    <Compile Include="BulePointLilac.Methods\StringExtension.cs" />
-    <Compile Include="BulePointLilac.Methods\TextBoxExtension.cs" />
-    <Compile Include="BulePointLilac.Methods\WindowsOsVersion.cs" />
-    <Compile Include="BulePointLilac.Methods\WshShortcut.cs" />
-    <Compile Include="Controls\AddGuidDicDialog.cs">
+    <Compile Include="Controls\EnhanceMenusItem.cs">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="Controls\CommandDialog.cs">
+    <Compile Include="Controls\EnhanceMenusList.cs">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="Controls\EnhanceMenusItem.cs">
+    <Compile Include="Controls\IEItem.cs">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="Controls\EnhanceMenusList.cs">
+    <Compile Include="Controls\IEList.cs">
       <SubType>Component</SubType>
     </Compile>
     <Compile Include="Controls\Interfaces\IBtnMoveUpDownItem.cs">
       <SubType>Component</SubType>
     </Compile>
+    <Compile Include="Controls\Interfaces\ITsiAdministratorItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\Interfaces\ITsiShortcutCommandItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
     <Compile Include="Controls\Interfaces\ITsiRegExportItem.cs">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="Controls\LockNewItem.cs">
+    <Compile Include="Controls\NewIEDialog.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\NewLnkFileDialog.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\SelectDialog.cs">
+      <SubType>Component</SubType>
+    </Compile>
+    <Compile Include="Controls\ShellExecuteDialog.cs">
       <SubType>Component</SubType>
     </Compile>
     <Compile Include="Controls\ShellStoreDialog.cs">
@@ -200,9 +221,6 @@
     <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>
@@ -212,16 +230,13 @@
     <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">
+    <Compile Include="Controls\RuleItem.cs">
       <SubType>Component</SubType>
     </Compile>
     <Compile Include="Controls\OpenWithItem.cs">
@@ -248,6 +263,9 @@
     <Compile Include="Controls\ThirdRulesList.cs">
       <SubType>Component</SubType>
     </Compile>
+    <Compile Include="Controls\WinXGroupItem.cs">
+      <SubType>Component</SubType>
+    </Compile>
     <Compile Include="Controls\WinXItem.cs">
       <SubType>Component</SubType>
     </Compile>
@@ -257,31 +275,31 @@
     <Compile Include="MainForm.cs">
       <SubType>Form</SubType>
     </Compile>
-    <Compile Include="BulePointLilac.Controls\IconDialog.cs">
+    <Compile Include="BluePointLilac.Controls\IconDialog.cs">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="BulePointLilac.Controls\InputDialog.cs">
+    <Compile Include="BluePointLilac.Controls\InputDialog.cs">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="BulePointLilac.Controls\MyCheckBox.cs">
+    <Compile Include="BluePointLilac.Controls\MyCheckBox.cs">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="BulePointLilac.Controls\MyListBox.cs">
+    <Compile Include="BluePointLilac.Controls\MyListBox.cs">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="BulePointLilac.Controls\MyMainForm.cs">
+    <Compile Include="BluePointLilac.Controls\MyMainForm.cs">
       <SubType>Form</SubType>
     </Compile>
-    <Compile Include="BulePointLilac.Controls\MySideBar.cs">
+    <Compile Include="BluePointLilac.Controls\MySideBar.cs">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="BulePointLilac.Controls\MyStatusBar.cs">
+    <Compile Include="BluePointLilac.Controls\MyStatusBar.cs">
       <SubType>Component</SubType>
     </Compile>
     <Compile Include="Controls\ShellSubMenuDialog.cs">
       <SubType>Component</SubType>
     </Compile>
-    <Compile Include="BulePointLilac.Controls\PictureButton.cs">
+    <Compile Include="BluePointLilac.Controls\PictureButton.cs">
       <SubType>Component</SubType>
     </Compile>
     <Compile Include="Controls\ShellExItem.cs">
@@ -290,20 +308,24 @@
     <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="BluePointLilac.Methods\ControlExtension.cs" />
+    <Compile Include="BluePointLilac.Methods\HighDpi.cs" />
+    <Compile Include="BluePointLilac.Methods\RegistryEx.cs" />
+    <Compile Include="BluePointLilac.Methods\RegTrustedInstaller.cs" />
     <Compile Include="AppString.cs" />
     <Compile Include="AppImage.cs" />
-    <Compile Include="BulePointLilac.Methods\EncodingType.cs" />
+    <Compile Include="BluePointLilac.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="BluePointLilac.Methods\ImageExtension.cs" />
+    <Compile Include="BluePointLilac.Methods\ResourceIcon.cs" />
+    <Compile Include="BluePointLilac.Methods\ResourceString.cs" />
+    <Compile Include="BluePointLilac.Methods\ObjectPath.cs" />
     <Compile Include="Program.cs" />
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
     <Compile Include="Updater.cs" />
     <None Include="App.config" />
     <None Include="Properties\App.manifest" />
@@ -314,11 +336,6 @@
       <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>
@@ -339,6 +356,10 @@
   </ItemGroup>
   <ItemGroup>
     <Content Include="Properties\AppIcon.ico" />
+    <Content Include="Properties\Resources\HashLnk\HashLnk_32.exe" />
+    <Content Include="Properties\Resources\HashLnk\HashLnk_64.exe" />
+    <Content Include="Properties\Resources\Images\Custom.png" />
+    <Content Include="Properties\Resources\Images\NewFolder.png" />
     <Content Include="Properties\Resources\Images\Refresh.png" />
     <Content Include="Properties\Resources\Images\Skype.png" />
     <Content Include="Properties\Resources\Texts\EnhanceMenusDic.xml" />
@@ -346,20 +367,17 @@
     <Content Include="Properties\Resources\Images\Add.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" />

+ 68 - 41
ContextMenuManager/Controls/AboutApp.cs

@@ -1,13 +1,10 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.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.Windows.Forms;
 
 namespace ContextMenuManager.Controls
@@ -22,7 +19,7 @@ namespace ContextMenuManager.Controls
             this.BackColor = Color.White;
             this.Font = new Font(SystemFonts.MenuFont.FontFamily, 10F);
             this.Controls.AddRange(new Control[] { lblInfo, picQR, llbDonationList });
-            llbDonationList.LinkClicked += (sender, e) => Process.Start(DonateListUrl);
+            llbDonationList.LinkClicked += (sender, e) => ExternalProgram.OpenUrl(DonateListUrl);
         }
 
         readonly Label lblInfo = new Label
@@ -70,12 +67,12 @@ namespace ContextMenuManager.Controls
                 boxs[i] = new ReadOnlyRichTextBox { Parent = pages[i] };
                 if(i > 0) boxs[i].ContextMenuStrip = cms;
             }
-            items[0].Click += (sender, e) => EditText();
+            items[0].Click += (sender, e) => ExternalProgram.OpenNotepadWithText(GetInitialText());
             items[2].Click += (sender, e) => SaveFile();
             boxs[0].Controls.Add(btnOpenDir);
             btnOpenDir.Top = boxs[0].Height - btnOpenDir.Height;
             MyToolTip.SetToolTip(btnOpenDir, AppString.Tip.OpenDictionariesDir);
-            btnOpenDir.MouseDown += (sender, e) => Process.Start(AppConfig.DicsDir);
+            btnOpenDir.MouseDown += (sender, e) => ExternalProgram.JumpExplorer(AppConfig.DicsDir);
         }
 
         readonly TabPage[] pages = new TabPage[] {
@@ -98,22 +95,6 @@ namespace ContextMenuManager.Controls
             new ToolStripMenuItem(AppString.Menu.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()
-        {
-            using(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())
@@ -140,7 +121,7 @@ namespace ContextMenuManager.Controls
                 dlg.InitialDirectory = dirPath;
                 if(dlg.ShowDialog() == DialogResult.OK)
                 {
-                    File.WriteAllText(dlg.FileName, GetInitialText(), Encoding.UTF8);
+                    File.WriteAllText(dlg.FileName, GetInitialText(), Encoding.Unicode);
                 }
             }
         }
@@ -186,8 +167,8 @@ namespace ContextMenuManager.Controls
             this.Controls.AddRange(new Control[] { cmbLanguages, btnOpenDir, llbOtherLanguages, txtTranslators });
             this.OnResize(null);
             cmbLanguages.SelectionChangeCommitted += (sender, e) => ChangeLanguage();
-            llbOtherLanguages.LinkClicked += (sender, e) => Process.Start(OtherLanguagesUrl);
-            btnOpenDir.MouseDown += (sender, e) => Process.Start(AppConfig.LangsDir);
+            llbOtherLanguages.LinkClicked += (sender, e) => ExternalProgram.OpenUrl(OtherLanguagesUrl);
+            btnOpenDir.MouseDown += (sender, e) => ExternalProgram.JumpExplorer(AppConfig.LangsDir);
             MyToolTip.SetToolTip(btnOpenDir, AppString.Tip.OpenLanguagesDir);
         }
 
@@ -237,7 +218,7 @@ namespace ContextMenuManager.Controls
             if(Directory.Exists(AppConfig.LangsDir))
             {
                 languages.Clear();
-                foreach(string fileName in Directory.GetFiles(AppConfig.LangsDir,"*.ini"))
+                foreach(string fileName in Directory.GetFiles(AppConfig.LangsDir, "*.ini"))
                 {
                     languages.Add(Path.GetFileNameWithoutExtension(fileName));
                     IniReader reader = new IniReader(fileName);
@@ -264,7 +245,7 @@ namespace ContextMenuManager.Controls
             else
             {
                 AppConfig.Language = language;
-                Application.Restart();
+                SingleInstance.Restart();
             }
         }
 
@@ -281,8 +262,8 @@ namespace ContextMenuManager.Controls
 
     sealed class AppSettingBox : MyList
     {
-        private const string GITHUBRELEASES = "https://github.com/BluePointLilac/ContextMenuManager/releases";
-        private const string GITEERELEASES = "https://gitee.com/BluePointLilac/ContextMenuManager/releases";
+        private const string GithubUrl = "https://github.com/BluePointLilac/ContextMenuManager/releases";
+        private const string GiteeUrl = "https://gitee.com/BluePointLilac/ContextMenuManager/releases";
 
         public AppSettingBox()
         {
@@ -292,16 +273,20 @@ namespace ContextMenuManager.Controls
             mliUpdate.AddCtrs(new Control[] { lblGitee, lblGithub, lblUpdate });
             mliProtect.AddCtr(chkProtect);
             mliEngine.AddCtr(cmbEngine);
+            mliWinXSortable.AddCtr(chkWinXSortable);
+            mliShowFilePath.AddCtr(chkShowFilePath);
+            mliOpenMoreRegedit.AddCtr(chkOpenMoreRegedit);
+            MyToolTip.SetToolTip(cmbConfigDir, AppString.Tip.ConfigPath);
             MyToolTip.SetToolTip(btnConfigDir, AppString.Other.OpenConfigDir);
             MyToolTip.SetToolTip(btnBackupDir, AppString.Other.OpenBackupDir);
             MyToolTip.SetToolTip(lblUpdate, AppString.Tip.CheckUpdate + Environment.NewLine
                 + AppString.Tip.LastCheckUpdateTime + AppConfig.LastCheckUpdateTime.ToLongDateString());
-            cmbConfigDir.Items.AddRange(new[] { AppString.Other.SaveToAppData, AppString.Other.SaveToAppDir });
+            cmbConfigDir.Items.AddRange(new[] { AppString.Other.AppDataDir, AppString.Other.AppDir });
             cmbEngine.Items.AddRange(new[] { "Baidu", "Bing", "Google", "DogeDoge", "Sogou", "360", AppString.Other.CustomEngine });
-            btnConfigDir.MouseDown += (sender, e) => Process.Start(AppConfig.ConfigDir);
-            btnBackupDir.MouseDown += (sender, e) => Process.Start(AppConfig.BackupDir);
-            lblGithub.Click += (sender, e) => Process.Start(GITHUBRELEASES);
-            lblGitee.Click += (sender, e) => Process.Start(GITEERELEASES);
+            btnConfigDir.MouseDown += (sender, e) => ExternalProgram.JumpExplorer(AppConfig.ConfigDir);
+            btnBackupDir.MouseDown += (sender, e) => ExternalProgram.JumpExplorer(AppConfig.ConfigDir);
+            lblGithub.Click += (sender, e) => ExternalProgram.OpenUrl(GithubUrl);
+            lblGitee.Click += (sender, e) => ExternalProgram.OpenUrl(GiteeUrl);
             lblUpdate.Click += (sender, e) =>
             {
                 if(!Updater.CheckUpdate()) MessageBoxEx.Show(AppString.MessageBox.NoUpdateDetected);
@@ -318,7 +303,7 @@ namespace ContextMenuManager.Controls
                 {
                     DirectoryEx.CopyTo(AppConfig.ConfigDir, newPath);
                     Directory.Delete(AppConfig.ConfigDir, true);
-                    Application.Restart();
+                    SingleInstance.Restart();
                 }
             };
             cmbEngine.SelectionChangeCommitted += (sender, e) =>
@@ -346,17 +331,32 @@ namespace ContextMenuManager.Controls
             };
             chkBackup.MouseDown += (sender, e) => AppConfig.AutoBackup = chkBackup.Checked = !chkBackup.Checked;
             chkProtect.MouseDown += (sender, e) => AppConfig.ProtectOpenItem = chkProtect.Checked = !chkProtect.Checked;
+            chkWinXSortable.MouseDown += (sender, e) => AppConfig.WinXSortable = chkWinXSortable.Checked = !chkWinXSortable.Checked;
+            chkOpenMoreRegedit.MouseDown += (sender, e) => AppConfig.OpenMoreRegedit = chkOpenMoreRegedit.Checked = !chkOpenMoreRegedit.Checked;
+            chkShowFilePath.MouseDown += (sender, e) =>
+            {
+                chkShowFilePath.Checked = !chkShowFilePath.Checked;
+                if(MessageBoxEx.Show(AppString.MessageBox.RestartApp, MessageBoxButtons.OKCancel) == DialogResult.OK)
+                {
+                    AppConfig.ShowFilePath = chkShowFilePath.Checked;
+                    SingleInstance.Restart();
+                }
+                else
+                {
+                    chkShowFilePath.Checked = !chkShowFilePath.Checked;
+                }
+            };
         }
 
         readonly MyListItem mliConfigDir = new MyListItem
         {
-            Text = AppString.Other.ConfigFile,
+            Text = AppString.Other.ConfigPath,
             HasImage = false
         };
         readonly ComboBox cmbConfigDir = new ComboBox
         {
             DropDownStyle = ComboBoxStyle.DropDownList,
-            Width = 160.DpiZoom()
+            Width = 120.DpiZoom()
         };
         readonly PictureButton btnConfigDir = new PictureButton(AppImage.Open);
 
@@ -410,15 +410,42 @@ namespace ContextMenuManager.Controls
         readonly ComboBox cmbEngine = new ComboBox
         {
             DropDownStyle = ComboBoxStyle.DropDownList,
-            Width = 100.DpiZoom()
+            Width = 120.DpiZoom()
         };
 
+        readonly MyListItem mliWinXSortable = new MyListItem
+        {
+            Text = AppString.Item.WinXSortable,
+            Visible = WindowsOsVersion.ISAfterOrEqual8,
+            HasImage = false
+        };
+        readonly MyCheckBox chkWinXSortable = new MyCheckBox();
+
+        readonly MyListItem mliShowFilePath = new MyListItem
+        {
+            Text = AppString.Item.ShowFilePath,
+            HasImage = false
+        };
+        readonly MyCheckBox chkShowFilePath = new MyCheckBox();
+
+        readonly MyListItem mliOpenMoreRegedit = new MyListItem
+        {
+            Text = AppString.Item.OpenMoreRegedit,
+            HasImage = false
+        };
+        readonly MyCheckBox chkOpenMoreRegedit = new MyCheckBox();
+
         public void LoadItems()
         {
-            this.AddItems(new[] { mliUpdate, mliConfigDir, mliBackup, mliProtect, mliEngine });
+            this.AddItems(new[] { mliUpdate, mliConfigDir, mliBackup, mliProtect,
+                mliEngine, mliWinXSortable, mliShowFilePath, mliOpenMoreRegedit });
             cmbConfigDir.SelectedIndex = AppConfig.SaveToAppDir ? 1 : 0;
             chkBackup.Checked = AppConfig.AutoBackup;
             chkProtect.Checked = AppConfig.ProtectOpenItem;
+            chkWinXSortable.Checked = AppConfig.WinXSortable;
+            chkShowFilePath.Checked = AppConfig.ShowFilePath;
+            chkOpenMoreRegedit.Checked = AppConfig.OpenMoreRegedit;
+
             string url = AppConfig.EngineUrl;
             for(int i = 0; i <= AppConfig.EngineUrls.Length; i++)
             {

+ 2 - 2
ContextMenuManager/Controls/AddGuidDicDialog.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using System;
 using System.Drawing;
 using System.Windows.Forms;

+ 0 - 107
ContextMenuManager/Controls/CommandDialog.cs

@@ -1,107 +0,0 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
-using System;
-using System.Drawing;
-using System.Windows.Forms;
-
-namespace ContextMenuManager.Controls
-{
-    sealed class CommandDialog : CommonDialog
-    {
-        public string Command { get; set; }
-        public string Arguments { get; set; }
-
-        public override void Reset() { }
-
-        protected override bool RunDialog(IntPtr hwndOwner)
-        {
-            using(CommandForm frm = new CommandForm())
-            {
-                frm.Command = this.Command;
-                frm.Arguments = this.Arguments;
-                bool flag = frm.ShowDialog() == DialogResult.OK;
-                if(flag)
-                {
-                    this.Command = frm.Command;
-                    this.Arguments = frm.Arguments;
-                }
-                return flag;
-            }
-        }
-
-        sealed class CommandForm : ResizbleForm
-        {
-            public CommandForm()
-            {
-                this.AcceptButton = btnOk;
-                this.CancelButton = btnCancel;
-                this.VerticalResizable = false;
-                this.Font = SystemFonts.MessageBoxFont;
-                this.Text = AppString.Menu.ChangeCommand;
-                this.SizeGripStyle = SizeGripStyle.Hide;
-                this.StartPosition = FormStartPosition.CenterParent;
-                this.MaximizeBox = MinimizeBox = ShowIcon = ShowInTaskbar = false;
-                InitializeComponents();
-            }
-
-            public string Command
-            {
-                get => txtCommand.Text;
-                set => txtCommand.Text = value;
-            }
-
-            public string Arguments
-            {
-                get => txtArguments.Text;
-                set => txtArguments.Text = value;
-            }
-
-            readonly Label lblCommand = new Label
-            {
-                Text = AppString.Dialog.ItemCommand,
-                AutoSize = true
-            };
-            readonly Label lblArguments = new Label
-            {
-                Text = AppString.Dialog.CommandArguments,
-                AutoSize = true
-            };
-            readonly TextBox txtCommand = new TextBox();
-            readonly TextBox txtArguments = new TextBox();
-            readonly Button btnOk = new Button
-            {
-                DialogResult = DialogResult.OK,
-                Text = AppString.Dialog.Ok,
-                AutoSize = true
-            };
-            readonly Button btnCancel = new Button
-            {
-                DialogResult = DialogResult.Cancel,
-                Text = AppString.Dialog.Cancel,
-                AutoSize = true
-            };
-
-            private void InitializeComponents()
-            {
-                this.Controls.AddRange(new Control[] { lblCommand, lblArguments, txtCommand, txtArguments, btnOk, btnCancel });
-                int a = 20.DpiZoom();
-                lblArguments.Left = lblCommand.Left = lblCommand.Top = txtCommand.Top = a;
-                lblArguments.Top = txtArguments.Top = txtCommand.Bottom + a;
-                btnOk.Top = btnCancel.Top = txtArguments.Bottom + a;
-                int b = Math.Max(lblCommand.Width, lblArguments.Width) + 3 * a;
-                this.ClientSize = new Size(250.DpiZoom() + b, btnOk.Bottom + a);
-                btnOk.Anchor = btnCancel.Anchor = AnchorStyles.Right | AnchorStyles.Top;
-                btnCancel.Left = this.ClientSize.Width - btnCancel.Width - a;
-                btnOk.Left = btnCancel.Left - btnOk.Width - a;
-                this.Resize += (sender, e) =>
-                {
-                    txtArguments.Width = txtCommand.Width = this.ClientSize.Width - b;
-                    txtArguments.Left = txtCommand.Left = btnCancel.Right - txtCommand.Width;
-                };
-                this.OnResize(null);
-                this.MinimumSize = this.Size;
-            }
-        }
-    }
-
-}

+ 58 - 4
ContextMenuManager/Controls/EnhanceMenusItem.cs

@@ -1,10 +1,12 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using ContextMenuManager.Controls.Interfaces;
 using Microsoft.Win32;
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
+using System.Text;
 using System.Xml;
 
 namespace ContextMenuManager.Controls
@@ -63,8 +65,16 @@ namespace ContextMenuManager.Controls
         private static void WriteSubKeysValue(XmlElement keyXE, string regPath)
         {
             if(keyXE == null) return;
-            string defaultValue = keyXE.GetAttribute("Default");
-            if(!defaultValue.IsNullOrWhiteSpace()) Registry.SetValue(regPath, "", defaultValue);
+            string defaultValue = Environment.ExpandEnvironmentVariables(keyXE.GetAttribute("Default"));
+            if(!defaultValue.IsNullOrWhiteSpace())
+            {
+                Registry.SetValue(regPath, "", defaultValue);
+            }
+            else if(keyXE.Name == "Command")
+            {
+                //按照规则Command节点无默认值则创建文件
+                WriteCommandValue(keyXE, regPath);
+            }
             WriteAttributesValue(keyXE.SelectSingleNode("Value"), regPath);
 
             XmlNode subKeyXN = keyXE.SelectSingleNode("SubKey");
@@ -74,6 +84,50 @@ namespace ContextMenuManager.Controls
                     WriteSubKeysValue(xe, $@"{regPath}\{xe.Name}");
             }
         }
+
+        private static void WriteCommandValue(XmlElement cmdXE, string regPath)
+        {
+            XmlElement fnXE = (XmlElement)cmdXE.SelectSingleNode("FileName");
+            XmlElement argXE = (XmlElement)cmdXE.SelectSingleNode("Arguments");
+            XmlElement seXE = (XmlElement)cmdXE.SelectSingleNode("ShellExecute");
+
+            string command;
+            string fileName = fnXE?.InnerText.Trim();
+            string arguments = argXE?.InnerText.Trim();
+            if(string.IsNullOrEmpty(fileName)) fileName = CreateCommandFile(fnXE);
+            if(string.IsNullOrEmpty(arguments)) arguments = CreateCommandFile(argXE);
+            fileName = Environment.ExpandEnvironmentVariables(fileName);
+            arguments = Environment.ExpandEnvironmentVariables(arguments);
+            if(seXE != null)
+            {
+                string verb = seXE.HasAttribute("Verb") ? seXE.GetAttribute("Verb") : "open";
+                int windowStyle = seXE.HasAttribute("WindowStyle") ? Convert.ToInt32(seXE.GetAttribute("WindowStyle")) : 1;
+                string directory = Environment.ExpandEnvironmentVariables(seXE.GetAttribute("Directory"));
+                command = ShellExecuteDialog.GetCommand(fileName, arguments, verb, windowStyle, directory);
+            }
+            else
+            {
+                command = fileName;
+                if(arguments != string.Empty) command += $" {arguments}";
+            }
+            Registry.SetValue(regPath, "", command);
+        }
+
+        private static string CreateCommandFile(XmlElement xe)
+        {
+            if(xe == null) return string.Empty;
+            XmlElement cfXE = (XmlElement)xe.SelectSingleNode("CreateFile");
+            if(cfXE == null) return string.Empty;
+            string fileName = cfXE.GetAttribute("FileName");
+            string content = cfXE.GetAttribute("Content");
+            string extension = Path.GetExtension(fileName).ToLower();
+            string filePath = $@"{AppConfig.ProgramsDir}\{fileName}";
+            Encoding encoding = Encoding.Unicode;
+            if(extension == ".bat" || extension == ".cmd") encoding = Encoding.Default;
+            if(File.Exists(filePath)) File.Delete(filePath);
+            File.WriteAllText(filePath, content, encoding);
+            return filePath;
+        }
     }
 
     sealed class EnhanceShellExItem : MyListItem, IFoldSubItem, IChkVisibleItem

+ 117 - 78
ContextMenuManager/Controls/EnhanceMenusList.cs

@@ -1,10 +1,9 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using ContextMenuManager.Controls.Interfaces;
 using System;
 using System.Drawing;
 using System.IO;
-using System.Text;
 using System.Xml;
 
 namespace ContextMenuManager.Controls
@@ -17,71 +16,9 @@ namespace ContextMenuManager.Controls
             {
                 foreach(XmlNode xn in ReadXml().DocumentElement.ChildNodes)
                 {
-                    string path = null;
-                    string text = null;
-                    Image image = null;
-                    switch(xn.Name)
-                    {
-                        case "File":
-                            path = ShellList.MENUPATH_FILE;
-                            text = AppString.SideBar.File;
-                            image = AppImage.File;
-                            break;
-                        case "Folder":
-                            path = ShellList.MENUPATH_FOLDER;
-                            text = AppString.SideBar.Folder;
-                            image = AppImage.Folder;
-                            break;
-                        case "Directory":
-                            path = ShellList.MENUPATH_FOLDER;
-                            text = AppString.SideBar.Directory;
-                            image = AppImage.Directory;
-                            break;
-                        case "Background":
-                            path = ShellList.MENUPATH_BACKGROUND;
-                            text = AppString.SideBar.Background;
-                            image = AppImage.Background;
-                            break;
-                        case "Desktop":
-                            path = ShellList.MENUPATH_DESKTOP;
-                            //Vista没有桌面右键菜单的独立注册表项
-                            if(WindowsOsVersion.IsEqualVista) path = ShellList.MENUPATH_BACKGROUND;
-                            text = AppString.SideBar.Desktop;
-                            image = AppImage.Desktop;
-                            break;
-                        case "Drive":
-                            path = ShellList.MENUPATH_DRIVE;
-                            text = AppString.SideBar.Drive;
-                            image = AppImage.Drive;
-                            break;
-                        case "AllObjects":
-                            path = ShellList.MENUPATH_ALLOBJECTS;
-                            text = AppString.SideBar.AllObjects;
-                            image = AppImage.AllObjects;
-                            break;
-                        case "Computer":
-                            path = ShellList.MENUPATH_COMPUTER;
-                            text = AppString.SideBar.Computer;
-                            image = AppImage.Computer;
-                            break;
-                        case "RecycleBin":
-                            path = ShellList.MENUPATH_RECYCLEBIN;
-                            text = AppString.SideBar.RecycleBin;
-                            image = AppImage.RecycleBin;
-                            break;
-                        default:
-                            XmlElement xe = (XmlElement)xn;
-                            path = xe.GetAttribute("RegPath");
-                            text = ResourceString.GetDirectString(xe.GetAttribute("Text"));
-                            image = ResourceIcon.GetIcon(xe.GetAttribute("Icon"))?.ToBitmap() ?? AppImage.NotFound;
-                            break;
-                    }
-                    if(string.IsNullOrEmpty(path) || string.IsNullOrEmpty(text)) continue;
-                    GroupPathItem groupItem = new GroupPathItem(path, ObjectPath.PathType.Registry)
-                    {
-                        Image = image,
-                        Text = text,
-                    };
+
+                    GroupPathItem groupItem = GetGroupPathItem(xn);
+                    if(groupItem == null) continue;
                     this.AddItem(groupItem);
                     XmlElement shellXE = (XmlElement)xn.SelectSingleNode("Shell");
                     XmlElement shellExXE = (XmlElement)xn.SelectSingleNode("ShellEx");
@@ -94,21 +31,89 @@ namespace ContextMenuManager.Controls
             catch { }
         }
 
+        private GroupPathItem GetGroupPathItem(XmlNode xn)
+        {
+            string path;
+            string text;
+            Image image;
+            switch(xn.Name)
+            {
+                case "File":
+                    path = ShellList.MENUPATH_FILE;
+                    text = AppString.SideBar.File;
+                    image = AppImage.File;
+                    break;
+                case "Folder":
+                    path = ShellList.MENUPATH_FOLDER;
+                    text = AppString.SideBar.Folder;
+                    image = AppImage.Folder;
+                    break;
+                case "Directory":
+                    path = ShellList.MENUPATH_FOLDER;
+                    text = AppString.SideBar.Directory;
+                    image = AppImage.Directory;
+                    break;
+                case "Background":
+                    path = ShellList.MENUPATH_BACKGROUND;
+                    text = AppString.SideBar.Background;
+                    image = AppImage.Background;
+                    break;
+                case "Desktop":
+                    path = ShellList.MENUPATH_DESKTOP;
+                    //Vista没有桌面右键菜单的独立注册表项
+                    if(WindowsOsVersion.IsEqualVista) path = ShellList.MENUPATH_BACKGROUND;
+                    text = AppString.SideBar.Desktop;
+                    image = AppImage.Desktop;
+                    break;
+                case "Drive":
+                    path = ShellList.MENUPATH_DRIVE;
+                    text = AppString.SideBar.Drive;
+                    image = AppImage.Drive;
+                    break;
+                case "AllObjects":
+                    path = ShellList.MENUPATH_ALLOBJECTS;
+                    text = AppString.SideBar.AllObjects;
+                    image = AppImage.AllObjects;
+                    break;
+                case "Computer":
+                    path = ShellList.MENUPATH_COMPUTER;
+                    text = AppString.SideBar.Computer;
+                    image = AppImage.Computer;
+                    break;
+                case "RecycleBin":
+                    path = ShellList.MENUPATH_RECYCLEBIN;
+                    text = AppString.SideBar.RecycleBin;
+                    image = AppImage.RecycleBin;
+                    break;
+                default:
+                    XmlElement xe = (XmlElement)xn;
+                    path = xe.GetAttribute("RegPath");
+                    text = ResourceString.GetDirectString(xe.GetAttribute("Text"));
+                    if(string.IsNullOrEmpty(path) || string.IsNullOrEmpty(text)) return null;
+                    image = ResourceIcon.GetIcon(xe.GetAttribute("Icon"))?.ToBitmap() ?? AppImage.NotFound;
+                    break;
+            }
+            GroupPathItem groupItem = new GroupPathItem(path, ObjectPath.PathType.Registry) { Image = image, Text = text };
+            return groupItem;
+        }
+
         private XmlDocument ReadXml()
         {
             XmlDocument doc1 = new XmlDocument();
             try
             {
-                //如果没有网络下载的,则将程序内置的写入
-                if(!File.Exists(AppConfig.WebEnhanceMenusDic))
+                if(File.Exists(AppConfig.WebEnhanceMenusDic))
+                {
+                    doc1.LoadXml(File.ReadAllText(AppConfig.WebEnhanceMenusDic, EncodingType.GetType(AppConfig.WebEnhanceMenusDic)));
+                }
+                else
                 {
-                    File.WriteAllText(AppConfig.WebEnhanceMenusDic, Properties.Resources.EnhanceMenusDic, Encoding.UTF8);
+                    doc1.LoadXml(Properties.Resources.EnhanceMenusDic);
                 }
-                doc1.Load(AppConfig.WebEnhanceMenusDic);
                 if(File.Exists(AppConfig.UserEnhanceMenusDic))
                 {
                     XmlDocument doc2 = new XmlDocument();
-                    doc2.Load(AppConfig.UserEnhanceMenusDic);
+                    doc2.LoadXml(File.ReadAllText(AppConfig.UserEnhanceMenusDic, EncodingType.GetType(AppConfig.UserEnhanceMenusDic)));
                     foreach(XmlNode xn in doc2.DocumentElement.ChildNodes)
                     {
                         XmlNode node = doc1.ImportNode(xn, true);
@@ -122,13 +127,13 @@ namespace ContextMenuManager.Controls
 
         private void LoadShellItems(XmlElement shellXE, GroupPathItem groupItem)
         {
-            foreach(XmlElement itemXE in shellXE.GetElementsByTagName("Item"))
+            foreach(XmlElement itemXE in shellXE.SelectNodes("Item"))
             {
                 if(!JudgeOSVersion(itemXE)) continue;
+                if(!FileExists(itemXE)) continue;
                 XmlElement szXE = (XmlElement)itemXE.SelectSingleNode("Value/REG_SZ");
                 string keyName = itemXE.GetAttribute("KeyName");
                 if(keyName.IsNullOrWhiteSpace()) continue;
-                string regPath = $@"{groupItem.TargetPath}\shell\{keyName}";
                 EnhanceShellItem item = new EnhanceShellItem()
                 {
                     RegPath = $@"{groupItem.TargetPath}\shell\{keyName}",
@@ -140,18 +145,40 @@ namespace ContextMenuManager.Controls
                     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;
+                    else
+                    {
+                        XmlElement cmdXE = (XmlElement)itemXE.SelectSingleNode("SubKey/Command");
+                        if(cmdXE != null)
+                        {
+                            Icon icon = null;
+                            if(cmdXE.HasAttribute("Default"))
+                            {
+                                string filePath = ObjectPath.ExtractFilePath(cmdXE.GetAttribute("Default"));
+                                icon = ResourceIcon.GetIcon(filePath);
+                            }
+                            item.Image = icon?.ToBitmap();
+                            icon?.Dispose();
+                        }
+                    }
                 }
                 if(item.Image == null) item.Image = AppImage.NotFound;
                 if(item.Text.IsNullOrWhiteSpace()) item.Text = keyName;
                 item.ChkVisible.Checked = item.ItemVisible;
-                MyToolTip.SetToolTip(item.ChkVisible, itemXE.GetAttribute("Tip"));
+                string tip = itemXE.GetAttribute("Tip");
+                if(itemXE.GetElementsByTagName("CreateFile").Count > 0)
+                {
+                    if(!tip.IsNullOrWhiteSpace()) tip += "\n";
+                    tip += AppString.Tip.CommandFiles;
+                    if(System.Diagnostics.Debugger.IsAttached) item.ChkVisible.Checked = item.ItemVisible = true;
+                }
+                MyToolTip.SetToolTip(item.ChkVisible, tip);
                 this.AddItem(item);
             }
         }
 
         private void LoadShellExItems(XmlElement shellExXE, GroupPathItem groupItem)
         {
-            foreach(XmlElement itemXE in shellExXE.GetElementsByTagName("Item"))
+            foreach(XmlElement itemXE in shellExXE.SelectNodes("Item"))
             {
                 if(!JudgeOSVersion(itemXE)) continue;
                 if(!GuidEx.TryParse(itemXE.GetAttribute("Guid"), out Guid guid)) continue;
@@ -174,6 +201,7 @@ namespace ContextMenuManager.Controls
 
         public static bool JudgeOSVersion(XmlElement itemXE)
         {
+            if(System.Diagnostics.Debugger.IsAttached) return true;//调试状态
             bool JudgeOne(XmlElement osXE)
             {
                 Version ver = new Version(osXE.InnerText);
@@ -197,11 +225,22 @@ namespace ContextMenuManager.Controls
                 }
             }
 
-            foreach(XmlElement osXE in itemXE.GetElementsByTagName("OSVersion"))
+            foreach(XmlElement osXE in itemXE.SelectNodes("OSVersion"))
             {
                 if(!JudgeOne(osXE)) return false;
             }
             return true;
         }
+
+        private static bool FileExists(XmlElement itemXE)
+        {
+            if(System.Diagnostics.Debugger.IsAttached) return true;//调试状态
+            foreach(XmlElement feXE in itemXE.SelectNodes("FileExists"))
+            {
+                string path = Environment.ExpandEnvironmentVariables(feXE.InnerText);
+                if(!File.Exists(path)) return false;
+            }
+            return true;
+        }
     }
 }

+ 6 - 37
ContextMenuManager/Controls/ExplorerRestarter.cs

@@ -1,7 +1,6 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using System;
-using System.Diagnostics;
 using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls
@@ -17,8 +16,8 @@ namespace ContextMenuManager.Controls
             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;
+            BtnRestart.MouseDown += (sender, e) => { ExternalProgram.RestartExplorer(); this.Visible = false; };
+            ShowHandler += (sender, e) => this.Visible = true;
         }
 
         protected override void OnVisibleChanged(EventArgs e)
@@ -29,38 +28,8 @@ namespace ContextMenuManager.Controls
 
         private readonly PictureButton BtnRestart = new PictureButton(AppImage.RestartExplorer);
 
-        private static event EventHandler RestartHandler;
+        private static event EventHandler ShowHandler;
 
-        private static bool needRestart;
-        public static bool NeedRestart
-        {
-            get => needRestart;
-            set
-            {
-                needRestart = value;
-                RestartHandler?.Invoke(null, null);
-            }
-        }
-    }
-
-    public static class Explorer
-    {
-        public static void ReStart()
-        {
-            using(Process process = new Process())
-            {
-                process.StartInfo = new ProcessStartInfo
-                {
-                    FileName = "cmd.exe",
-                    Arguments = "/c taskkill -f -im explorer.exe",
-                    WindowStyle = ProcessWindowStyle.Hidden,
-                    UseShellExecute = true
-                };
-                process.Start();
-                process.WaitForExit();
-                process.StartInfo.Arguments = "/c explorer";
-                process.Start();
-            }
-        }
+        public new static void Show() { ShowHandler?.Invoke(null, null); }
     }
 }

+ 0 - 89
ContextMenuManager/Controls/FileExtensionDialog.cs

@@ -1,89 +0,0 @@
-using BulePointLilac.Methods;
-using System;
-using System.Drawing;
-using System.Windows.Forms;
-
-namespace ContextMenuManager.Controls
-{
-    sealed class FileExtensionDialog : CommonDialog
-    {
-        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;
-            }
-        }
-
-        sealed class FileExtensionForm : Form
-        {
-            public FileExtensionForm()
-            {
-                this.AcceptButton = btnOk;
-                this.CancelButton = btnCancel;
-                this.Text = AppString.Dialog.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) =>
-                {
-                    int index = cmbExtension.Text.IndexOf('.');
-                    if(index >= 0) this.Extension = cmbExtension.Text.Substring(index);
-                    else this.Extension = $".{cmbExtension.Text}";
-                };
-            }
-
-            public string Extension { get; private set; }
-
-            readonly ComboBox cmbExtension = new ComboBox
-            {
-                AutoCompleteMode = AutoCompleteMode.SuggestAppend,
-                AutoCompleteSource = AutoCompleteSource.ListItems,
-                DropDownHeight = 294.DpiZoom(),
-                ImeMode = ImeMode.Disable
-            };
-            readonly Button btnOk = new Button
-            {
-                DialogResult = DialogResult.OK,
-                Text = AppString.Dialog.Ok,
-                AutoSize = true
-            };
-            readonly Button btnCancel = new Button
-            {
-                DialogResult = DialogResult.Cancel,
-                Text = AppString.Dialog.Cancel,
-                AutoSize = true
-            };
-
-            private void InitializeComponents()
-            {
-                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 = a;
-                btnOk.Left = cmbExtension.Right + a;
-                btnCancel.Left = btnOk.Right + a;
-                this.ClientSize = new Size(btnCancel.Right + a, btnCancel.Bottom + a);
-            }
-
-            private void LoadExtensions()
-            {
-                foreach(string extension in Microsoft.Win32.Registry.ClassesRoot.GetSubKeyNames())
-                {
-                    if(!extension.StartsWith(".")) continue;
-                    cmbExtension.Items.Add(extension.Substring(1));
-                }
-            }
-        }
-    }
-}

+ 3 - 3
ContextMenuManager/Controls/GuidBlockedItem.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using ContextMenuManager.Controls.Interfaces;
 using System;
 using System.Windows.Forms;
@@ -63,7 +63,7 @@ namespace ContextMenuManager.Controls
         public void DeleteMe()
         {
             Array.ForEach(BlockedPaths, path => { RegistryEx.DeleteValue(path, this.Value); });
-            if(!this.Guid.Equals(Guid.Empty)) ExplorerRestarter.NeedRestart = true;
+            if(!this.Guid.Equals(Guid.Empty)) ExplorerRestarter.Show();
             this.Dispose();
         }
     }

+ 5 - 13
ContextMenuManager/Controls/GuidBlockedList.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using Microsoft.Win32;
 using System;
 using System.Collections.Generic;
@@ -32,17 +32,9 @@ namespace ContextMenuManager.Controls
 
         private void AddNewItem()
         {
-            MyListItem newItem = new MyListItem
-            {
-                Text = AppString.Item.AddGuidBlockedItem,
-                Image = AppImage.AddNewItem
-            };
-            PictureButton btnAddNewItem = new PictureButton(AppImage.AddNewItem);
-            newItem.AddCtr(btnAddNewItem);
-            newItem.SetNoClickEvent();
+            NewItem newItem = new NewItem(AppString.Item.AddGuidBlockedItem);
             this.AddItem(newItem);
-            MyToolTip.SetToolTip(btnAddNewItem, newItem.Text);
-            btnAddNewItem.MouseDown += (sender, e) =>
+            newItem.AddNewItem += (sender, e) =>
             {
                 using(InputDialog dlg = new InputDialog { Title = AppString.Dialog.InputGuid })
                 {
@@ -63,7 +55,7 @@ namespace ContextMenuManager.Controls
                             }
                         }
                         this.InsertItem(new GuidBlockedItem(dlg.Text), 1);
-                        ExplorerRestarter.NeedRestart = true;
+                        ExplorerRestarter.Show();
                     }
                     else MessageBoxEx.Show(AppString.MessageBox.MalformedGuid);
                 }

+ 122 - 0
ContextMenuManager/Controls/IEItem.cs

@@ -0,0 +1,122 @@
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
+using ContextMenuManager.Controls.Interfaces;
+using Microsoft.Win32;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class IEItem : MyListItem, ITsiRegPathItem, ITsiFilePathItem, ITsiRegDeleteItem, ITsiCommandItem,
+        ITsiWebSearchItem, ITsiTextItem, ITsiRegExportItem, IBtnShowMenuItem, IChkVisibleItem
+    {
+        public static readonly string[] MeParts = { "MenuExt", "-MenuExt" };
+
+        public IEItem(string regPath)
+        {
+            InitializeComponents();
+            this.RegPath = regPath;
+        }
+
+        private string regPath;
+        public string RegPath
+        {
+            get => regPath;
+            set
+            {
+                regPath = value;
+                this.Text = this.ItemText;
+                this.Image = this.ItemImage;
+                ChkVisible.Checked = this.ItemVisible;
+            }
+        }
+        public string ValueName => null;
+        private string KeyName => RegistryEx.GetKeyName(RegPath);
+        private string BackupPath => $@"{IEList.IEPath}\{(ItemVisible ? MeParts[1] : MeParts[0])}\{KeyName}";
+        private string MeKeyName => RegistryEx.GetKeyName(RegistryEx.GetParentPath(RegPath));
+
+        public string ItemText
+        {
+            get => RegistryEx.GetKeyName(RegPath);
+            set
+            {
+                string newPath = $@"{RegistryEx.GetParentPath(RegPath)}\{value.Replace("\\", "")}";
+                string defaultValue = Registry.GetValue(newPath, "", null)?.ToString();
+                if(!defaultValue.IsNullOrWhiteSpace())
+                {
+                    MessageBoxEx.Show(AppString.MessageBox.HasBeenAdded);
+                }
+                else
+                {
+                    RegistryEx.MoveTo(RegPath, newPath);
+                    RegPath = newPath;
+                }
+            }
+        }
+
+        public bool ItemVisible
+        {
+            get => MeKeyName.Equals(MeParts[0], StringComparison.OrdinalIgnoreCase);
+            set
+            {
+                RegistryEx.MoveTo(RegPath, BackupPath);
+                RegPath = BackupPath;
+            }
+        }
+
+        public string ItemCommand
+        {
+            get => Registry.GetValue(RegPath, "", null)?.ToString();
+            set
+            {
+                Registry.SetValue(RegPath, "", value);
+                this.Image = this.ItemImage;
+            }
+        }
+
+        public string SearchText => $@"{AppString.SideBar.IEMenu} {Text}";
+        public string ItemFilePath => ObjectPath.ExtractFilePath(ItemCommand);
+        private Icon ItemIcon => ResourceIcon.GetIcon(ItemFilePath) ?? ResourceIcon.GetExtensionIcon(ItemFilePath);
+        private Image ItemImage => ItemIcon?.ToBitmap() ?? AppImage.NotFound;
+
+        public MenuButton BtnShowMenu { get; set; }
+        public VisibleCheckBox ChkVisible { get; set; }
+        public WebSearchMenuItem TsiSearch { get; set; }
+        public ChangeTextMenuItem TsiChangeText { get; set; }
+        public ChangeCommandMenuItem TsiChangeCommand { get; set; }
+        public FileLocationMenuItem TsiFileLocation { get; set; }
+        public FilePropertiesMenuItem TsiFileProperties { get; set; }
+        public RegLocationMenuItem TsiRegLocation { get; set; }
+        public RegExportMenuItem TsiRegExport { 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);
+            TsiFileLocation = new FileLocationMenuItem(this);
+            TsiFileProperties = new FilePropertiesMenuItem(this);
+            TsiRegLocation = new RegLocationMenuItem(this);
+            TsiRegExport = new RegExportMenuItem(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, TsiRegExport});
+        }
+
+        public void DeleteMe()
+        {
+            RegistryEx.DeleteKeyTree(this.RegPath);
+            RegistryEx.DeleteKeyTree(this.BackupPath);
+            this.Dispose();
+        }
+    }
+}

+ 63 - 0
ContextMenuManager/Controls/IEList.cs

@@ -0,0 +1,63 @@
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class IEList : MyList
+    {
+        public const string IEPath = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer";
+
+        public void LoadItems()
+        {
+            AddNewItem();
+            LoadIEItems();
+        }
+
+        private void LoadIEItems()
+        {
+            List<string> names = new List<string>();
+            using(RegistryKey ieKey = RegistryEx.GetRegistryKey(IEPath))
+            {
+                if(ieKey == null) return;
+                foreach(string part in IEItem.MeParts)
+                {
+                    using(RegistryKey meKey = ieKey.OpenSubKey(part))
+                    {
+                        if(meKey == null) continue;
+                        foreach(string keyName in meKey.GetSubKeyNames())
+                        {
+                            if(names.Contains(keyName, StringComparer.OrdinalIgnoreCase)) continue;
+                            using(RegistryKey key = meKey.OpenSubKey(keyName))
+                            {
+                                if(!string.IsNullOrEmpty(key.GetValue("")?.ToString()))
+                                {
+                                    this.AddItem(new IEItem(key.Name));
+                                    names.Add(keyName);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        private void AddNewItem()
+        {
+            NewItem newItem = new NewItem();
+            this.AddItem(newItem);
+            newItem.AddNewItem += (sender, e) =>
+            {
+                using(NewIEDialog dlg = new NewIEDialog())
+                {
+                    if(dlg.ShowDialog() != DialogResult.OK) return;
+                    this.InsertItem(new IEItem(dlg.RegPath), 1);
+                }
+            };
+        }
+    }
+}

+ 2 - 2
ContextMenuManager/Controls/Interfaces/IBtnDeleteItem.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls.Interfaces

+ 1 - 1
ContextMenuManager/Controls/Interfaces/IBtnMoveUpDownItem.cs

@@ -1,4 +1,4 @@
-using BulePointLilac.Controls;
+using BluePointLilac.Controls;
 
 namespace ContextMenuManager.Controls.Interfaces
 {

+ 16 - 3
ContextMenuManager/Controls/Interfaces/IBtnOpenPathItem.cs

@@ -1,5 +1,6 @@
-using BulePointLilac.Controls;
-using static BulePointLilac.Methods.ObjectPath;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
+using static BluePointLilac.Methods.ObjectPath;
 
 namespace ContextMenuManager.Controls.Interfaces
 {
@@ -15,7 +16,19 @@ namespace ContextMenuManager.Controls.Interfaces
         public ObjectPathButton(IBtnOpenPathItem item) : base(AppImage.Open)
         {
             ((MyListItem)item).AddCtr(this);
-            this.MouseDown += (sender, e) => ShowPath(item.TargetPath, item.PathType);
+            this.MouseDown += (sender, e) =>
+            {
+                switch(item.PathType)
+                {
+                    case PathType.File:
+                    case PathType.Directory:
+                        ExternalProgram.JumpExplorer(item.TargetPath);
+                        break;
+                    case PathType.Registry:
+                        ExternalProgram.JumpRegEdit(item.TargetPath, null, AppConfig.OpenMoreRegedit);
+                        break;
+                }
+            };
         }
     }
 }

+ 1 - 1
ContextMenuManager/Controls/Interfaces/IBtnShowMenuItem.cs

@@ -1,4 +1,4 @@
-using BulePointLilac.Controls;
+using BluePointLilac.Controls;
 using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls.Interfaces

+ 1 - 1
ContextMenuManager/Controls/Interfaces/IChkVisibleItem.cs

@@ -1,4 +1,4 @@
-using BulePointLilac.Controls;
+using BluePointLilac.Controls;
 using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls.Interfaces

+ 18 - 5
ContextMenuManager/Controls/Interfaces/IFoldGroupItem.cs

@@ -1,9 +1,10 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
+using System;
 using System.Drawing;
 using System.IO;
 using System.Windows.Forms;
-using static BulePointLilac.Methods.ObjectPath;
+using static BluePointLilac.Methods.ObjectPath;
 
 namespace ContextMenuManager.Controls.Interfaces
 {
@@ -11,6 +12,7 @@ namespace ContextMenuManager.Controls.Interfaces
     {
         FoldButton BtnFold { get; set; }
         bool IsFold { get; set; }
+        string Text { get; set; }
     }
 
     interface IFoldSubItem
@@ -30,10 +32,12 @@ namespace ContextMenuManager.Controls.Interfaces
                 this.BaseImage = ReplaceImage(value);
                 Control list = ((MyListItem)FoldGroup).Parent;
                 if(list == null) return;
+                list.SuspendLayout();
                 foreach(Control ctr in list.Controls)
                 {
                     if(ctr is IFoldSubItem item && item.FoldGroupItem == FoldGroup) ctr.Visible = !value;
                 }
+                list.ResumeLayout();
             }
         }
 
@@ -53,7 +57,7 @@ namespace ContextMenuManager.Controls.Interfaces
         }
     }
 
-    sealed class GroupPathItem : MyListItem, IFoldGroupItem, IBtnOpenPathItem
+    class GroupPathItem : MyListItem, IFoldGroupItem, IBtnOpenPathItem
     {
         public bool IsFold
         {
@@ -70,6 +74,10 @@ namespace ContextMenuManager.Controls.Interfaces
             BtnFold = new FoldButton(this);
             BtnOpenPath = new ObjectPathButton(this);
             this.Font = new Font(base.Font, FontStyle.Bold);
+            if(pathType == PathType.File || pathType == PathType.Directory)
+            {
+                targetPath = Environment.ExpandEnvironmentVariables(targetPath);
+            }
             this.TargetPath = targetPath;
             this.PathType = pathType;
             string tip = null;
@@ -90,7 +98,6 @@ namespace ContextMenuManager.Controls.Interfaces
                     break;
             }
             MyToolTip.SetToolTip(BtnOpenPath, tip);
-            this.SetNoClickEvent();
         }
 
         public void HideWhenNoSubItem()
@@ -102,5 +109,11 @@ namespace ContextMenuManager.Controls.Interfaces
             }
             if(count == 0) this.Visible = false;
         }
+
+        protected override void OnDoubleClick(EventArgs e)
+        {
+            base.OnDoubleClick(e);
+            IsFold = !IsFold;
+        }
     }
 }

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

@@ -0,0 +1,46 @@
+using BluePointLilac.Methods;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls.Interfaces
+{
+    interface ITsiAdministratorItem
+    {
+        ContextMenuStrip ContextMenuStrip { get; set; }
+        RunAsAdministratorItem TsiAdministrator { get; set; }
+        WshShortcut Shortcut { get; }
+    }
+
+    sealed class RunAsAdministratorItem : ToolStripMenuItem
+    {
+        public RunAsAdministratorItem(ITsiAdministratorItem item) : base(AppString.Menu.RunAsAdministrator)
+        {
+            item.ContextMenuStrip.Opening += (sender, e) =>
+            {
+                if(item.Shortcut == null)
+                {
+                    this.Enabled = false;
+                    return;
+                }
+                string filePath = item.Shortcut.TargetPath;
+                string extension = Path.GetExtension(filePath)?.ToLower();
+                switch(extension)
+                {
+                    case ".exe":
+                    case ".bat":
+                    case ".cmd":
+                        this.Enabled = true;
+                        break;
+                    default:
+                        this.Enabled = false;
+                        break;
+                }
+                this.Checked = item.Shortcut.RunAsAdministrator;
+            };
+            this.Click += (sender, e) =>
+            {
+                item.Shortcut.RunAsAdministrator = !this.Checked;
+            };
+        }
+    }
+}

+ 2 - 2
ContextMenuManager/Controls/Interfaces/ITsiCommandItem.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls.Interfaces

+ 13 - 14
ContextMenuManager/Controls/Interfaces/ITsiDeleteItem.cs

@@ -1,4 +1,4 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System;
 using System.IO;
 using System.Windows.Forms;
@@ -13,7 +13,7 @@ namespace ContextMenuManager.Controls.Interfaces
 
     interface ITsiRegDeleteItem : ITsiDeleteItem
     {
-        string ItemText { get; }
+        string Text { get; }
         string RegPath { get; }
     }
 
@@ -23,20 +23,19 @@ namespace ContextMenuManager.Controls.Interfaces
         {
             this.Click += (sender, e) =>
             {
-                if(MessageBoxEx.Show(AppString.MessageBox.ConfirmDeletePermanently,
-                    MessageBoxButtons.YesNo) == DialogResult.Yes)
+                if(item is ITsiRegDeleteItem regItem && AppConfig.AutoBackup)
                 {
-                    if(item is ITsiRegDeleteItem regItem && AppConfig.AutoBackup)
-                    {
-                        string date = DateTime.Today.ToString("yyyy-MM-dd");
-                        string fileName = ObjectPath.RemoveIllegalChars(regItem.ItemText);
-                        string filePath = $@"{AppConfig.BackupDir}\{date}\{fileName}.reg";
-                        filePath = ObjectPath.GetNewPathWithIndex(filePath, ObjectPath.PathType.File);
-                        Directory.CreateDirectory(Path.GetDirectoryName(filePath));
-                        RegistryEx.Export(regItem.RegPath, filePath);
-                    }
-                    item.DeleteMe();
+                    if(MessageBoxEx.Show(AppString.MessageBox.DeleteButCanRestore,
+                     MessageBoxButtons.YesNo) != DialogResult.Yes) return;
+                    string date = DateTime.Today.ToString("yyyy-MM-dd");
+                    string time = DateTime.Now.ToString("HH.mm.ss");
+                    string filePath = $@"{AppConfig.BackupDir}\{date}\{regItem.Text}-{time}.reg";
+                    Directory.CreateDirectory(Path.GetDirectoryName(filePath));
+                    RegistryEx.Export(regItem.RegPath, filePath);
                 }
+                else if(MessageBoxEx.Show(AppString.MessageBox.ConfirmDeletePermanently,
+                     MessageBoxButtons.YesNo) != DialogResult.Yes) return;
+                item.DeleteMe();
             };
         }
     }

+ 5 - 12
ContextMenuManager/Controls/Interfaces/ITsiFilePathItem.cs

@@ -1,7 +1,6 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System.IO;
 using System.Windows.Forms;
-using static BulePointLilac.Methods.ObjectPath;
 
 namespace ContextMenuManager.Controls.Interfaces
 {
@@ -17,15 +16,9 @@ namespace ContextMenuManager.Controls.Interfaces
     {
         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);
-            };
+            item.ContextMenuStrip.Opening += (sender, e) =>
+                this.Visible = File.Exists(item.ItemFilePath) || Directory.Exists(item.ItemFilePath);
+            this.Click += (sender, e) => ExternalProgram.JumpExplorer(item.ItemFilePath);
         }
     }
 
@@ -35,7 +28,7 @@ namespace ContextMenuManager.Controls.Interfaces
         {
             item.ContextMenuStrip.Opening += (sender, e)
                 => this.Visible = File.Exists(item.ItemFilePath) || Directory.Exists(item.ItemFilePath);
-            this.Click += (sender, e) => PropertiesDialog.Show(item.ItemFilePath);
+            this.Click += (sender, e) => ExternalProgram.ShowPropertiesDialog(item.ItemFilePath);
         }
     }
 }

+ 2 - 2
ContextMenuManager/Controls/Interfaces/ITsiIconItem.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using System.Drawing;
 using System.Windows.Forms;
 

+ 1 - 1
ContextMenuManager/Controls/Interfaces/ITsiRegExportItem.cs

@@ -1,4 +1,4 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System;
 using System.IO;
 using System.Windows.Forms;

+ 4 - 5
ContextMenuManager/Controls/Interfaces/ITsiRegPathItem.cs

@@ -1,22 +1,21 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System.Windows.Forms;
-using static BulePointLilac.Methods.ObjectPath;
 
 namespace ContextMenuManager.Controls.Interfaces
 {
     interface ITsiRegPathItem
     {
-        string RegPath { get; set; }
+        string RegPath { get; }
+        string ValueName { get; }
         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);
+            this.Click += (sender, e) => ExternalProgram.JumpRegEdit(item.RegPath, item.ValueName, AppConfig.OpenMoreRegedit);
             item.ContextMenuStrip.Opening += (sender, e) =>
             {
                 using(var key = RegistryEx.GetRegistryKey(item.RegPath))

+ 138 - 0
ContextMenuManager/Controls/Interfaces/ITsiShortcutCommandItem.cs

@@ -0,0 +1,138 @@
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls.Interfaces
+{
+    interface ITsiShortcutCommandItem
+    {
+        WshShortcut Shortcut { get; }
+        ShortcutCommandMenuItem TsiChangeCommand { get; set; }
+        ContextMenuStrip ContextMenuStrip { get; set; }
+    }
+
+    sealed class ShortcutCommandMenuItem : ToolStripMenuItem
+    {
+        public ShortcutCommandMenuItem(ITsiShortcutCommandItem item) : base(AppString.Menu.ChangeCommand)
+        {
+            item.ContextMenuStrip.Opening += (sender, e) =>
+            {
+                this.Visible = !string.IsNullOrEmpty(item.Shortcut?.TargetPath);
+            };
+        }
+
+        public bool ChangeCommand(WshShortcut shortcut)
+        {
+            using(CommandDialog dlg = new CommandDialog())
+            {
+                dlg.Command = shortcut.TargetPath;
+                dlg.Arguments = shortcut.Arguments;
+                if(dlg.ShowDialog() != DialogResult.OK) return false;
+                shortcut.TargetPath = dlg.Command;
+                shortcut.Arguments = dlg.Arguments;
+                shortcut.Save();
+                return true;
+            }
+        }
+
+        sealed class CommandDialog : CommonDialog
+        {
+            public string Command { get; set; }
+            public string Arguments { get; set; }
+
+            public override void Reset() { }
+
+            protected override bool RunDialog(IntPtr hwndOwner)
+            {
+                using(CommandForm frm = new CommandForm())
+                {
+                    frm.Command = this.Command;
+                    frm.Arguments = this.Arguments;
+                    bool flag = frm.ShowDialog() == DialogResult.OK;
+                    if(flag)
+                    {
+                        this.Command = frm.Command;
+                        this.Arguments = frm.Arguments;
+                    }
+                    return flag;
+                }
+            }
+
+            sealed class CommandForm : ResizbleForm
+            {
+                public CommandForm()
+                {
+                    this.AcceptButton = btnOk;
+                    this.CancelButton = btnCancel;
+                    this.VerticalResizable = false;
+                    this.Font = SystemFonts.MessageBoxFont;
+                    this.Text = AppString.Menu.ChangeCommand;
+                    this.SizeGripStyle = SizeGripStyle.Hide;
+                    this.StartPosition = FormStartPosition.CenterParent;
+                    this.MaximizeBox = MinimizeBox = ShowIcon = ShowInTaskbar = false;
+                    InitializeComponents();
+                }
+
+                public string Command
+                {
+                    get => txtCommand.Text;
+                    set => txtCommand.Text = value;
+                }
+
+                public string Arguments
+                {
+                    get => txtArguments.Text;
+                    set => txtArguments.Text = value;
+                }
+
+                readonly Label lblCommand = new Label
+                {
+                    Text = AppString.Dialog.ItemCommand,
+                    AutoSize = true
+                };
+                readonly Label lblArguments = new Label
+                {
+                    Text = AppString.Dialog.CommandArguments,
+                    AutoSize = true
+                };
+                readonly TextBox txtCommand = new TextBox();
+                readonly TextBox txtArguments = new TextBox();
+                readonly Button btnOk = new Button
+                {
+                    DialogResult = DialogResult.OK,
+                    Text = AppString.Dialog.Ok,
+                    AutoSize = true
+                };
+                readonly Button btnCancel = new Button
+                {
+                    DialogResult = DialogResult.Cancel,
+                    Text = AppString.Dialog.Cancel,
+                    AutoSize = true
+                };
+
+                private void InitializeComponents()
+                {
+                    this.Controls.AddRange(new Control[] { lblCommand, lblArguments, txtCommand, txtArguments, btnOk, btnCancel });
+                    int a = 20.DpiZoom();
+                    lblArguments.Left = lblCommand.Left = lblCommand.Top = txtCommand.Top = a;
+                    lblArguments.Top = txtArguments.Top = txtCommand.Bottom + a;
+                    btnOk.Top = btnCancel.Top = txtArguments.Bottom + a;
+                    int b = Math.Max(lblCommand.Width, lblArguments.Width) + 3 * a;
+                    this.ClientSize = new Size(250.DpiZoom() + b, btnOk.Bottom + a);
+                    btnOk.Anchor = btnCancel.Anchor = AnchorStyles.Right | AnchorStyles.Top;
+                    btnCancel.Left = this.ClientSize.Width - btnCancel.Width - a;
+                    btnOk.Left = btnCancel.Left - btnOk.Width - a;
+                    this.Resize += (sender, e) =>
+                    {
+                        txtArguments.Width = txtCommand.Width = this.ClientSize.Width - b;
+                        txtArguments.Left = txtCommand.Left = btnCancel.Right - txtCommand.Width;
+                    };
+                    this.OnResize(null);
+                    this.MinimumSize = this.Size;
+                }
+            }
+        }
+    }
+}

+ 2 - 2
ContextMenuManager/Controls/Interfaces/ITsiTextItem.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls.Interfaces

+ 6 - 9
ContextMenuManager/Controls/Interfaces/ITsiWebSearchItem.cs

@@ -1,4 +1,4 @@
-using System.Diagnostics;
+using BluePointLilac.Methods;
 using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls.Interfaces
@@ -13,14 +13,11 @@ namespace ContextMenuManager.Controls.Interfaces
     {
         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(AppConfig.EngineUrl.Replace("%s", text));
+            this.Click += (sender, e) =>
+            {
+                string url = AppConfig.EngineUrl.Replace("%s", item.SearchText);
+                ExternalProgram.OpenUrl(url);
+            };
         }
     }
 }

+ 0 - 103
ContextMenuManager/Controls/LockNewItem.cs

@@ -1,103 +0,0 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
-using ContextMenuManager.Controls.Interfaces;
-using Microsoft.Win32;
-using System;
-using System.Security.AccessControl;
-using System.Security.Principal;
-using System.Windows.Forms;
-
-namespace ContextMenuManager.Controls
-{
-    sealed class LockNewItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, ITsiWebSearchItem
-    {
-        public LockNewItem(ShellNewList list)
-        {
-            this.Owner = list;
-            this.Image = AppImage.Lock;
-            this.Text = AppString.Item.LockNewMenu;
-            this.SetNoClickEvent();
-            BtnShowMenu = new MenuButton(this);
-            ChkVisible = new VisibleCheckBox(this) { Checked = IsLocked() };
-            MyToolTip.SetToolTip(ChkVisible, AppString.Tip.LockNewMenu);
-            TsiSearch = new WebSearchMenuItem(this);
-            this.ContextMenuStrip = new ContextMenuStrip();
-            this.ContextMenuStrip.Items.Add(TsiSearch);
-        }
-
-        public MenuButton BtnShowMenu { get; set; }
-        public WebSearchMenuItem TsiSearch { get; set; }
-        public VisibleCheckBox ChkVisible { get; set; }
-        public ShellNewList Owner { get; private set; }
-
-        public bool ItemVisible
-        {
-            get => IsLocked();
-            set
-            {
-                if(value) Owner.WriteRegistry();
-                else UnLock();
-                foreach(Control ctr in Owner.Controls)
-                {
-                    if(ctr.GetType() == typeof(ShellNewItem))
-                    {
-                        ShellNewItem item = (ShellNewItem)ctr;
-                        if(item.CanSort)
-                        {
-                            item.BtnMoveDown.Visible = item.BtnMoveUp.Visible = value;
-                        }
-                    }
-                }
-            }
-        }
-
-        public string SearchText => Text;
-
-
-        public static bool IsLocked()
-        {
-            using(RegistryKey key = RegistryEx.GetRegistryKey(ShellNewList.ShellNewPath))
-            {
-                RegistrySecurity rs = key.GetAccessControl();
-                foreach(RegistryAccessRule rar in rs.GetAccessRules(true, true, typeof(NTAccount)))
-                {
-                    if(rar.AccessControlType.ToString().Equals("Deny", StringComparison.OrdinalIgnoreCase))
-                    {
-                        if(rar.IdentityReference.ToString().Equals("Everyone", StringComparison.OrdinalIgnoreCase)) return true;
-                    }
-                }
-            }
-            return false;
-        }
-
-        public static void Lock()
-        {
-            using(RegistryKey key = RegistryEx.GetRegistryKey(ShellNewList.ShellNewPath, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.ChangePermissions))
-            {
-                RegistrySecurity rs = new RegistrySecurity();
-                RegistryAccessRule rar = new RegistryAccessRule("Everyone", RegistryRights.Delete | RegistryRights.WriteKey, AccessControlType.Deny);
-                rs.AddAccessRule(rar);
-                key.SetAccessControl(rs);
-            }
-        }
-
-        public static void UnLock()
-        {
-            using(RegistryKey key = RegistryEx.GetRegistryKey(ShellNewList.ShellNewPath, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.ChangePermissions))
-            {
-                RegistrySecurity rs = key.GetAccessControl();
-                foreach(RegistryAccessRule rar in rs.GetAccessRules(true, true, typeof(NTAccount)))
-                {
-                    if(rar.AccessControlType.ToString().Equals("Deny", StringComparison.OrdinalIgnoreCase))
-                    {
-                        if(rar.IdentityReference.ToString().Equals("Everyone", StringComparison.OrdinalIgnoreCase))
-                        {
-                            rs.RemoveAccessRule(rar);
-                        }
-                    }
-                }
-                key.SetAccessControl(rs);
-            }
-        }
-    }
-}

+ 64 - 0
ContextMenuManager/Controls/NewIEDialog.cs

@@ -0,0 +1,64 @@
+using BluePointLilac.Methods;
+using System;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class NewIEDialog : CommonDialog
+    {
+        public string RegPath { get; private set; }
+        public override void Reset() { }
+
+        protected override bool RunDialog(IntPtr hwndOwner)
+        {
+            using(NewIEForm frm = new NewIEForm())
+            {
+                bool flag = frm.ShowDialog() == DialogResult.OK;
+                if(flag) this.RegPath = frm.RegPath;
+                return flag;
+            }
+        }
+
+        sealed class NewIEForm : NewItemForm
+        {
+            public string RegPath { get; set; }
+
+            protected override void InitializeComponents()
+            {
+                base.InitializeComponents();
+                btnOk.Click += (sender, e) =>
+                {
+                    if(ItemText.IsNullOrWhiteSpace())
+                    {
+                        MessageBoxEx.Show(AppString.MessageBox.TextCannotBeEmpty);
+                        return;
+                    }
+                    if(ItemCommand.IsNullOrWhiteSpace())
+                    {
+                        MessageBoxEx.Show(AppString.MessageBox.CommandCannotBeEmpty);
+                        return;
+                    }
+                    AddNewItem();
+                    DialogResult = DialogResult.OK;
+                };
+
+                btnBrowse.Click += (sender, e) =>
+                {
+                    using(OpenFileDialog dlg = new OpenFileDialog())
+                    {
+                        if(dlg.ShowDialog() != DialogResult.OK) return;
+                        this.ItemFilePath = dlg.FileName;
+                        this.ItemText = Path.GetFileNameWithoutExtension(dlg.FileName);
+                    }
+                };
+            }
+
+            private void AddNewItem()
+            {
+                this.RegPath = $@"{IEList.IEPath}\{IEItem.MeParts[0]}\{ItemText.Replace("\\", "")}";
+                Microsoft.Win32.Registry.SetValue(RegPath, "", ItemCommand);
+            }
+        }
+    }
+}

+ 8 - 5
ContextMenuManager/Controls/NewItem.cs

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

+ 15 - 13
ContextMenuManager/Controls/NewItemForm.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using System;
 using System.Drawing;
 using System.Windows.Forms;
@@ -12,6 +12,7 @@ namespace ContextMenuManager.Controls
         {
             this.AcceptButton = btnOk;
             this.CancelButton = btnCancel;
+            this.Text = AppString.Item.NewItem;
             this.Font = SystemFonts.MenuFont;
             this.MaximizeBox = this.MinimizeBox = false;
             this.ShowIcon = this.ShowInTaskbar = false;
@@ -22,15 +23,16 @@ namespace ContextMenuManager.Controls
         }
 
         public string ItemText { get => txtText.Text; set => txtText.Text = value; }
-        public string Command { get => txtCommand.Text; set => txtCommand.Text = value; }
+        public string ItemFilePath { get => txtFilePath.Text; set => txtFilePath.Text = value; }
         public string Arguments { get => txtArguments.Text; set => txtArguments.Text = value; }
-        public string FullCommand
+        public string ItemCommand
         {
             get
             {
-                if(Arguments.IsNullOrWhiteSpace()) return Command;
-                else if(Command.IsNullOrWhiteSpace()) return Arguments;
-                else return $"\"{Command}\" \"{Arguments}\"";
+                if(Arguments.IsNullOrWhiteSpace()) return ItemFilePath;
+                if(ItemFilePath.IsNullOrWhiteSpace()) return Arguments;
+                if(Arguments.StartsWith("\"") && Arguments.EndsWith("\"")) return $"\"{ItemFilePath}\" {Arguments}";
+                return $"\"{ItemFilePath}\" \"{Arguments}\"";
             }
         }
 
@@ -50,7 +52,7 @@ namespace ContextMenuManager.Controls
             AutoSize = true
         };
         protected readonly TextBox txtText = new TextBox();
-        protected readonly TextBox txtCommand = new TextBox();
+        protected readonly TextBox txtFilePath = new TextBox();
         protected readonly TextBox txtArguments = new TextBox();
         protected readonly Button btnBrowse = new Button
         {
@@ -74,12 +76,12 @@ namespace ContextMenuManager.Controls
         protected virtual void InitializeComponents()
         {
             this.Controls.AddRange(new Control[] { lblText, lblCommand, lblArguments,
-                txtText, txtCommand, txtArguments, btnBrowse, btnOk, btnCancel });
+                txtText, txtFilePath, txtArguments, btnBrowse, btnOk, btnCancel });
             int a = 20.DpiZoom();
             btnBrowse.Anchor = btnOk.Anchor = btnCancel.Anchor = AnchorStyles.Right | AnchorStyles.Top;
             txtText.Top = lblText.Top = lblText.Left = lblCommand.Left = lblArguments.Left = a;
-            btnBrowse.Top = txtCommand.Top = lblCommand.Top = txtText.Bottom + a;
-            lblArguments.Top = txtArguments.Top = txtCommand.Bottom + a;
+            btnBrowse.Top = txtFilePath.Top = lblCommand.Top = txtText.Bottom + a;
+            lblArguments.Top = txtArguments.Top = txtFilePath.Bottom + a;
             btnOk.Top = btnCancel.Top = txtArguments.Bottom + a;
             btnCancel.Left = btnBrowse.Left = this.ClientSize.Width - btnCancel.Width - a;
             btnOk.Left = btnCancel.Left - btnOk.Width - a;
@@ -88,8 +90,8 @@ namespace ContextMenuManager.Controls
             this.MinimumSize = this.Size;
             this.Resize += (sender, e) =>
             {
-                txtText.Width = txtCommand.Width = txtArguments.Width = this.ClientSize.Width - b;
-                txtText.Left = txtCommand.Left = txtArguments.Left = btnBrowse.Left - txtCommand.Width - a;
+                txtText.Width = txtFilePath.Width = txtArguments.Width = this.ClientSize.Width - b;
+                txtText.Left = txtFilePath.Left = txtArguments.Left = btnBrowse.Left - txtFilePath.Width - a;
                 LastSize = this.Size;
             };
             if(LastSize != null) this.Size = LastSize;

+ 52 - 37
ContextMenuManager/Controls/NewSendToDialog.cs → ContextMenuManager/Controls/NewLnkFileDialog.cs

@@ -1,28 +1,37 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System;
 using System.IO;
 using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls
 {
-    sealed class NewSendToDialog : CommonDialog
+    sealed class NewLnkFileDialog : CommonDialog
     {
-        public string FilePath { get; private set; }
+        public string ItemText { get; set; }
+        public string ItemFilePath { get; set; }
+        public string Arguments { get; set; }
+        public string FileFilter { get; set; }
         public override void Reset() { }
 
         protected override bool RunDialog(IntPtr hwndOwner)
         {
-            using(NewSendToForm frm = new NewSendToForm())
+            using(NewLnkForm frm = new NewLnkForm())
             {
+                frm.FileFilter = this.FileFilter;
                 bool flag = frm.ShowDialog() == DialogResult.OK;
-                if(flag) this.FilePath = frm.FilePath;
+                if(flag)
+                {
+                    this.ItemText = frm.ItemText;
+                    this.ItemFilePath = frm.ItemFilePath;
+                    this.Arguments = frm.Arguments;
+                }
                 return flag;
             }
         }
 
-        sealed class NewSendToForm : NewItemForm
+        sealed class NewLnkForm : NewItemForm
         {
-            public string FilePath { get; set; }
+            public string FileFilter { get; set; }
 
             readonly RadioButton rdoFile = new RadioButton
             {
@@ -39,7 +48,6 @@ namespace ContextMenuManager.Controls
             protected override void InitializeComponents()
             {
                 base.InitializeComponents();
-                this.Text = AppString.Dialog.NewSendToItem;
                 this.Controls.AddRange(new Control[] { rdoFile, rdoFolder });
                 rdoFile.Top = rdoFolder.Top = btnOk.Top;
                 rdoFile.Left = lblCommand.Left;
@@ -56,25 +64,34 @@ namespace ContextMenuManager.Controls
                     if(ItemText.IsNullOrWhiteSpace())
                     {
                         MessageBoxEx.Show(AppString.MessageBox.TextCannotBeEmpty);
-                        return;
                     }
-                    if(Command.IsNullOrWhiteSpace())
+                    else if(ItemFilePath.IsNullOrWhiteSpace())
                     {
                         MessageBoxEx.Show(AppString.MessageBox.CommandCannotBeEmpty);
-                        return;
                     }
-                    if(rdoFile.Checked && !ObjectPath.GetFullFilePath(Command, out _))
+                    else if(rdoFile.Checked && !ObjectPath.GetFullFilePath(ItemFilePath, out _))
                     {
                         MessageBoxEx.Show(AppString.MessageBox.FileNotExists);
-                        return;
                     }
-                    if(rdoFolder.Checked && !Directory.Exists(Command))
+                    else if(rdoFolder.Checked && !Directory.Exists(ItemFilePath))
                     {
                         MessageBoxEx.Show(AppString.MessageBox.FolderNotExists);
-                        return;
                     }
-                    AddNewItem();
-                    DialogResult = DialogResult.OK;
+                    else DialogResult = DialogResult.OK;
+                };
+
+                txtFilePath.TextChanged += (sender, e) =>
+                {
+                    if(Path.GetExtension(ItemFilePath).ToLower() == ".lnk")
+                    {
+                        using(WshShortcut shortcut = new WshShortcut(ItemFilePath))
+                        {
+                            if(File.Exists(shortcut.TargetPath))
+                            {
+                                ItemFilePath = shortcut.TargetPath;
+                            }
+                        }
+                    }
                 };
             }
 
@@ -82,10 +99,23 @@ namespace ContextMenuManager.Controls
             {
                 using(OpenFileDialog dlg = new OpenFileDialog())
                 {
-                    dlg.Filter = $"{AppString.Dialog.Program}|*.exe;*.bat;*.cmd;*.vbs;*.vbe;*.jse;*.wsf";
+                    dlg.Filter = this.FileFilter;
+                    //取消获取lnk目标路径,可选中UWP快捷方式
+                    dlg.DereferenceLinks = false;
                     if(dlg.ShowDialog() == DialogResult.OK)
                     {
-                        Command = dlg.FileName;
+                        ItemFilePath = dlg.FileName;
+                        string extension = Path.GetExtension(dlg.FileName).ToLower();
+                        if(extension == ".lnk")
+                        {
+                            using(WshShortcut shortcut = new WshShortcut(dlg.FileName))
+                            {
+                                if(File.Exists(shortcut.TargetPath))
+                                {
+                                    ItemFilePath = shortcut.TargetPath;
+                                }
+                            }
+                        }
                         ItemText = Path.GetFileNameWithoutExtension(dlg.FileName);
                     }
                 }
@@ -95,30 +125,15 @@ namespace ContextMenuManager.Controls
             {
                 using(FolderBrowserDialog dlg = new FolderBrowserDialog())
                 {
-                    if(Directory.Exists(Command)) dlg.SelectedPath = Command;
+                    if(Directory.Exists(ItemFilePath)) dlg.SelectedPath = ItemFilePath;
                     else dlg.SelectedPath = Application.StartupPath;
                     if(dlg.ShowDialog() == DialogResult.OK)
                     {
-                        Command = dlg.SelectedPath;
-                        ItemText = new DirectoryInfo(dlg.SelectedPath).Name;
+                        ItemFilePath = dlg.SelectedPath;
+                        ItemText = Path.GetFileNameWithoutExtension(dlg.SelectedPath);
                     }
                 }
             }
-
-            private void AddNewItem()
-            {
-                FilePath = $@"{SendToList.SendToPath}\{ObjectPath.RemoveIllegalChars(ItemText)}.lnk";
-                FilePath = ObjectPath.GetNewPathWithIndex(FilePath, ObjectPath.PathType.File);
-                WshShortcut shortcut = new WshShortcut
-                {
-                    FullName = FilePath,
-                    TargetPath = Command,
-                    WorkingDirectory = Path.GetDirectoryName(Command),
-                    Arguments = Arguments
-                };
-                shortcut.Save();
-                SendToList.DesktopIniWriter.SetValue("LocalizedFileNames", Path.GetFileName(FilePath), ItemText);
-            }
         }
     }
 }

+ 26 - 16
ContextMenuManager/Controls/NewOpenWithDialog.cs

@@ -1,4 +1,4 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System;
 using System.Diagnostics;
 using System.IO;
@@ -26,12 +26,13 @@ namespace ContextMenuManager.Controls
             public string RegPath { get; private set; }
 
             private string FilePath;
-            private string AppRegPath;
+            private string FileName => Path.GetFileName(FilePath);
+            private string AppRegPath => $@"HKEY_CLASSES_ROOT\Applications\{FileName}";
+            private string CommandPath => $@"{AppRegPath}\shell\open\command";
 
             protected override void InitializeComponents()
             {
                 base.InitializeComponents();
-                this.Text = AppString.Dialog.NewOpenWithItem;
                 btnBrowse.Click += (sender, e) => BrowseFile();
                 btnOk.Click += (sender, e) =>
                 {
@@ -40,17 +41,26 @@ namespace ContextMenuManager.Controls
                         MessageBoxEx.Show(AppString.MessageBox.TextCannotBeEmpty);
                         return;
                     }
-                    if(FullCommand.IsNullOrWhiteSpace())
+                    if(ItemCommand.IsNullOrWhiteSpace())
                     {
                         MessageBoxEx.Show(AppString.MessageBox.CommandCannotBeEmpty);
                         return;
                     }
-                    FilePath = ObjectPath.ExtractFilePath(Command);
-                    AppRegPath = $@"HKEY_CLASSES_ROOT\Applications\{Path.GetFileName(FilePath)}";
-                    if(FilePath == null || RegistryEx.GetRegistryKey(AppRegPath) != null)
+                    FilePath = ObjectPath.ExtractFilePath(base.ItemFilePath);
+                    using(var key = RegistryEx.GetRegistryKey(CommandPath))
                     {
-                        MessageBoxEx.Show(AppString.MessageBox.UnsupportedFilename);
-                        return;
+                        string path = ObjectPath.ExtractFilePath(key?.GetValue("")?.ToString());
+                        string name = Path.GetFileName(path);
+                        if(FilePath != null && FilePath.Equals(path, StringComparison.OrdinalIgnoreCase))
+                        {
+                            MessageBoxEx.Show(AppString.MessageBox.HasBeenAdded);
+                            return;
+                        }
+                        if(FileName == null || FileName.Equals(name, StringComparison.OrdinalIgnoreCase))
+                        {
+                            MessageBoxEx.Show(AppString.MessageBox.UnsupportedFilename);
+                            return;
+                        }
                     }
                     AddNewItem();
                     this.DialogResult = DialogResult.OK;
@@ -64,8 +74,8 @@ namespace ContextMenuManager.Controls
                     dlg.Filter = $"{AppString.Dialog.Program}|*.exe";
                     if(dlg.ShowDialog() == DialogResult.OK)
                     {
-                        Command = dlg.FileName;
-                        Arguments = "%1";
+                        base.ItemFilePath = dlg.FileName;
+                        Arguments = "\"%1\"";
                         ItemText = FileVersionInfo.GetVersionInfo(dlg.FileName).FileDescription;
                     }
                 }
@@ -76,11 +86,11 @@ namespace ContextMenuManager.Controls
                 using(var key = RegistryEx.GetRegistryKey(AppRegPath, true, true))
                 {
                     key.SetValue("FriendlyAppName", ItemText);
-                    using(var cmdKey = key.CreateSubKey(@"shell\open\command", true))
-                    {
-                        cmdKey.SetValue("", FullCommand);
-                        RegPath = cmdKey.Name;
-                    }
+                }
+                using(var cmdKey = RegistryEx.GetRegistryKey(CommandPath, true, true))
+                {
+                    cmdKey.SetValue("", ItemCommand);
+                    RegPath = cmdKey.Name;
                 }
             }
         }

+ 39 - 18
ContextMenuManager/Controls/NewShellDialog.cs

@@ -1,4 +1,4 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using System;
 using System.IO;
 using System.Windows.Forms;
@@ -45,12 +45,11 @@ namespace ContextMenuManager.Controls
                 Text = AppString.Dialog.MultiMenu,
                 AutoSize = true
             };
+            readonly ShellExecuteCheckBox chkSE = new ShellExecuteCheckBox();
 
             static readonly string[] DirScenePaths = {
                 ShellList.MENUPATH_DIRECTORY,
-                ShellList.MENUPATH_DIRECTORY_IMAGE,
-                ShellList.MENUPATH_DIRECTORY_VIDEO,
-                ShellList.MENUPATH_DIRECTORY_AUDIO
+                $@"{ShellList.SYSFILEASSPATH}\Directory."
             };
             static readonly string[] FileObjectsScenePaths = {
                 ShellList.MENUPATH_FILE,
@@ -58,19 +57,19 @@ namespace ContextMenuManager.Controls
                 ShellList.MENUPATH_ALLOBJECTS,
                 ShellList.SYSFILEASSPATH,
                 ShellList.MENUPATH_UNKNOWN,
-                ShellList.MENUPATH_LNKFILE,
-                ShellList.MENUPATH_EXEFILE,
                 ShellList.MENUPATH_UWPLNK
             };
 
             protected override void InitializeComponents()
             {
                 base.InitializeComponents();
-                this.Text = AppString.Dialog.NewShellItem;
-                this.Controls.AddRange(new[] { rdoSingle, rdoMulti });
+                this.Controls.AddRange(new Control[] { rdoSingle, rdoMulti, chkSE });
                 rdoSingle.Top = rdoMulti.Top = btnOk.Top;
                 rdoSingle.Left = lblCommand.Left;
                 rdoMulti.Left = rdoSingle.Right + 20.DpiZoom();
+                chkSE.Top = txtArguments.Top + (txtArguments.Height - chkSE.Height) / 2;
+                this.Resize += (sender, e) => chkSE.Left = txtArguments.Right + 20.DpiZoom();
+                this.OnResize(null);
 
                 rdoMulti.CheckedChanged += (sender, e) =>
                 {
@@ -80,8 +79,8 @@ namespace ContextMenuManager.Controls
                         rdoSingle.Checked = true;
                         return;
                     }
-                    lblCommand.Enabled = txtCommand.Enabled = lblArguments.Enabled 
-                    = txtArguments.Enabled = btnBrowse.Enabled = !rdoMulti.Checked;
+                    lblCommand.Enabled = txtFilePath.Enabled = lblArguments.Enabled
+                    = txtArguments.Enabled = btnBrowse.Enabled = chkSE.Enabled = !rdoMulti.Checked;
                 };
 
                 btnBrowse.Click += (sender, e) => BrowseFile();
@@ -104,19 +103,36 @@ namespace ContextMenuManager.Controls
             {
                 using(OpenFileDialog dlg = new OpenFileDialog())
                 {
-                    dlg.Filter = $"{AppString.Dialog.Program}|*.exe;*.bat;*.cmd;*.pif;*.com";
+                    dlg.Filter = $"{AppString.Dialog.Program}|*.exe;*.bat;*.cmd;*.pif;*.com;*.vbs;*.vbe;*.js;*.jse;*.wsf";
                     if(dlg.ShowDialog() != DialogResult.OK) return;
-                    Command = dlg.FileName;
                     ItemText = Path.GetFileNameWithoutExtension(dlg.FileName);
+                    string extension = Path.GetExtension(dlg.FileName).ToLower();
+                    switch(extension)
+                    {
+                        case ".vbs":
+                        case ".vbe":
+                        case ".js":
+                        case ".jse":
+                        case ".wsf":
+                            ItemFilePath = "wscript.exe";
+                            Arguments = dlg.FileName;
+                            break;
+                        default:
+                            ItemFilePath = dlg.FileName;
+                            break;
+                    }
                     if(Array.FindIndex(DirScenePaths, path
-                       => ScenePath.Equals(path, StringComparison.OrdinalIgnoreCase)) != -1)
+                       => ScenePath.StartsWith(path, StringComparison.OrdinalIgnoreCase)) != -1)
                     {
-                        Arguments = "%V";//自动加目录后缀
+                        if(!Arguments.IsNullOrWhiteSpace()) Arguments += " ";
+                        if(ScenePath != ShellList.MENUPATH_BACKGROUND)
+                            Arguments += "\"%V\"";//自动加目录后缀
                     }
                     else if(Array.FindIndex(FileObjectsScenePaths, path
                        => ScenePath.StartsWith(path, StringComparison.OrdinalIgnoreCase)) != -1)
                     {
-                        Arguments += "%1";//自动加文件对象后缀
+                        if(!Arguments.IsNullOrWhiteSpace()) Arguments += " ";
+                        Arguments += "\"%1\"";//自动加文件对象后缀
                     }
                 }
             }
@@ -126,7 +142,7 @@ namespace ContextMenuManager.Controls
                 using(var shellKey = RegistryEx.GetRegistryKey(ShellPath, true, true))
                 {
                     string keyName = "Item";
-                    NewItemRegPath = ObjectPath.GetNewPathWithIndex($@"{ShellPath}\{keyName}", ObjectPath.PathType.Registry);
+                    NewItemRegPath = ObjectPath.GetNewPathWithIndex($@"{ShellPath}\{keyName}", ObjectPath.PathType.Registry, 0);
                     keyName = RegistryEx.GetKeyName(NewItemRegPath);
 
                     using(var key = shellKey.CreateSubKey(keyName, true))
@@ -136,8 +152,13 @@ namespace ContextMenuManager.Controls
                             key.SetValue("SubCommands", "");
                         else
                         {
-                            if(!FullCommand.IsNullOrWhiteSpace())
-                                key.CreateSubKey("command", true).SetValue("", FullCommand);
+                            if(!ItemCommand.IsNullOrWhiteSpace())
+                            {
+                                string command;
+                                if(!chkSE.Checked) command = ItemCommand;
+                                else command = ShellExecuteDialog.GetCommand(ItemFilePath, Arguments, chkSE.Verb, chkSE.WindowStyle);
+                                key.CreateSubKey("command", true).SetValue("", command);
+                            }
                         }
                     }
                 }

+ 9 - 4
ContextMenuManager/Controls/OpenWithItem.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using ContextMenuManager.Controls.Interfaces;
 using Microsoft.Win32;
 using System;
@@ -33,8 +33,9 @@ namespace ContextMenuManager.Controls
                 ChkVisible.Checked = this.ItemVisible;
             }
         }
-
-        private string AppPath => RegistryEx.GetParentPath(RegistryEx.GetParentPath(RegistryEx.GetParentPath(RegPath)));
+        public string ValueName => null;
+        private string ShellPath => RegistryEx.GetParentPath(RegPath);
+        private string AppPath => RegistryEx.GetParentPath(RegistryEx.GetParentPath(ShellPath));
         private bool NameEquals => RegistryEx.GetKeyName(AppPath).Equals(Path.GetFileName(ItemFilePath), StringComparison.OrdinalIgnoreCase);
         private Icon ItemIcon => Icon.ExtractAssociatedIcon(ItemFilePath);
 
@@ -123,6 +124,10 @@ namespace ContextMenuManager.Controls
         public void DeleteMe()
         {
             RegistryEx.DeleteKeyTree(this.RegPath);
+            using(RegistryKey key = RegistryEx.GetRegistryKey(ShellPath))
+            {
+                if(key.GetSubKeyNames().Length == 0) RegistryEx.DeleteKeyTree(this.AppPath);
+            }
             this.Dispose();
         }
     }

+ 5 - 5
ContextMenuManager/Controls/OpenWithList.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using Microsoft.Win32;
 using System;
 using System.Collections.Generic;
@@ -12,10 +12,10 @@ namespace ContextMenuManager.Controls
     {
         public void LoadItems()
         {
-            this.LoadCommonItems();
+            this.LoadOpenWithItems();
             this.SortItemByText();
             this.AddNewItem();
-            RegRuleItem storeItem = new RegRuleItem(RegRuleItem.UseStoreOpenWith)
+            VisibleRegRuleItem storeItem = new VisibleRegRuleItem(VisibleRegRuleItem.UseStoreOpenWith)
             {
                 //Win8、Win8.1、Win10才有在应用商店中查找应用
                 Visible = WindowsOsVersion.ISAfterOrEqual8
@@ -23,7 +23,7 @@ namespace ContextMenuManager.Controls
             this.InsertItem(storeItem, 1);
         }
 
-        private void LoadCommonItems()
+        private void LoadOpenWithItems()
         {
             using(RegistryKey appKey = Registry.ClassesRoot.OpenSubKey("Applications"))
             {

+ 0 - 230
ContextMenuManager/Controls/RegRuleItem.cs

@@ -1,230 +0,0 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
-using ContextMenuManager.Controls.Interfaces;
-using Microsoft.Win32;
-using System.Drawing;
-using System.Windows.Forms;
-
-namespace ContextMenuManager.Controls
-{
-    sealed class RegRuleItem : MyListItem, IChkVisibleItem, IFoldSubItem, IBtnShowMenuItem, ITsiWebSearchItem
-    {
-        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;
-            BtnShowMenu = new MenuButton(this);
-            ChkVisible = new VisibleCheckBox(this);
-            MyToolTip.SetToolTip(ChkVisible, info.Tip);
-            TsiSearch = new WebSearchMenuItem(this);
-            this.ContextMenuStrip = new ContextMenuStrip();
-            this.ContextMenuStrip.Items.Add(TsiSearch);
-        }
-
-        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 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;
-                    if(data != null)
-                    {
-                        Registry.SetValue(rule.RegPath, rule.ValueName, data, rule.ValueKind);
-                    }
-                    else
-                    {
-                        RegistryEx.DeleteValue(rule.RegPath, rule.ValueName);
-                    }
-                }
-                if(RestartExplorer) ExplorerRestarter.NeedRestart = true;
-            }
-        }
-
-        public IFoldGroupItem FoldGroupItem { get; set; }
-        public WebSearchMenuItem TsiSearch { get; set; }
-        public MenuButton BtnShowMenu { get; set; }
-
-        public string SearchText => Text;
-
-        const string CU_SMWCEA = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced";
-        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 const string SkypeGuidStr = "{776dbc8d-7347-478c-8d71-791e12ef49d8}";
-
-        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.Item.CustomFolder,
-                Image = AppImage.Folder,
-                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.Item.MapNetworkDrive} && {AppString.Item.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.Item.RecycleBinProperties,
-                Image = AppImage.RecycleBin,
-                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.Item.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.Item.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.Item.UseStoreOpenWith,
-                Image = AppImage.MicrosoftStore
-            }
-        };
-
-        public static RuleAndInfo ShareWithSkype = new RuleAndInfo
-        {
-            Rules = new[]
-            {
-                new RegRule(GuidBlockedItem.HKLMBLOCKED, SkypeGuidStr, null, "", RegistryValueKind.String),
-                new RegRule(GuidBlockedItem.HKCUBLOCKED, SkypeGuidStr, null, "", RegistryValueKind.String)
-            },
-            ItemInfo = new ItemInfo
-            {
-                Text = AppString.Item.ShareWithSkype,
-                Tip = AppString.Tip.ShareWithSkype,
-                Image = AppImage.Skype,
-                RestartExplorer = true
-            }
-        };
-    }
-}

+ 498 - 0
ContextMenuManager/Controls/RuleItem.cs

@@ -0,0 +1,498 @@
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
+using ContextMenuManager.Controls.Interfaces;
+using Microsoft.Win32;
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    class RuleItem : MyListItem, IFoldSubItem, IBtnShowMenuItem, ITsiWebSearchItem
+    {
+        public RuleItem(ItemInfo info)
+        {
+            this.Text = info.Text;
+            this.Image = info.Image;
+            this.RestartExplorer = info.RestartExplorer;
+            BtnShowMenu = new MenuButton(this);
+            TsiSearch = new WebSearchMenuItem(this);
+            this.ContextMenuStrip = new ContextMenuStrip();
+            this.ContextMenuStrip.Items.Add(TsiSearch);
+        }
+
+        public IFoldGroupItem FoldGroupItem { get; set; }
+        public WebSearchMenuItem TsiSearch { get; set; }
+        public MenuButton BtnShowMenu { get; set; }
+
+        public bool RestartExplorer { get; set; }
+
+        public string SearchText
+        {
+            get
+            {
+                if(this.FoldGroupItem == null) return this.Text;
+                else return $"{FoldGroupItem.Text} {this.Text}";
+            }
+        }
+    }
+
+    public struct ItemInfo
+    {
+        public string Text { get; set; }
+        public Image Image { get; set; }
+        public string Tip { get; set; }
+        public bool RestartExplorer { get; set; }
+    }
+
+    sealed class VisibleRegRuleItem : RuleItem, IChkVisibleItem, ITsiRegPathItem
+    {
+        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 RuleAndInfo
+        {
+            public RegRule[] Rules { get; set; }
+            public ItemInfo ItemInfo { get; set; }
+        }
+
+        private VisibleRegRuleItem(ItemInfo info) : base(info)
+        {
+            ChkVisible = new VisibleCheckBox(this);
+            MyToolTip.SetToolTip(ChkVisible, info.Tip);
+            TsiRegLocation = new RegLocationMenuItem(this);
+            this.ContextMenuStrip.Items.AddRange(new ToolStripItem[] { new ToolStripSeparator(), TsiRegLocation });
+        }
+
+        public VisibleRegRuleItem(RegRule[] rules, ItemInfo info)
+            : this(info) { this.Rules = rules; }
+
+        public VisibleRegRuleItem(RegRule rule, ItemInfo info)
+            : this(info) { this.Rules = new[] { rule }; }
+
+        public VisibleRegRuleItem(RuleAndInfo ruleAndInfo)
+            : this(ruleAndInfo.Rules, ruleAndInfo.ItemInfo) { }
+
+        private RegRule[] _Rules;
+        public RegRule[] Rules
+        {
+            get => _Rules;
+            set
+            {
+                _Rules = value;
+                ChkVisible.Checked = ItemVisible;
+            }
+        }
+
+        public VisibleCheckBox ChkVisible { get; set; }
+        public RegLocationMenuItem TsiRegLocation { get; set; }
+
+        public bool ItemVisible
+        {
+            get
+            {
+                for(int i = 0; i < Rules.Length; i++)
+                {
+                    RegRule rule = Rules[i];
+                    using(RegistryKey key = RegistryEx.GetRegistryKey(rule.RegPath))
+                    {
+                        string value = key?.GetValue(rule.ValueName)?.ToString().ToLower();
+                        string turnOnValue = rule.TurnOnValue?.ToString().ToLower();
+                        string turnOffValue = rule.TurnOffValue?.ToString().ToLower();
+                        if(value == null || key.GetValueKind(rule.ValueName) != rule.ValueKind)
+                        {
+                            if(i < Rules.Length - 1) continue;
+                        }
+                        if(value == turnOnValue) return true;
+                        if(value == turnOffValue) return false;
+                    }
+                }
+                return true;
+            }
+            set
+            {
+                foreach(RegRule rule in Rules)
+                {
+                    object data = value ? rule.TurnOnValue : rule.TurnOffValue;
+                    if(data != null)
+                    {
+                        Registry.SetValue(rule.RegPath, rule.ValueName, data, rule.ValueKind);
+                    }
+                    else
+                    {
+                        RegistryEx.DeleteValue(rule.RegPath, rule.ValueName);
+                    }
+                }
+                if(RestartExplorer) ExplorerRestarter.Show();
+            }
+        }
+
+        public string RegPath => Rules[0].RegPath;
+        public string ValueName => Rules[0].ValueName;
+
+        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 const string SkypeGuid = "{776dbc8d-7347-478c-8d71-791e12ef49d8}";
+
+        public static RuleAndInfo CustomFolder = new RuleAndInfo
+        {
+            Rules = new[] {
+                new RegRule(LM_SMWCPE, "NoCustomizeThisFolder", null, 1),
+                new RegRule(LM_SMWCPE, "NoCustomizeWebView", null, 1),
+                new RegRule(CU_SMWCPE, "NoCustomizeThisFolder", null, 1),
+                new RegRule(CU_SMWCPE, "NoCustomizeWebView", null, 1)
+            },
+            ItemInfo = new ItemInfo
+            {
+                Text = AppString.Item.CustomFolder,
+                Image = AppImage.Folder,
+                Tip = AppString.Tip.CustomFolder,
+                RestartExplorer = true
+            }
+        };
+
+        public static RuleAndInfo NetworkDrive = new RuleAndInfo
+        {
+            Rules = new[] {
+                new RegRule(LM_SMWCPE, "NoNetConnectDisconnect", null, 1),
+                new RegRule(CU_SMWCPE, "NoNetConnectDisconnect", null, 1)
+            },
+            ItemInfo = new ItemInfo
+            {
+                Text = $"{AppString.Item.MapNetworkDrive} && {AppString.Item.DisconnectNetworkDrive}",
+                Image = AppImage.NetworkDrive,
+                RestartExplorer = true
+            }
+        };
+
+        public static RuleAndInfo RecycleBinProperties = new RuleAndInfo
+        {
+            Rules = new[] {
+                new RegRule(LM_SMWCPE, "NoPropertiesRecycleBin", null, 1),
+                new RegRule(CU_SMWCPE, "NoPropertiesRecycleBin", null, 1)
+            },
+            ItemInfo = new ItemInfo
+            {
+                Text = AppString.Item.RecycleBinProperties,
+                Image = AppImage.RecycleBin,
+                RestartExplorer = true
+            }
+        };
+
+        public static RuleAndInfo SendToDrive = new RuleAndInfo
+        {
+            Rules = new[] {
+                new RegRule(LM_SMWCPE, "NoDrivesInSendToMenu", null, 1),
+                new RegRule(CU_SMWCPE, "NoDrivesInSendToMenu", null, 1)
+            },
+            ItemInfo = new ItemInfo
+            {
+                Text = AppString.Item.RemovableDrive,
+                Image = AppImage.Drive,
+                Tip = AppString.Tip.SendToDrive,
+                RestartExplorer = true
+            }
+        };
+
+        public static RuleAndInfo DeferBuildSendTo = new RuleAndInfo
+        {
+            Rules = new[] {
+                new RegRule(LM_SMWCE, "DelaySendToMenuBuild", null, 1),
+                new RegRule(CU_SMWCE, "DelaySendToMenuBuild", null, 1)
+            },
+            ItemInfo = new ItemInfo
+            {
+                Text = AppString.Item.BuildSendtoMenu,
+                Image = AppImage.SendTo,
+                Tip = AppString.Tip.BuildSendtoMenu
+            }
+        };
+
+        public static RuleAndInfo UseStoreOpenWith = new RuleAndInfo
+        {
+            Rules = new[] {
+                new RegRule(LM_SPMWE, "NoUseStoreOpenWith", null, 1),
+                new RegRule(CU_SPMWE, "NoUseStoreOpenWith", null, 1)
+            },
+            ItemInfo = new ItemInfo
+            {
+                Text = AppString.Item.UseStoreOpenWith,
+                Image = AppImage.MicrosoftStore
+            }
+        };
+
+        public static RuleAndInfo ShareWithSkype = new RuleAndInfo
+        {
+            Rules = new[]
+            {
+                new RegRule(GuidBlockedItem.HKLMBLOCKED, SkypeGuid, null, "", RegistryValueKind.String),
+                new RegRule(GuidBlockedItem.HKCUBLOCKED, SkypeGuid, null, "", RegistryValueKind.String)
+            },
+            ItemInfo = new ItemInfo
+            {
+                Text = AppString.Item.ShareWithSkype,
+                Image = AppImage.Skype,
+                RestartExplorer = true
+            }
+        };
+    }
+
+    sealed class NumberRegRuleItem : RuleItem, ITsiRegPathItem
+    {
+        public struct RegRule
+        {
+            public string RegPath { get; set; }
+            public string ValueName { get; set; }
+            public RegistryValueKind ValueKind { get; set; }
+            public int MaxValue { get; set; }
+            public int MinValue { get; set; }
+            public int DefaultValue { get; set; }
+        }
+
+        readonly NumericUpDown NudValue = new NumericUpDown
+        {
+            Font = new Font(SystemFonts.MenuFont.FontFamily, 12F),
+            TextAlign = HorizontalAlignment.Center,
+            Width = 80.DpiZoom()
+        };
+        public RegLocationMenuItem TsiRegLocation { get; set; }
+
+        public NumberRegRuleItem(RegRule rule, ItemInfo info) : base(info)
+        {
+            this.AddCtr(NudValue);
+            MyToolTip.SetToolTip(NudValue, info.Tip);
+            TsiRegLocation = new RegLocationMenuItem(this);
+            this.ContextMenuStrip.Items.AddRange(new ToolStripItem[] { new ToolStripSeparator(), TsiRegLocation });
+            this.Rule = rule;
+            NudValue.Maximum = rule.MaxValue;
+            NudValue.Minimum = rule.MinValue;
+            NudValue.ValueChanged += (sender, e) =>
+            {
+                if(NudValue.Value == Rule.DefaultValue) NudValue.ForeColor = Color.Red;
+                else NudValue.ForeColor = Color.Black;
+                this.ItemValue = (int)NudValue.Value;
+            };
+            NudValue.Value = ItemValue;
+        }
+
+        public string RegPath => Rule.RegPath;
+        public string ValueName => Rule.ValueName;
+        public RegRule Rule { get; set; }
+
+        public int ItemValue
+        {
+            get
+            {
+                object value = Registry.GetValue(Rule.RegPath, Rule.ValueName, null);
+                if(value == null) return Rule.DefaultValue;
+                int num = Convert.ToInt32(value);
+                if(num > Rule.MaxValue) return Rule.MaxValue;
+                if(num < Rule.MinValue) return Rule.MinValue;
+                else return num;
+            }
+            set
+            {
+                Registry.SetValue(Rule.RegPath, Rule.ValueName, value, Rule.ValueKind);
+            }
+        }
+    }
+
+    sealed class StringRegRuleItem : RuleItem, ITsiRegPathItem
+    {
+        public struct RegRule
+        {
+            public string RegPath { get; set; }
+            public string ValueName { get; set; }
+        }
+
+        readonly Label LblValue = new Label
+        {
+            Font = new Font(SystemFonts.MenuFont.FontFamily, 12F),
+            BorderStyle = BorderStyle.FixedSingle,
+            AutoSize = true
+        };
+
+        public RegLocationMenuItem TsiRegLocation { get; set; }
+
+        public StringRegRuleItem(RegRule rule, ItemInfo info) : base(info)
+        {
+            this.AddCtr(LblValue);
+            MyToolTip.SetToolTip(LblValue, info.Tip);
+            TsiRegLocation = new RegLocationMenuItem(this);
+            this.ContextMenuStrip.Items.AddRange(new ToolStripItem[] { new ToolStripSeparator(), TsiRegLocation });
+            this.Rule = rule;
+            LblValue.Text = ItemValue;
+            LblValue.MouseDown += (sender, e) =>
+            {
+                using(InputDialog dlg = new InputDialog())
+                {
+                    dlg.Title = AppString.Menu.ChangeText;
+                    dlg.Text = ItemValue;
+                    if(dlg.ShowDialog() != DialogResult.OK) return;
+                    ItemValue = LblValue.Text = dlg.Text;
+                }
+            };
+            LblValue.TextChanged += (sender, e) => ItemValue = LblValue.Text;
+        }
+
+        public string RegPath => Rule.RegPath;
+        public string ValueName => Rule.ValueName;
+        public RegRule Rule { get; set; }
+
+        public string ItemValue
+        {
+            get => Registry.GetValue(Rule.RegPath, Rule.ValueName, null)?.ToString();
+            set => Registry.SetValue(Rule.RegPath, Rule.ValueName, value);
+        }
+    }
+
+    sealed class VisbleIniRuleItem : RuleItem, IChkVisibleItem
+    {
+        public struct IniRule
+        {
+            public string IniPath { get; set; }
+            public string Section { get; set; }
+            public string KeyName { get; set; }
+            public string TurnOnValue { get; set; }
+            public string TurnOffValue { get; set; }
+        }
+
+        public VisbleIniRuleItem(IniRule rule, ItemInfo info) : base(info)
+        {
+            this.Rule = rule;
+            this.IniWriter = new IniWriter(rule.IniPath);
+            ChkVisible = new VisibleCheckBox(this) { Checked = ItemVisible };
+            MyToolTip.SetToolTip(ChkVisible, info.Tip);
+        }
+
+        public IniRule Rule { get; set; }
+        public IniWriter IniWriter { get; set; }
+        public VisibleCheckBox ChkVisible { get; set; }
+        public bool ItemVisible
+        {
+            get => IniWriter.GetValue(Rule.Section, Rule.KeyName) == Rule.TurnOnValue;
+            set => IniWriter.SetValue(Rule.Section, Rule.KeyName, value ? Rule.TurnOnValue : Rule.TurnOffValue);
+        }
+    }
+
+    sealed class NumberIniRuleItem : RuleItem
+    {
+        public struct IniRule
+        {
+            public string IniPath { get; set; }
+            public string Section { get; set; }
+            public string KeyName { get; set; }
+            public int MaxValue { get; set; }
+            public int MinValue { get; set; }
+            public int DefaultValue { get; set; }
+        }
+
+        public NumberIniRuleItem(IniRule rule, ItemInfo info) : base(info)
+        {
+            this.AddCtr(NudValue);
+            this.Rule = rule;
+            this.IniWriter = new IniWriter(rule.IniPath);
+            MyToolTip.SetToolTip(NudValue, info.Tip);
+            NudValue.Maximum = rule.MaxValue;
+            NudValue.Minimum = rule.MinValue;
+            NudValue.ValueChanged += (sender, e) =>
+            {
+                if(NudValue.Value == Rule.DefaultValue) NudValue.ForeColor = Color.Red;
+                else NudValue.ForeColor = Color.Black;
+                this.ItemValue = (int)NudValue.Value;
+            };
+            NudValue.Value = ItemValue;
+        }
+
+        public IniRule Rule { get; set; }
+        public IniWriter IniWriter { get; set; }
+
+        readonly NumericUpDown NudValue = new NumericUpDown
+        {
+            Font = new Font(SystemFonts.MenuFont.FontFamily, 12F),
+            TextAlign = HorizontalAlignment.Center,
+            Width = 80.DpiZoom()
+        };
+
+        public int ItemValue
+        {
+            get
+            {
+                string value = IniWriter.GetValue(Rule.Section, Rule.KeyName);
+                if(value.IsNullOrWhiteSpace()) return Rule.DefaultValue;
+                int num = Convert.ToInt32(value);
+                if(num > Rule.MaxValue) return Rule.MaxValue;
+                if(num < Rule.MinValue) return Rule.MinValue;
+                else return num;
+            }
+            set
+            {
+                IniWriter.SetValue(Rule.Section, Rule.KeyName, value.ToString());
+            }
+        }
+    }
+
+    sealed class StringIniRuleItem : RuleItem
+    {
+        public struct IniRule
+        {
+            public string IniPath { get; set; }
+            public string Secation { get; set; }
+            public string KeyName { get; set; }
+        }
+
+
+        readonly Label LblValue = new Label
+        {
+            Font = new Font(SystemFonts.MenuFont.FontFamily, 12F),
+            BorderStyle = BorderStyle.FixedSingle,
+            AutoSize = true
+        };
+
+        public StringIniRuleItem(IniRule rule, ItemInfo info) : base(info)
+        {
+            this.Rule = rule;
+            this.IniWriter = new IniWriter(rule.IniPath);
+            this.AddCtr(LblValue);
+            MyToolTip.SetToolTip(LblValue, info.Tip);
+            LblValue.Text = ItemValue;
+            LblValue.MouseDown += (sender, e) =>
+            {
+                using(InputDialog dlg = new InputDialog())
+                {
+                    dlg.Title = AppString.Menu.ChangeText;
+                    dlg.Text = ItemValue;
+                    if(dlg.ShowDialog() != DialogResult.OK) return;
+                    ItemValue = LblValue.Text = dlg.Text;
+                }
+            };
+            LblValue.TextChanged += (sender, e) => ItemValue = LblValue.Text;
+        }
+
+        public IniRule Rule { get; set; }
+        public IniWriter IniWriter { get; set; }
+
+        public string ItemValue
+        {
+            get => IniWriter.GetValue(Rule.Secation, Rule.KeyName);
+            set => IniWriter.SetValue(Rule.Secation, Rule.KeyName, value);
+        }
+    }
+}

+ 147 - 0
ContextMenuManager/Controls/SelectDialog.cs

@@ -0,0 +1,147 @@
+using BluePointLilac.Methods;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    class SelectDialog : CommonDialog
+    {
+        public string Title { get; set; }
+        public string Selected { get; set; }
+        public int SelectedIndex { get; private set; }
+        public string[] Items { get; set; }
+        public ComboBoxStyle DropDownStyle { get; set; } = ComboBoxStyle.DropDownList;
+
+        public override void Reset() { }
+
+        protected override bool RunDialog(IntPtr hwndOwner)
+        {
+            using(SelectForm frm = new SelectForm())
+            {
+                frm.Text = this.Title;
+                frm.Items = this.Items;
+                frm.Selected = this.Selected;
+                frm.DropDownStyle = this.DropDownStyle;
+                bool flag = frm.ShowDialog() == DialogResult.OK;
+                if(flag)
+                {
+                    this.Selected = frm.Selected;
+                    this.SelectedIndex = frm.SelectedIndex;
+                }
+                return flag;
+            }
+        }
+
+        sealed class SelectForm : Form
+        {
+            public SelectForm()
+            {
+                this.AcceptButton = btnOk;
+                this.CancelButton = btnCancel;
+                this.Font = SystemFonts.MenuFont;
+                this.ShowIcon = this.ShowInTaskbar = false;
+                this.MaximizeBox = this.MinimizeBox = false;
+                this.FormBorderStyle = FormBorderStyle.FixedSingle;
+                this.StartPosition = FormStartPosition.CenterParent;
+                this.InitializeComponents();
+            }
+
+            public string Selected
+            {
+                get => cmbItems.Text;
+                set => cmbItems.Text = value;
+            }
+
+            public string[] Items
+            {
+                get
+                {
+                    string[] value = new string[cmbItems.Items.Count];
+                    cmbItems.Items.CopyTo(value, 0);
+                    return value;
+                }
+                set
+                {
+                    cmbItems.Items.Clear();
+                    cmbItems.Items.AddRange(value);
+                }
+            }
+
+            public ComboBoxStyle DropDownStyle
+            {
+                get => cmbItems.DropDownStyle;
+                set => cmbItems.DropDownStyle = value;
+            }
+
+            public int SelectedIndex => cmbItems.SelectedIndex;
+
+            readonly Button btnOk = new Button
+            {
+                DialogResult = DialogResult.OK,
+                Text = AppString.Dialog.Ok,
+                AutoSize = true
+            };
+            readonly Button btnCancel = new Button
+            {
+                DialogResult = DialogResult.Cancel,
+                Text = AppString.Dialog.Cancel,
+                AutoSize = true
+            };
+            readonly ComboBox cmbItems = new ComboBox
+            {
+                AutoCompleteMode = AutoCompleteMode.SuggestAppend,
+                AutoCompleteSource = AutoCompleteSource.ListItems,
+                DropDownHeight = 294.DpiZoom(),
+                ImeMode = ImeMode.Disable
+            };
+
+            private void InitializeComponents()
+            {
+                this.Controls.AddRange(new Control[] { cmbItems, btnOk, btnCancel });
+                int a = 20.DpiZoom();
+                cmbItems.Left = a;
+                cmbItems.Width = 85.DpiZoom();
+                cmbItems.Top = btnOk.Top = btnCancel.Top = a;
+                btnOk.Left = cmbItems.Right + a;
+                btnCancel.Left = btnOk.Right + a;
+                this.ClientSize = new Size(btnCancel.Right + a, btnCancel.Bottom + a);
+            }
+        }
+    }
+
+    sealed class FileExtensionDialog : SelectDialog
+    {
+        public string Extension
+        {
+            get => Selected;
+            set => Selected = value;
+        }
+
+        public FileExtensionDialog()
+        {
+            this.Title = AppString.Item.SelectExtension;
+            this.DropDownStyle = ComboBoxStyle.DropDown;
+            List<string> items = new List<string>();
+            foreach(string keyName in Microsoft.Win32.Registry.ClassesRoot.GetSubKeyNames())
+            {
+                if(keyName.StartsWith(".")) items.Add(keyName.Substring(1));
+            }
+            this.Items = items.ToArray();
+        }
+
+        protected override bool RunDialog(IntPtr hwndOwner)
+        {
+            bool flag = base.RunDialog(hwndOwner);
+            if(flag)
+            {
+                string extension = ObjectPath.RemoveIllegalChars(this.Extension);
+                int index = extension.LastIndexOf('.');
+                if(index >= 0) this.Extension = extension.Substring(index);
+                else this.Extension = $".{extension}";
+            }
+            return flag;
+        }
+    }
+}

+ 49 - 39
ContextMenuManager/Controls/SendToItem.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using ContextMenuManager.Controls.Interfaces;
 using Microsoft.Win32;
 using System.Drawing;
@@ -8,8 +8,8 @@ using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls
 {
-    sealed class SendToItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, ITsiTextItem,
-        ITsiIconItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiDeleteItem
+    sealed class SendToItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, ITsiTextItem, ITsiAdministratorItem,
+        ITsiIconItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiDeleteItem, ITsiShortcutCommandItem
     {
         public SendToItem(string filePath)
         {
@@ -24,15 +24,14 @@ namespace ContextMenuManager.Controls
             set
             {
                 filePath = value;
-                if(IsShortcut) this.Shortcut.FullName = value;
+                if(IsShortcut) this.Shortcut = new WshShortcut(value);
                 this.Text = this.ItemText;
                 this.Image = this.ItemIcon.ToBitmap();
                 ChkVisible.Checked = this.ItemVisible;
             }
         }
 
-        private WshShortcut Shortcut = new WshShortcut();
-        private string FileName => Path.GetFileName(FilePath);
+        public WshShortcut Shortcut { get; private set; }
         private string FileExtension => Path.GetExtension(FilePath);
         private bool IsShortcut => FileExtension.ToLower() == ".lnk";
         public string SearchText => $"{AppString.SideBar.SendTo} {Text}";
@@ -41,13 +40,25 @@ namespace ContextMenuManager.Controls
         {
             get
             {
-                if(IsShortcut) return Shortcut.TargetPath;
+                string path = null;
+                if(IsShortcut) path = 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();
+                    using(RegistryKey root = Registry.ClassesRoot)
+                    using(RegistryKey extKey = root.OpenSubKey(FileExtension))
+                    {
+                        string guidPath = extKey?.GetValue("")?.ToString();
+                        if(!string.IsNullOrEmpty(guidPath))
+                        {
+                            using(RegistryKey ipsKey = root.OpenSubKey($@"{guidPath}\InProcServer32"))
+                            {
+                                path = ipsKey?.GetValue("")?.ToString();
+                            }
+                        }
+                    }
                 }
+                if(!File.Exists(path) && !Directory.Exists(path)) path = FilePath;
+                return path;
             }
         }
 
@@ -67,17 +78,16 @@ namespace ContextMenuManager.Controls
         {
             get
             {
-                string name = SendToList.DesktopIniReader.GetValue("LocalizedFileNames", FileName);
-                name = ResourceString.GetDirectString(name);
+                string name = DesktopIni.GetLocalizedFileNames(FilePath, true);
                 if(name == string.Empty) name = Path.GetFileNameWithoutExtension(FilePath);
                 if(name == string.Empty) name = FileExtension;
                 return name;
             }
             set
             {
-                SendToList.DesktopIniWriter.SetValue("LocalizedFileNames", FileName, value);
+                DesktopIni.SetLocalizedFileNames(FilePath, value);
                 this.Text = ResourceString.GetDirectString(value);
-                ExplorerRestarter.NeedRestart = true;
+                ExplorerRestarter.Show();
             }
         }
 
@@ -87,12 +97,14 @@ namespace ContextMenuManager.Controls
             {
                 Icon icon = ResourceIcon.GetIcon(IconLocation, out string iconPath, out int iconIndex);
                 IconPath = iconPath; IconIndex = iconIndex;
-                if(icon == null && IsShortcut)
+                if(icon != null) return icon;
+                if(IsShortcut)
                 {
-                    if(File.Exists(Shortcut.TargetPath)) icon = Icon.ExtractAssociatedIcon(Shortcut.TargetPath);
-                    else if(Directory.Exists(Shortcut.TargetPath)) icon = ResourceIcon.GetFolderIcon(Shortcut.TargetPath);
+                    string path = ItemFilePath;
+                    if(File.Exists(path)) icon = ResourceIcon.GetExtensionIcon(path);
+                    else if(Directory.Exists(path)) icon = ResourceIcon.GetFolderIcon(path);
                 }
-                icon = icon ?? ResourceIcon.GetExtensionIcon(FileExtension);
+                else icon = ResourceIcon.GetExtensionIcon(FileExtension);
                 return icon;
             }
         }
@@ -105,7 +117,7 @@ namespace ContextMenuManager.Controls
                 if(IsShortcut)
                 {
                     location = Shortcut.IconLocation;
-                    if(location.StartsWith(",")) location = $"{Shortcut.TargetPath}{location}";
+                    if(location==",0") location = Shortcut.TargetPath;
                 }
                 else
                 {
@@ -128,7 +140,7 @@ namespace ContextMenuManager.Controls
             {
                 if(IsShortcut)
                 {
-                    Shortcut.IconLocation = value;
+                    Shortcut.IconLocation = $"{this.IconPath},{this.IconIndex}";
                     Shortcut.Save();
                 }
                 else
@@ -142,7 +154,7 @@ namespace ContextMenuManager.Controls
                             string regPath = $@"{root.Name}\{guidPath}\DefaultIcon";
                             RegTrustedInstaller.TakeRegTreeOwnerShip(regPath);
                             Registry.SetValue(regPath, "", value);
-                            ExplorerRestarter.NeedRestart = true;
+                            ExplorerRestarter.Show();
                         }
                     }
                 }
@@ -160,8 +172,10 @@ namespace ContextMenuManager.Controls
         public FilePropertiesMenuItem TsiFileProperties { get; set; }
         public FileLocationMenuItem TsiFileLocation { get; set; }
         public DeleteMeMenuItem TsiDeleteMe { get; set; }
+        public ShortcutCommandMenuItem TsiChangeCommand { get; set; }
+        public RunAsAdministratorItem TsiAdministrator { get; set; }
+
         readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details);
-        readonly ToolStripMenuItem TsiChangeCommand = new ToolStripMenuItem(AppString.Menu.ChangeCommand);
 
         private void InitializeComponents()
         {
@@ -169,40 +183,36 @@ namespace ContextMenuManager.Controls
             ChkVisible = new VisibleCheckBox(this);
             TsiChangeText = new ChangeTextMenuItem(this);
             TsiChangeIcon = new ChangeIconMenuItem(this);
+            TsiChangeCommand = new ShortcutCommandMenuItem(this);
+            TsiAdministrator = new RunAsAdministratorItem(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(),
-                TsiChangeIcon, new ToolStripSeparator(), TsiDetails, new ToolStripSeparator(), TsiDeleteMe });
+                TsiChangeIcon, new ToolStripSeparator(), TsiAdministrator, new ToolStripSeparator(),
+                TsiDetails, new ToolStripSeparator(), TsiDeleteMe });
 
             TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(),
                 TsiChangeCommand, TsiFileProperties, TsiFileLocation });
 
             ContextMenuStrip.Opening += (sender, e) => TsiChangeCommand.Visible = IsShortcut;
 
-            TsiChangeCommand.Click += (sender, e) => ChangeCommand();
-
-        }
-
-        private void ChangeCommand()
-        {
-            using(CommandDialog dlg = new CommandDialog())
+            TsiChangeCommand.Click += (sender, e) =>
             {
-                dlg.Command = Shortcut.TargetPath;
-                dlg.Arguments = Shortcut.Arguments;
-                if(dlg.ShowDialog() != DialogResult.OK) return;
-                Shortcut.TargetPath = dlg.Command;
-                Shortcut.Arguments = dlg.Arguments;
-                Shortcut.Save();
-            }
+                if(TsiChangeCommand.ChangeCommand(Shortcut))
+                {
+                    Image = ItemIcon.ToBitmap();
+                }
+            };
         }
 
         public void DeleteMe()
         {
             File.Delete(this.FilePath);
-            SendToList.DesktopIniWriter.DeleteKey("LocalizedFileNames", FileName);
+            DesktopIni.DeleteLocalizedFileNames(FilePath);
+            this.Shortcut.Dispose();
             this.Dispose();
         }
     }

+ 22 - 16
ContextMenuManager/Controls/SendToList.cs

@@ -1,7 +1,6 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using System;
-using System.Diagnostics;
 using System.IO;
 using System.Windows.Forms;
 
@@ -10,23 +9,19 @@ namespace ContextMenuManager.Controls
     sealed class SendToList : MyList
     {
         public static readonly string SendToPath = Environment.ExpandEnvironmentVariables(@"%AppData%\Microsoft\Windows\SendTo");
-        public static readonly string DesktopIniPath = $@"{SendToPath}\desktop.ini";
-        public static IniWriter DesktopIniWriter = new IniWriter(DesktopIniPath);
-        public static IniReader DesktopIniReader;
 
         public void LoadItems()
         {
-            DesktopIniReader = new IniReader(DesktopIniPath);
-            Array.ForEach(new DirectoryInfo(SendToPath).GetFiles(), fi =>
+            Array.ForEach(Directory.GetFiles(SendToPath), path =>
             {
-                if(fi.Name.ToLower() != "desktop.ini")
-                    this.AddItem(new SendToItem(fi.FullName));
+                if(Path.GetFileName(path).ToLower() != "desktop.ini")
+                    this.AddItem(new SendToItem(path));
             });
             this.SortItemByText();
             this.AddNewItem();
             this.AddDirItem();
-            this.AddItem(new RegRuleItem(RegRuleItem.SendToDrive));
-            this.AddItem(new RegRuleItem(RegRuleItem.DeferBuildSendTo));
+            this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.SendToDrive));
+            this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.DeferBuildSendTo));
         }
 
         private void AddNewItem()
@@ -35,10 +30,21 @@ namespace ContextMenuManager.Controls
             this.InsertItem(newItem, 0);
             newItem.AddNewItem += (sender, e) =>
             {
-                using(NewSendToDialog dlg = new NewSendToDialog())
+                using(NewLnkFileDialog dlg = new NewLnkFileDialog())
                 {
-                    if(dlg.ShowDialog() == DialogResult.OK)
-                        this.InsertItem(new SendToItem(dlg.FilePath), 2);
+                    dlg.FileFilter = $"{AppString.Dialog.Program}|*.exe;*.bat;*.cmd;*.vbs;*.vbe;*.js;*.jse;*.wsf";
+                    if(dlg.ShowDialog() != DialogResult.OK) return;
+                    string lnkPath = $@"{SendToPath}\{ObjectPath.RemoveIllegalChars(dlg.ItemText)}.lnk";
+                    lnkPath = ObjectPath.GetNewPathWithIndex(lnkPath, ObjectPath.PathType.File);
+                    using(WshShortcut shortcut = new WshShortcut(lnkPath))
+                    {
+                        shortcut.TargetPath = dlg.ItemFilePath;
+                        shortcut.WorkingDirectory = Path.GetDirectoryName(dlg.ItemFilePath);
+                        shortcut.Arguments = dlg.Arguments;
+                        shortcut.Save();
+                    }
+                    DesktopIni.SetLocalizedFileNames(lnkPath, dlg.ItemText);
+                    this.InsertItem(new SendToItem(lnkPath), 2);
                 }
             };
         }
@@ -52,7 +58,7 @@ namespace ContextMenuManager.Controls
             };
             PictureButton btnPath = new PictureButton(AppImage.Open);
             MyToolTip.SetToolTip(btnPath, AppString.Menu.FileLocation);
-            btnPath.MouseDown += (sender, e) => Process.Start(SendToPath);
+            btnPath.MouseDown += (sender, e) => ExternalProgram.JumpExplorer(SendToPath);
             item.AddCtr(btnPath);
             item.SetNoClickEvent();
             this.InsertItem(item, 1);

+ 37 - 14
ContextMenuManager/Controls/ShellExItem.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using ContextMenuManager.Controls.Interfaces;
 using Microsoft.Win32;
 using System;
@@ -8,15 +8,16 @@ using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls
 {
-    sealed class ShellExItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem,
+    sealed class ShellExItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, IFoldSubItem,
         ITsiWebSearchItem, ITsiFilePathItem, ITsiRegPathItem, ITsiRegDeleteItem, ITsiRegExportItem
     {
-        public static Dictionary<string, Guid> GetPathAndGuids(string shellExPath)
+        public static Dictionary<string, Guid> GetPathAndGuids(string shellExPath, bool isDragDrop = false)
         {
             Dictionary<string, Guid> dic = new Dictionary<string, Guid>();
-            foreach(string cmhPart in CmhParts)
+            string[] parts = isDragDrop ? DdhParts : CmhParts;
+            foreach(string part in parts)
             {
-                using(RegistryKey cmKey = RegistryEx.GetRegistryKey($@"{shellExPath}\{cmhPart}"))
+                using(RegistryKey cmKey = RegistryEx.GetRegistryKey($@"{shellExPath}\{part}"))
                 {
                     if(cmKey == null) continue;
                     foreach(string keyName in cmKey.GetSubKeyNames())
@@ -34,6 +35,7 @@ namespace ContextMenuManager.Controls
             return dic;
         }
 
+        public static readonly string[] DdhParts = { "DragDropHandlers", "-DragDropHandlers" };
         public static readonly string[] CmhParts = { "ContextMenuHandlers", "-ContextMenuHandlers" };
         private const string LnkOpenGuid = "00021401-0000-0000-c000-000000000046";
 
@@ -57,26 +59,39 @@ namespace ContextMenuManager.Controls
             }
         }
 
+        public string ValueName => DefaultValue;
         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 ParentPath => RegistryEx.GetParentPath(RegPath);
+        private string ShellExPath => RegistryEx.GetParentPath(ParentPath);
+        private string ParentKeyName => RegistryEx.GetKeyName(ParentPath);
         private string DefaultValue => Registry.GetValue(RegPath, "", null)?.ToString();
         public string ItemText => GuidInfo.GetText(Guid) ?? ((Guid.ToString("B") == KeyName) ? DefaultValue : KeyName);
-        private string BackupPath => $@"{ShellExPath}\{(ItemVisible ? CmhParts[1] : CmhParts[0])}\{KeyName}";
         private GuidInfo.IconLocation IconLocation => GuidInfo.GetIconLocation(this.Guid);
         private bool IsOpenLnkItem => Guid.ToString() == LnkOpenGuid;
-        private bool TryProtectOpenItem => IsOpenLnkItem && AppConfig.ProtectOpenItem && MessageBoxEx.Show
-            (AppString.MessageBox.PromptIsOpenItem, MessageBoxButtons.YesNo) != DialogResult.Yes;
+        public bool IsDragDropItem => ParentKeyName.EndsWith(DdhParts[0], StringComparison.OrdinalIgnoreCase);
+
+        private string BackupPath
+        {
+            get
+            {
+                string[] parts = IsDragDropItem ? DdhParts : CmhParts;
+                return $@"{ShellExPath}\{(ItemVisible ? parts[1] : parts[0])}\{KeyName}";
+            }
+        }
 
         public bool ItemVisible
         {
-            get => CmhKeyName.Equals(CmhParts[0], StringComparison.OrdinalIgnoreCase);
+            get
+            {
+                string[] parts = IsDragDropItem ? DdhParts : CmhParts;
+                return ParentKeyName.Equals(parts[0], StringComparison.OrdinalIgnoreCase);
+            }
             set
             {
-                if(!value && TryProtectOpenItem) return;
+                if(!value && TryProtectOpenItem()) return;
                 try
                 {
                     RegistryEx.MoveTo(RegPath, BackupPath);
@@ -98,6 +113,7 @@ namespace ContextMenuManager.Controls
         public RegLocationMenuItem TsiRegLocation { get; set; }
         public DeleteMeMenuItem TsiDeleteMe { get; set; }
         public RegExportMenuItem TsiRegExport { get; set; }
+        public IFoldGroupItem FoldGroupItem { get; set; }
 
         readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details);
         readonly ToolStripMenuItem TsiHandleGuid = new ToolStripMenuItem(AppString.Menu.HandleGuid);
@@ -151,7 +167,7 @@ namespace ContextMenuManager.Controls
                     Registry.SetValue(path, this.Guid.ToString("B"), string.Empty);
                 }
             }
-            ExplorerRestarter.NeedRestart = true;
+            ExplorerRestarter.Show();
         }
 
         private void AddGuidDic()
@@ -240,6 +256,13 @@ namespace ContextMenuManager.Controls
             }
         }
 
+        private bool TryProtectOpenItem()
+        {
+            if(!IsOpenLnkItem) return false;
+            if(!AppConfig.ProtectOpenItem) return false;
+            return MessageBoxEx.Show(AppString.MessageBox.PromptIsOpenItem, MessageBoxButtons.YesNo) != DialogResult.Yes;
+        }
+
         public void DeleteMe()
         {
             try

+ 162 - 0
ContextMenuManager/Controls/ShellExecuteDialog.cs

@@ -0,0 +1,162 @@
+using BluePointLilac.Methods;
+using System;
+using System.Drawing;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class ShellExecuteDialog : CommonDialog
+    {
+        public string Verb { get; set; }
+        public int WindowStyle { get; set; }
+        public override void Reset() { }
+
+        protected override bool RunDialog(IntPtr hwndOwner)
+        {
+            using(ShellExecuteForm frm = new ShellExecuteForm())
+            {
+                bool flag = frm.ShowDialog() == DialogResult.OK;
+                if(flag)
+                {
+                    this.Verb = frm.Verb;
+                    this.WindowStyle = frm.WindowStyle;
+                }
+                return flag;
+            }
+        }
+
+        public static string GetCommand(string fileName, string arguments, string verb, int windowStyle, string directory = null)
+        {
+            arguments = arguments.Replace("\"", "\"\"");
+            if(directory == null)
+            {
+                ObjectPath.GetFullFilePath(fileName, out string filePath);
+                directory = Path.GetDirectoryName(filePath);
+            }
+            return "mshta vbscript:createobject(\"shell.application\").shellexecute" +
+                $"(\"{fileName}\",\"{arguments}\",\"{directory}\",\"{verb}\",{windowStyle})(close)";
+        }
+
+        sealed class ShellExecuteForm : Form
+        {
+            private const string ApiInfoUrl = "https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutea";
+            private static readonly string[] Verbs = new[] { "open", "runas", "edit", "print", "find", "explore" };
+            public ShellExecuteForm()
+            {
+                this.Text = "ShellExecute";
+                this.Font = SystemFonts.MenuFont;
+                this.FormBorderStyle = FormBorderStyle.FixedSingle;
+                this.StartPosition = FormStartPosition.CenterParent;
+                this.ShowIcon = ShowInTaskbar = MaximizeBox = MinimizeBox = false;
+                this.HelpButton = true;
+                this.HelpButtonClicked += (sender, e) => ExternalProgram.OpenUrl(ApiInfoUrl);
+                this.InitializeComponents();
+            }
+            public string Verb { get; set; }
+            public int WindowStyle { get; set; }
+
+            readonly RadioButton[] rdoVerbs = new RadioButton[6];
+            readonly GroupBox grpVerb = new GroupBox { Text = "Verb" };
+            readonly Label lblStyle = new Label
+            {
+                Text = "WindowStyle",
+                AutoSize = true
+            };
+            readonly NumericUpDown nudStyle = new NumericUpDown
+            {
+                TextAlign = HorizontalAlignment.Center,
+                Width = 80.DpiZoom(),
+                Maximum = 10,
+                Minimum = 0,
+                Value = 1
+            };
+            readonly Button btnOk = new Button
+            {
+                Text = AppString.Dialog.Ok,
+                DialogResult = DialogResult.OK,
+                AutoSize = true
+            };
+            readonly Button btnCancel = new Button
+            {
+                Text = AppString.Dialog.Cancel,
+                DialogResult = DialogResult.Cancel,
+                AutoSize = true
+            };
+
+            private void InitializeComponents()
+            {
+                this.Controls.AddRange(new Control[] { grpVerb, lblStyle, nudStyle, btnOk, btnCancel });
+                int a = 10.DpiZoom();
+                int b = 2 * a;
+                for(int i = 0; i < 6; i++)
+                {
+                    rdoVerbs[i] = new RadioButton
+                    {
+                        Text = Verbs[i],
+                        AutoSize = true,
+                        Parent = grpVerb,
+                        Location = new Point(a, b + a)
+                    };
+                    if(i > 0) rdoVerbs[i].Left += rdoVerbs[i - 1].Right;
+                }
+                rdoVerbs[0].Checked = true;
+                grpVerb.Width = rdoVerbs[5].Right + a;
+                grpVerb.Height = rdoVerbs[5].Bottom + b;
+                lblStyle.Left = grpVerb.Left = grpVerb.Top = b;
+                btnOk.Top = btnCancel.Top = lblStyle.Top = nudStyle.Top = grpVerb.Bottom + b;
+                nudStyle.Left = lblStyle.Right + b;
+                btnCancel.Left = grpVerb.Right - btnCancel.Width;
+                btnOk.Left = btnCancel.Left - btnOk.Width - b;
+                this.ClientSize = new Size(btnCancel.Right + b, btnCancel.Bottom + b);
+                btnOk.Click += (sender, e) =>
+                {
+                    for(int i = 0; i < 6; i++)
+                    {
+                        if(rdoVerbs[i].Checked)
+                        {
+                            this.Verb = rdoVerbs[i].Text;
+                            break;
+                        }
+                    }
+                    this.WindowStyle = (int)nudStyle.Value;
+                };
+            }
+        }
+    }
+
+    sealed class ShellExecuteCheckBox : CheckBox
+    {
+        public ShellExecuteCheckBox()
+        {
+            this.Text = "ShellExecute";
+            this.AutoSize = true;
+            this.Font = new Font(SystemFonts.DialogFont.FontFamily, 8F);
+        }
+
+        public string Verb { get; set; }
+        public int WindowStyle { get; set; }
+
+        readonly ToolTip ttpInfo = new ToolTip();
+
+        protected override void OnClick(EventArgs e)
+        {
+            if(this.Checked)
+            {
+                this.Checked = false;
+                ttpInfo.RemoveAll();
+            }
+            else
+            {
+                using(ShellExecuteDialog dlg = new ShellExecuteDialog())
+                {
+                    if(dlg.ShowDialog() != DialogResult.OK) return;
+                    this.Verb = dlg.Verb;
+                    this.WindowStyle = dlg.WindowStyle;
+                    this.Checked = true;
+                    ttpInfo.SetToolTip(this, $"Verb = {Verb}\nWindowStyle = {WindowStyle}");
+                }
+            }
+        }
+    }
+}

+ 87 - 22
ContextMenuManager/Controls/ShellItem.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using ContextMenuManager.Controls.Interfaces;
 using Microsoft.Win32;
 using System;
@@ -16,6 +16,8 @@ namespace ContextMenuManager.Controls
         /// <summary>Shell公共引用子菜单注册表项路径</summary>
         public const string CommandStorePath = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\CommandStore\shell";
 
+        private const string OpenInNewWindowPath = @"HKEY_CLASSES_ROOT\Folder\shell\opennewwindow";
+
         /// <summary>系统原有Shell公共子菜单项名</summary>
         public static readonly string[] SysStoreItemNames = { "Windows.aboutWindows", "Windows.AddColumns",
                 "Windows.AddDevice", "Windows.AddMediaServer", "Windows.AddNetworkLocation", "Windows.AddPrinter",
@@ -119,18 +121,28 @@ namespace ContextMenuManager.Controls
             }
         }
 
+        public string ValueName => null;
         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 && AppConfig.ProtectOpenItem && MessageBoxEx.Show(AppString.MessageBox.PromptIsOpenItem,
-                MessageBoxButtons.YesNo) != DialogResult.Yes;
 
         public string ItemFilePath => GuidInfo.GetFilePath(Guid) ?? ObjectPath.ExtractFilePath(ItemCommand);
         private bool HasIcon => !IconLocation.IsNullOrWhiteSpace() || HasLUAShield;
 
+        private bool IsMultiItem
+        {
+            get
+            {
+                object value = Registry.GetValue(RegPath, "SubCommands", null);
+                if(value != null) return true;
+                value = Registry.GetValue(RegPath, "ExtendedSubCommandsKey", null);
+                if(!string.IsNullOrEmpty(value?.ToString())) return true;
+                return false;
+            }
+        }
+
         private bool OnlyInExplorer
         {
             get => Registry.GetValue(RegPath, "OnlyInBrowserWindow", null) != null;
@@ -138,7 +150,7 @@ namespace ContextMenuManager.Controls
             {
                 if(value)
                 {
-                    if(TryProtectOpenItem) return;
+                    if(TryProtectOpenItem()) return;
                     Registry.SetValue(RegPath, "OnlyInBrowserWindow", "");
                 }
                 else RegistryEx.DeleteValue(RegPath, "OnlyInBrowserWindow");
@@ -152,7 +164,7 @@ namespace ContextMenuManager.Controls
             {
                 if(value)
                 {
-                    if(TryProtectOpenItem) return;
+                    if(TryProtectOpenItem()) return;
                     Registry.SetValue(RegPath, "Extended", "");
                 }
                 else RegistryEx.DeleteValue(RegPath, "Extended");
@@ -179,6 +191,16 @@ namespace ContextMenuManager.Controls
             }
         }
 
+        private bool ShowAsDisabledIfHidden
+        {
+            get => Registry.GetValue(RegPath, "ShowAsDisabledIfHidden", null) != null;
+            set
+            {
+                if(value) Registry.SetValue(RegPath, "ShowAsDisabledIfHidden", "");
+                else RegistryEx.DeleteValue(RegPath, "ShowAsDisabledIfHidden");
+            }
+        }
+
         private Positions ItemPosition
         {
             get
@@ -234,24 +256,38 @@ namespace ContextMenuManager.Controls
             {
                 try
                 {
-                    if(value)
+                    void DeleteSomeValues()
                     {
-                        RegistryEx.DeleteValue(RegPath, "CommandFlags");
-                        RegistryEx.DeleteValue(RegPath, "HideBasedOnVelocityId");
                         RegistryEx.DeleteValue(RegPath, "LegacyDisable");
                         RegistryEx.DeleteValue(RegPath, "ProgrammaticAccessOnly");
+                        if(WindowsOsVersion.IsAfterVista && Convert.ToInt32(Registry.GetValue(RegPath, "CommandFlags", 0)) % 16 >= 8)
+                        {
+                            RegistryEx.DeleteValue(RegPath, "CommandFlags");
+                        }
+                    };
+
+                    if(value)
+                    {
+                        RegistryEx.DeleteValue(RegPath, "HideBasedOnVelocityId");
+                        DeleteSomeValues();
                     }
                     else
                     {
-                        if(TryProtectOpenItem) return;
+                        if(TryProtectOpenItem()) return;
                         if(!IsSubItem)
                         {
-                            Registry.SetValue(RegPath, "LegacyDisable", string.Empty);
-                            Registry.SetValue(RegPath, "ProgrammaticAccessOnly", string.Empty);
+                            //当LegaryDisable键值作用于文件夹-"在新窗口中打开"时
+                            //会导致点击任务栏explorer图标和 Win+E 快捷键错误访问
+                            if(!RegPath.StartsWith(OpenInNewWindowPath, StringComparison.OrdinalIgnoreCase))
+                            {
+                                Registry.SetValue(RegPath, "LegacyDisable", "");
+                            }
+                            Registry.SetValue(RegPath, "ProgrammaticAccessOnly", "");
                         }
-                        else if(WindowsOsVersion.IsAfterOrEqualWin10_1703)
+                        if(WindowsOsVersion.IsAfterOrEqualWin10_1703)
                         {
                             Registry.SetValue(RegPath, "HideBasedOnVelocityId", 0x639bc8);
+                            if(ShowAsDisabledIfHidden) DeleteSomeValues();
                         }
                         else
                         {
@@ -307,7 +343,7 @@ namespace ContextMenuManager.Controls
             }
             set
             {
-                if(TryProtectOpenItem) return;
+                if(TryProtectOpenItem()) return;
                 Registry.SetValue(CommandPath, "", value);
                 if(!this.HasIcon) this.Image = this.ItemIcon.ToBitmap().ToTransparent();
             }
@@ -365,7 +401,11 @@ namespace ContextMenuManager.Controls
             get
             {
                 string value = Registry.GetValue(CommandPath, "DelegateExecute", null)?.ToString();
-                GuidEx.TryParse(value, out Guid guid);
+                if(!GuidEx.TryParse(value, out Guid guid))
+                {
+                    value = Registry.GetValue($@"{RegPath}\DropTarget", "CLSID", null)?.ToString();
+                    GuidEx.TryParse(value, out guid);
+                }
                 return guid;
             }
         }
@@ -382,6 +422,7 @@ namespace ContextMenuManager.Controls
         public DeleteMeMenuItem TsiDeleteMe { get; set; }
         public RegExportMenuItem TsiRegExport { get; set; }
 
+        protected readonly PictureButton BtnSubItems = new PictureButton(AppImage.SubItems);
         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);
@@ -395,7 +436,7 @@ namespace ContextMenuManager.Controls
         readonly ToolStripMenuItem TsiNoWorkDir = new ToolStripMenuItem(AppString.Menu.NoWorkingDirectory);
         readonly ToolStripMenuItem TsiNeverDefault = new ToolStripMenuItem(AppString.Menu.NeverDefault);
         readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details);
-        protected readonly PictureButton BtnSubItems = new PictureButton(AppImage.SubItems);
+        readonly ToolStripMenuItem TsiShowAsDisabled = new ToolStripMenuItem(AppString.Menu.ShowAsDisabledIfHidden);
 
         private void InitializeComponents()
         {
@@ -418,7 +459,8 @@ namespace ContextMenuManager.Controls
 
             TsiPosition.DropDownItems.AddRange(new ToolStripItem[] { TsiDefault, TsiSetTop, TsiSetBottom });
 
-            TsiOtherAttributes.DropDownItems.AddRange(new ToolStripItem[] { TsiOnlyWithShift, TsiOnlyInExplorer, TsiNoWorkDir, TsiNeverDefault });
+            TsiOtherAttributes.DropDownItems.AddRange(new ToolStripItem[] { TsiOnlyWithShift, TsiOnlyInExplorer,
+                TsiNoWorkDir, TsiNeverDefault, TsiShowAsDisabled });
 
             TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(),
                 TsiChangeCommand, TsiFileProperties, TsiFileLocation, TsiRegLocation, TsiRegExport});
@@ -431,6 +473,7 @@ namespace ContextMenuManager.Controls
             TsiOnlyWithShift.Click += (sender, e) => this.OnlyWithShift = !TsiOnlyWithShift.Checked;
             TsiNoWorkDir.Click += (sender, e) => this.NoWorkingDirectory = !TsiNoWorkDir.Checked;
             TsiNeverDefault.Click += (sender, e) => this.NeverDefault = !TsiNeverDefault.Checked;
+            TsiShowAsDisabled.Click += (sender, e) => SetDisabled(!TsiShowAsDisabled.Checked);
             ContextMenuStrip.Opening += (sender, e) => RefreshMenuItem();
             BtnSubItems.MouseDown += (sender, e) => ShowSubItems();
             TsiShieldIcon.Click += (sender, e) => UseShieldIcon();
@@ -448,19 +491,34 @@ namespace ContextMenuManager.Controls
         private void UseShieldIcon()
         {
             bool flag = this.HasLUAShield = TsiShieldIcon.Checked = !TsiShieldIcon.Checked;
-            if(flag && IconLocation == null)
+            if(IconLocation == null)
             {
-                this.Image = AppImage.Shield;
-                this.IconPath = "imageres.dll";
-                this.IconIndex = -78;
+                if(flag)
+                {
+                    this.Image = AppImage.Shield;
+                    this.IconPath = "imageres.dll";
+                    this.IconIndex = -78;
+                }
+                else
+                {
+                    this.Image = this.Image.ToTransparent();
+                }
             }
         }
 
+        private void SetDisabled(bool flag)
+        {
+            this.ShowAsDisabledIfHidden = flag;
+            if(!ItemVisible && flag) ItemVisible = false;
+        }
+
         private void RefreshMenuItem()
         {
             TsiOnlyWithShift.Visible = !IsSubItem;
             TsiDeleteMe.Enabled = !(IsOpenItem && AppConfig.ProtectOpenItem);
             TsiNoWorkDir.Checked = this.NoWorkingDirectory;
+            TsiShowAsDisabled.Visible = WindowsOsVersion.IsAfterOrEqualWin10_1703;
+            TsiShowAsDisabled.Checked = this.ShowAsDisabledIfHidden;
             TsiChangeCommand.Visible = !IsMultiItem && Guid.Equals(Guid.Empty);
             if(!this.IsSubItem) TsiOnlyWithShift.Checked = this.OnlyWithShift;
 
@@ -525,6 +583,13 @@ namespace ContextMenuManager.Controls
             }
         }
 
+        private bool TryProtectOpenItem()
+        {
+            if(!IsOpenItem) return false;
+            if(!AppConfig.ProtectOpenItem) return false;
+            return MessageBoxEx.Show(AppString.MessageBox.PromptIsOpenItem, MessageBoxButtons.YesNo) != DialogResult.Yes;
+        }
+
         public virtual void DeleteMe()
         {
             try

+ 325 - 144
ContextMenuManager/Controls/ShellList.cs

@@ -1,11 +1,11 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
+using ContextMenuManager.Controls.Interfaces;
 using Microsoft.Win32;
 using System;
 using System.Collections.Generic;
 using System.Drawing;
 using System.Linq;
-using System.Threading;
 using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls
@@ -24,49 +24,76 @@ namespace ContextMenuManager.Controls
         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_UWPLNK = @"HKEY_CLASSES_ROOT\Launcher.ImmersiveApplication";//UWP快捷方式
         public const string MENUPATH_UNKNOWN = @"HKEY_CLASSES_ROOT\Unknown";//未知格式
-        public const string MENUPATH_TEXT = @"HKEY_CLASSES_ROOT\SystemFileAssociations\text";//通用文本文件
-        public const string MENUPATH_DOCUMENT = @"HKEY_CLASSES_ROOT\SystemFileAssociations\document";//通用文档文件
-        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.Audio";//通用音频文件目录
         public const string SYSFILEASSPATH = @"HKEY_CLASSES_ROOT\SystemFileAssociations";//系统扩展名注册表父项路径
 
         public enum Scenes
         {
-            File, Folder, Directory, Background, Desktop, Drive, AllObjects, Computer, RecycleBin,
-            Library, LnkFile, UwpLnk, ExeFile, TextFile, DocumentFile, ImageFile, VideoFile, AudioFile,
-            ImageDirectory, VideoDirectory, AudioDirectory, UnknownType, CustomType, CommandStore
+            File, Folder, Directory, Background, Desktop, Drive, AllObjects, Computer, RecycleBin, Library,
+            LnkFile, UwpLnk, ExeFile, UnknownType, CustomExtension, PerceivedType, DirectoryType, CommandStore, DragDrop
         }
 
-        private Scenes scene;
-        public Scenes Scene
+        private static readonly string[] DirectoryTypes = { "Document", "Image", "Video", "Audio" };
+        private static readonly string[] PerceivedTypes = { "Text", "Document", "Image", "Video", "Audio", "Compressed", "System" };
+        private static readonly string[] DirectoryTypeNames =
         {
-            get => scene;
-            set { scene = value; LoadItems(); }
+            AppString.Dialog.DocumentDirectory, AppString.Dialog.ImageDirectory,
+            AppString.Dialog.VideoDirectory, AppString.Dialog.AudioDirectory
+        };
+        private static readonly string[] PerceivedTypeNames =
+        {
+            AppString.Dialog.TextFile, AppString.Dialog.DocumentFile, AppString.Dialog.ImageFile, AppString.Dialog.VideoFile,
+            AppString.Dialog.AudioFile, AppString.Dialog.CompressedFile, AppString.Dialog.SystemFile
+        };
+
+        private static string GetDirectoryTypeName()
+        {
+            if(CurrentDirectoryType != null)
+            {
+                for(int i = 0; i < DirectoryTypes.Length; i++)
+                {
+                    if(CurrentDirectoryType.Equals(DirectoryTypes[i], StringComparison.OrdinalIgnoreCase))
+                    {
+                        return DirectoryTypeNames[i];
+                    }
+                }
+            }
+            return null;
         }
 
+        private static string GetPerceivedTypeName()
+        {
+            if(CurrentPerceivedType != null)
+            {
+                for(int i = 0; i < PerceivedTypes.Length; i++)
+                {
+                    if(CurrentPerceivedType.Equals(PerceivedTypes[i], StringComparison.OrdinalIgnoreCase))
+                    {
+                        return PerceivedTypeNames[i];
+                    }
+                }
+            }
+            return null;
+        }
+
+        private static string CurrentExtension = null;
+        private static string CurrentDirectoryType = null;
+        private static string CurrentPerceivedType = null;
+
         private static string GetShellPath(string scenePath) => $@"{scenePath}\shell";
         private static string GetShellExPath(string scenePath) => $@"{scenePath}\shellEx";
+        private static string GetSysAssExtPath(string typeName) => typeName != null ? $@"{SYSFILEASSPATH}\{typeName}" : null;
+        private static string GetOpenModePath(string extension) => extension != null ? $@"HKEY_CLASSES_ROOT\{FileExtension.GetOpenMode(extension)}" : null;
+
+        public Scenes Scene { get; set; }
 
         public ShellList()
         {
-            TypeItem.ExtensionChanged += (sender, e) =>
-            {
-                this.ClearItems();
-                this.Scene = Scenes.CustomType;
-            };
+            SelectItem.SelectedChanged += (sender, e) => { this.ClearItems(); this.LoadItems(); };
         }
 
-        private void LoadItems()
+        public void LoadItems()
         {
             string scenePath = null;
             switch(Scene)
@@ -96,79 +123,82 @@ namespace ContextMenuManager.Controls
                     if(WindowsOsVersion.IsEqualVista) return;
                     scenePath = MENUPATH_LIBRARY; break;
                 case Scenes.LnkFile:
-                    scenePath = MENUPATH_LNKFILE; break;
+                    scenePath = GetSysAssExtPath(".lnk"); break;
                 case Scenes.UwpLnk:
                     //Win8之前没有Uwp
                     if(WindowsOsVersion.IsBefore8) return;
                     scenePath = MENUPATH_UWPLNK; break;
                 case Scenes.ExeFile:
-                    scenePath = MENUPATH_EXEFILE; break;
-                case Scenes.TextFile:
-                    scenePath = MENUPATH_TEXT; break;
-                case Scenes.DocumentFile:
-                    scenePath = MENUPATH_DOCUMENT; break;
-                case Scenes.ImageFile:
-                    scenePath = MENUPATH_IMAGE; break;
-                case Scenes.VideoFile:
-                    scenePath = MENUPATH_VIDEO; break;
-                case Scenes.AudioFile:
-                    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;
+                    scenePath = GetSysAssExtPath(".exe"); break;
                 case Scenes.UnknownType:
                     scenePath = MENUPATH_UNKNOWN; break;
-                case Scenes.CustomType:
-                    scenePath = TypeItem.SysAssExtPath; break;
+                case Scenes.CustomExtension:
+                    scenePath = GetSysAssExtPath(CurrentExtension); break;
+                case Scenes.PerceivedType:
+                    scenePath = GetSysAssExtPath(CurrentPerceivedType); break;
+                case Scenes.DirectoryType:
+                    if(CurrentDirectoryType == null) scenePath = null;
+                    else scenePath = GetSysAssExtPath($"Directory.{CurrentDirectoryType}"); break;
                 case Scenes.CommandStore:
                     //Vista系统没有这一项
                     if(WindowsOsVersion.IsEqualVista) return;
                     this.AddNewItem(RegistryEx.GetParentPath(ShellItem.CommandStorePath));
-                    this.LoadCommandStoreItems();
+                    this.LoadStoreItems();
+                    return;
+                case Scenes.DragDrop:
+                    this.AddNewItem(MENUPATH_FOLDER);
+                    this.LoadShellExItems(GetShellExPath(MENUPATH_FOLDER));
+                    this.LoadShellExItems(GetShellExPath(MENUPATH_DIRECTORY));
+                    this.LoadShellExItems(GetShellExPath(MENUPATH_DRIVE));
+                    this.LoadShellExItems(GetShellExPath(MENUPATH_ALLOBJECTS));
                     return;
             }
             this.AddNewItem(scenePath);
             this.LoadItems(scenePath);
-
-            switch(scene)
+            switch(Scene)
             {
                 case Scenes.File:
-                    if(WindowsOsVersion.ISAfterOrEqual10)
-                        this.AddItem(new RegRuleItem(RegRuleItem.ShareWithSkype));
+                    bool flag = WindowsOsVersion.ISAfterOrEqual10;
+                    if(flag)
+                    {
+                        using(RegistryKey key = RegistryEx.GetRegistryKey(@"HKEY_CLASSES_ROOT\PackagedCom\Package"))
+                        {
+                            flag = key != null && key.GetSubKeyNames().ToList().Any(name => name.StartsWith("Microsoft.SkypeApp", StringComparison.OrdinalIgnoreCase));
+                        }
+                    }
+                    if(flag) this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.ShareWithSkype));
                     break;
                 case Scenes.Background:
-                    this.AddItem(new RegRuleItem(RegRuleItem.CustomFolder));
+                    this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.CustomFolder));
                     break;
                 case Scenes.Computer:
-                    this.AddItem(new RegRuleItem(RegRuleItem.NetworkDrive));
+                    this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.NetworkDrive));
                     break;
                 case Scenes.RecycleBin:
-                    this.AddItem(new RegRuleItem(RegRuleItem.RecycleBinProperties));
+                    this.AddItem(new VisibleRegRuleItem(VisibleRegRuleItem.RecycleBinProperties));
                     break;
                 case Scenes.Library:
                     this.LoadItems(MENUPATH_LIBRARY_BACKGROUND);
                     this.LoadItems(MENUPATH_LIBRARY_USER);
                     break;
                 case Scenes.LnkFile:
-                    this.LoadItems(MENUPATH_SYSLNKFILE);
+                    this.LoadItems(GetOpenModePath(".lnk"));
                     break;
                 case Scenes.ExeFile:
-                    this.LoadItems(MENUPATH_SYSEXEFILE);
+                    this.LoadItems(GetOpenModePath(".exe"));
                     break;
-                case Scenes.CustomType:
-                    this.InsertItem(new TypeItem(), 0);
-                    this.InsertItem(new PerceptionItem(), 1);
-                    this.LoadItems(TypeItem.AssExtPath);
+                case Scenes.CustomExtension:
+                case Scenes.PerceivedType:
+                case Scenes.DirectoryType:
+                    this.InsertItem(new SelectItem(Scene), 0);
+                    if(Scene == Scenes.CustomExtension) this.LoadItems(GetOpenModePath(CurrentExtension));
                     break;
             }
         }
 
         private void LoadItems(string scenePath)
         {
-            if(this.Scene == Scenes.CustomType && TypeItem.Extension == null) return;
+            if(scenePath == null) return;
             RegTrustedInstaller.TakeRegKeyOwnerShip(scenePath);
             this.LoadShellItems(GetShellPath(scenePath));
             this.LoadShellExItems(GetShellExPath(scenePath));
@@ -193,142 +223,293 @@ namespace ContextMenuManager.Controls
             using(RegistryKey shellExKey = RegistryEx.GetRegistryKey(shellExPath))
             {
                 if(shellExKey == null) return;
+                bool isDragDrop = Scene == Scenes.DragDrop;
                 RegTrustedInstaller.TakeRegTreeOwnerShip(shellExKey.Name);
-                Dictionary<string, Guid> dic = ShellExItem.GetPathAndGuids(shellExPath);
+                Dictionary<string, Guid> dic = ShellExItem.GetPathAndGuids(shellExPath, isDragDrop);
+                GroupPathItem groupItem = null;
+                if(isDragDrop)
+                {
+                    groupItem = GetDragDropGroupItem(shellExPath);
+                    this.AddItem(groupItem);
+                }
                 foreach(string path in dic.Keys)
                 {
                     string keyName = RegistryEx.GetKeyName(path);
                     if(!names.Contains(keyName))
                     {
-                        this.AddItem(new ShellExItem(dic[path], path));
+                        ShellExItem item = new ShellExItem(dic[path], path);
+                        if(groupItem != null) item.FoldGroupItem = groupItem;
+                        this.AddItem(item);
                         names.Add(keyName);
                     }
                 }
+                if(groupItem != null) groupItem.IsFold = true;
             }
         }
 
+        private GroupPathItem GetDragDropGroupItem(string shellExPath)
+        {
+            string text = null;
+            Image image = null;
+            string path = shellExPath.Substring(0, shellExPath.LastIndexOf('\\'));
+            switch(path)
+            {
+                case MENUPATH_FOLDER:
+                    text = AppString.SideBar.Folder;
+                    image = AppImage.Folder;
+                    break;
+                case MENUPATH_DIRECTORY:
+                    text = AppString.SideBar.Directory;
+                    image = AppImage.Directory;
+                    break;
+                case MENUPATH_DRIVE:
+                    text = AppString.SideBar.Drive;
+                    image = AppImage.Drive;
+                    break;
+                case MENUPATH_ALLOBJECTS:
+                    text = AppString.SideBar.AllObjects;
+                    image = AppImage.AllObjects;
+                    break;
+            }
+            return new GroupPathItem(shellExPath, ObjectPath.PathType.Registry) { Text = text, Image = image };
+        }
+
         private void AddNewItem(string scenePath)
         {
-            string shellPath = GetShellPath(scenePath);
-            NewItem newItem = new NewItem();
+            NewItem newItem = new NewItem { Visible = scenePath != null };
             this.AddItem(newItem);
-            if(this.Scene == Scenes.CustomType)
+            newItem.AddNewItem += (sender, e) =>
+            {
+                bool isShell;
+                if(Scene == Scenes.DragDrop) isShell = false;
+                else
+                {
+                    using(SelectDialog dlg = new SelectDialog())
+                    {
+                        dlg.Items = new[] { "Shell", "ShellEx" };
+                        dlg.Title = "请选择新建菜单类型";
+                        dlg.Selected = dlg.Items[0];
+                        if(dlg.ShowDialog() != DialogResult.OK) return;
+                        isShell = dlg.SelectedIndex == 0;
+                    }
+                }
+                if(isShell) this.AddNewShellItem(scenePath);
+                else this.AddNewShellExItem(scenePath);
+            };
+        }
+
+        private void AddNewShellItem(string scenePath)
+        {
+            string shellPath = GetShellPath(scenePath);
+            using(NewShellDialog dlg = new NewShellDialog())
             {
-                newItem.Visible = TypeItem.Extension != null;
-                TypeItem.ExtensionChanged += (sender, e) => newItem.Visible = TypeItem.Extension != null;
+                dlg.ScenePath = scenePath;
+                dlg.ShellPath = shellPath;
+                if(dlg.ShowDialog() != DialogResult.OK) return;
+                for(int i = 0; i < this.Controls.Count; i++)
+                {
+                    if(this.Controls[i] is NewItem)
+                    {
+                        this.InsertItem(new ShellItem(dlg.NewItemRegPath), i + 1);
+                        break;
+                    }
+                }
             }
-            newItem.AddNewItem += (sender, e) =>
+        }
+
+        private void AddNewShellExItem(string scenePath)
+        {
+            bool isDragDrop = Scene == Scenes.DragDrop;
+            using(InputDialog dlg1 = new InputDialog { Title = AppString.Dialog.InputGuid })
             {
-                using(NewShellDialog dlg = new NewShellDialog
+                if(GuidEx.TryParse(Clipboard.GetText(), out Guid guid)) dlg1.Text = guid.ToString();
+                if(dlg1.ShowDialog() != DialogResult.OK) return;
+                if(GuidEx.TryParse(dlg1.Text, out guid))
                 {
-                    ScenePath = scenePath,
-                    ShellPath = shellPath
-                })
+                    if(isDragDrop)
+                    {
+                        using(SelectDialog dlg2 = new SelectDialog())
+                        {
+                            dlg2.Title = AppString.Dialog.SelectGroup;
+                            dlg2.Items = new[] { AppString.SideBar.Folder, AppString.SideBar.Directory,
+                                        AppString.SideBar.Drive, AppString.SideBar.AllObjects };
+                            dlg2.Selected = dlg2.Items[0];
+                            if(dlg2.ShowDialog() != DialogResult.OK) return;
+                            switch(dlg2.SelectedIndex)
+                            {
+                                case 0:
+                                    scenePath = MENUPATH_FOLDER; break;
+                                case 1:
+                                    scenePath = MENUPATH_DIRECTORY; break;
+                                case 2:
+                                    scenePath = MENUPATH_DRIVE; break;
+                                case 3:
+                                    scenePath = MENUPATH_ALLOBJECTS; break;
+                            }
+                        }
+                    }
+                    string shellExPath = GetShellExPath(scenePath);
+                    if(ShellExItem.GetPathAndGuids(shellExPath, isDragDrop).Values.Contains(guid))
+                    {
+                        MessageBoxEx.Show(AppString.MessageBox.HasBeenAdded);
+                    }
+                    else
+                    {
+                        string part = isDragDrop ? ShellExItem.DdhParts[0] : ShellExItem.CmhParts[0];
+                        string regPath = $@"{shellExPath}\{part}\{guid:B}";
+                        Registry.SetValue(regPath, "", guid.ToString("B"));
+                        ShellExItem item = new ShellExItem(guid, regPath);
+                        for(int i = 0; i < this.Controls.Count; i++)
+                        {
+                            if(isDragDrop)
+                            {
+                                if(this.Controls[i] is GroupPathItem groupItem)
+                                {
+                                    if(groupItem.TargetPath.Equals(shellExPath, StringComparison.OrdinalIgnoreCase))
+                                    {
+                                        this.InsertItem(item, i + 1);
+                                        item.FoldGroupItem = groupItem;
+                                        item.Visible = !groupItem.IsFold;
+                                        break;
+                                    }
+                                }
+                            }
+                            else
+                            {
+                                if(this.Controls[i] is NewItem)
+                                {
+                                    this.InsertItem(item, i + 1);
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+                else
                 {
-                    if(dlg.ShowDialog() == DialogResult.OK)
-                        this.InsertItem(new ShellItem(dlg.NewItemRegPath), GetItemIndex(newItem) + 1);
+                    MessageBoxEx.Show(AppString.MessageBox.MalformedGuid);
                 }
-            };
+            }
         }
 
-        private void LoadCommandStoreItems()
+        ///<summary>“其他规则”-“公共引用”</summary>
+        private void LoadStoreItems()
         {
             using(var shellKey = RegistryEx.GetRegistryKey(ShellItem.CommandStorePath))
             {
                 Array.ForEach(Array.FindAll(shellKey.GetSubKeyNames(), itemName =>
                     !ShellItem.SysStoreItemNames.Contains(itemName, StringComparer.OrdinalIgnoreCase)), itemName =>
                     {
-                        this.AddItem(new ShellItem($@"{ShellItem.CommandStorePath}\{itemName}"));
+                        this.AddItem(new StoreShellItem($@"{ShellItem.CommandStorePath}\{itemName}", true, false));
                     });
             }
         }
 
-        sealed class TypeItem : MyListItem
+        sealed class SelectItem : MyListItem
         {
-            static string extension;
-            public static string Extension
+            static string selected;
+            public static string Selected
             {
-                get => extension;
+                get => selected;
                 set
                 {
-                    extension = value;
-                    ExtensionChanged?.Invoke(null, null);
+                    selected = value;
+                    SelectedChanged?.Invoke(null, null);
                 }
             }
 
-            public static string SysAssExtPath => Extension == null ? null : $@"{SYSFILEASSPATH}\{Extension}";
-            public static string AssExtPath => Extension == null ? null : $@"HKEY_CLASSES_ROOT\{FileExtension.GetOpenMode(Extension)}";
-            public static string ExtensionPath => $@"HKEY_CLASSES_ROOT\{Extension}";
-
-            public static event EventHandler ExtensionChanged;
+            public static event EventHandler SelectedChanged;
 
-            readonly PictureButton BtnType = new PictureButton(AppImage.Types);
+            readonly PictureButton BtnSelect = new PictureButton(AppImage.Select);
 
-            public TypeItem()
+            public SelectItem(Scenes scene)
             {
-                this.GetText();
-                this.Image = AppImage.CustomType;
-                this.AddCtr(BtnType);
                 this.SetNoClickEvent();
-                BtnType.MouseDown += (sender, e) =>
-                {
-                    using(FileExtensionDialog dlg = new FileExtensionDialog())
-                        if(dlg.ShowDialog() == DialogResult.OK) Extension = dlg.Extension;
-                };
-                ExtensionChanged += (sender, e) => this.GetText();
+                this.Image = AppImage.Custom;
+                this.Text = this.GetText(scene);
+                this.AddCtr(BtnSelect);
+                BtnSelect.MouseDown += (sender, e) => Select(scene);
             }
 
-            private void GetText()
+            private string GetText(Scenes scene)
             {
-                if(Extension == null)
-                {
-                    this.Text = AppString.Dialog.SelectExtension;
-                }
-                else
+                switch(scene)
                 {
-                    this.Text = $"{AppString.Item.CurrentExtension}{Extension}";
+                    case Scenes.CustomExtension:
+                        if(CurrentExtension == null)
+                        {
+                            return AppString.Item.SelectExtension;
+                        }
+                        else
+                        {
+                            return AppString.Item.CurrentExtension.Replace("%s", CurrentExtension);
+                        }
+                    case Scenes.PerceivedType:
+                        if(CurrentPerceivedType == null)
+                        {
+                            return AppString.Item.SelectPerceivedType;
+                        }
+                        else
+                        {
+                            return AppString.Item.CurrentPerceivedType.Replace("%s", GetPerceivedTypeName());
+                        }
+                    case Scenes.DirectoryType:
+                        if(CurrentDirectoryType == null)
+                        {
+                            return AppString.Item.SelectDirectoryType;
+                        }
+                        else
+                        {
+                            return AppString.Item.CurrentDirectoryType.Replace("%s", GetDirectoryTypeName());
+                        }
+                    default:
+                        return null;
                 }
             }
-        }
-
-        sealed class PerceptionItem : MyListItem
-        {
-            private static readonly string[] PerceptionTypes = { "Text", "Image", "Video", "Audio", "System", "Compressed" };
-
-            public PerceptionItem()
-            {
-                this.Image = ResourceIcon.GetExtensionIcon(TypeItem.Extension)?.ToBitmap() ?? AppImage.NotFound;
-                this.Text = AppString.Item.SetPerceivedType;
-                this.Visible = TypeItem.Extension != null;
-                this.AddCtr(cmbType);
-                cmbType.Text = Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(PerceivedType ?? string.Empty);
-                cmbType.Items.AddRange(PerceptionTypes);
-                cmbType.TextChanged += (sneder, e) => PerceivedType = cmbType.Text;
-                TypeItem.ExtensionChanged += (sender, e) => this.Visible = TypeItem.Extension != null;
-            }
 
-            private static string PerceivedType
+            private void Select(Scenes scene)
             {
-                get => Registry.GetValue(TypeItem.ExtensionPath, "PerceivedType", null)?.ToString();
-                set
+                SelectDialog dlg;
+                switch(scene)
                 {
-                    if(value.IsNullOrWhiteSpace())
-                    {
-                        RegistryEx.DeleteValue(TypeItem.ExtensionPath, "PerceivedType");
-                    }
-                    else
-                    {
-                        Registry.SetValue(TypeItem.ExtensionPath, "PerceivedType", value);
-                    }
+                    case Scenes.CustomExtension:
+                        dlg = new FileExtensionDialog
+                        {
+                            Selected = CurrentExtension?.Substring(1)
+                        };
+                        break;
+                    case Scenes.PerceivedType:
+                        dlg = new SelectDialog
+                        {
+                            Items = PerceivedTypeNames,
+                            Title = AppString.Item.SelectPerceivedType,
+                            Selected = GetPerceivedTypeName() ?? PerceivedTypeNames[0]
+                        };
+                        break;
+                    case Scenes.DirectoryType:
+                        dlg = new SelectDialog
+                        {
+                            Items = DirectoryTypeNames,
+                            Title = AppString.Item.SelectDirectoryType,
+                            Selected = GetDirectoryTypeName() ?? DirectoryTypeNames[0]
+                        };
+                        break;
+                    default: return;
+                }
+                if(dlg.ShowDialog() != DialogResult.OK) return;
+                switch(scene)
+                {
+                    case Scenes.CustomExtension:
+                        Selected = CurrentExtension = dlg.Selected;
+                        break;
+                    case Scenes.PerceivedType:
+                        Selected = CurrentPerceivedType = PerceivedTypes[dlg.SelectedIndex];
+                        break;
+                    case Scenes.DirectoryType:
+                        Selected = CurrentDirectoryType = DirectoryTypes[dlg.SelectedIndex];
+                        break;
                 }
             }
-
-            readonly ComboBox cmbType = new ComboBox
-            {
-                Font = new Font(SystemFonts.MenuFont.FontFamily, 10F),
-                ImeMode = ImeMode.Disable,
-                Width = 100.DpiZoom()
-            };
         }
     }
 }

+ 66 - 11
ContextMenuManager/Controls/ShellNewItem.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using ContextMenuManager.Controls.Interfaces;
 using Microsoft.Win32;
 using System;
@@ -19,7 +19,9 @@ namespace ContextMenuManager.Controls
          ITsiIconItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiRegPathItem, ITsiRegDeleteItem, ITsiRegExportItem, ITsiCommandItem
     {
         public static readonly string[] SnParts = { "ShellNew", "-ShellNew" };
-        public static readonly string[] UnableSortExtensions = { ".library-ms", ".lnk", "Folder" };
+        public static readonly string[] UnableSortExtensions = { "Folder", ".library-ms" };
+        public static readonly string[] DefaultBeforeSeparatorExtensions = { "Folder", ".library-ms", ".lnk" };
+        public static readonly string[] EffectValueNames = { "NullFile", "Data", "FileName", "Directory", "Command" };
         private static readonly string[] UnableEditDataValues = { "Directory", "FileName", "Handler", "Command" };
         private static readonly string[] UnableChangeCommandValues = { "Data", "Directory", "FileName", "Handler" };
 
@@ -28,7 +30,7 @@ namespace ContextMenuManager.Controls
             this.Owner = list;
             InitializeComponents();
             this.RegPath = regPath;
-            BtnMoveUp.Visible = BtnMoveDown.Visible = this.CanSort && LockNewItem.IsLocked();
+            SetSortabled(ShellNewLockItem.IsLocked);
         }
 
         private string regPath;
@@ -44,6 +46,7 @@ namespace ContextMenuManager.Controls
             }
         }
 
+        public string ValueName => null;
         public string SearchText => $"{AppString.SideBar.New} {Text}";
         public string Extension => RegPath.Split('\\')[1];
         private string SnKeyName => RegistryEx.GetKeyName(RegPath);
@@ -54,10 +57,12 @@ namespace ContextMenuManager.Controls
         private string OpenModePath => $@"{HKCR}\{OpenMode}";//关联打开方式注册表路径
         private string DefaultOpenMode => Registry.GetValue($@"{HKCR}\{Extension}", "", null)?.ToString();//默认关联打开方式
         private string DefaultOpenModePath => $@"{HKCR}\{DefaultOpenMode}";//默认关联打开方式注册表路径
+        private string ConfigPath => $@"{RegPath}\Config";
 
+        public bool CanSort => !UnableSortExtensions.Contains(Extension, StringComparer.OrdinalIgnoreCase);//能够排序的
         private bool CanEditData => UnableEditDataValues.All(value => Registry.GetValue(RegPath, value, null) == null);//能够编辑初始数据的
         private bool CanChangeCommand => UnableChangeCommandValues.All(value => Registry.GetValue(RegPath, value, null) == null);//能够更改菜单命令的
-        public bool CanSort => !UnableSortExtensions.Contains(Extension, StringComparer.OrdinalIgnoreCase);//能够排序
+        private bool DefaultBeforeSeparator => DefaultBeforeSeparatorExtensions.Contains(Extension, StringComparer.OrdinalIgnoreCase);//默认显示在分割线上不可更改
 
         public string ItemFilePath
         {
@@ -169,6 +174,34 @@ namespace ContextMenuManager.Controls
             }
         }
 
+        public bool BeforeSeparator
+        {
+            get
+            {
+                if(DefaultBeforeSeparator) return true;
+                else return Registry.GetValue(ConfigPath, "BeforeSeparator", null) != null;
+            }
+            set
+            {
+                if(value)
+                {
+                    Registry.SetValue(ConfigPath, "BeforeSeparator", "");
+                }
+                else
+                {
+                    using(RegistryKey snkey = RegistryEx.GetRegistryKey(RegPath, true))
+                    using(RegistryKey ckey = snkey.OpenSubKey("Config", true))
+                    {
+                        ckey.DeleteValue("BeforeSeparator");
+                        if(ckey.GetValueNames().Length == 0 && ckey.GetSubKeyNames().Length == 0)
+                        {
+                            snkey.DeleteSubKey("Config");
+                        }
+                    }
+                }
+            }
+        }
+
         public ShellNewList Owner { get; private set; }
         public MoveButton BtnMoveUp { get; set; }
         public MoveButton BtnMoveDown { get; set; }
@@ -185,6 +218,8 @@ namespace ContextMenuManager.Controls
         public ChangeCommandMenuItem TsiChangeCommand { get; set; }
 
         readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details);
+        readonly ToolStripMenuItem TsiOtherAttributes = new ToolStripMenuItem(AppString.Menu.OtherAttributes);
+        readonly ToolStripMenuItem TsiBeforeSeparator = new ToolStripMenuItem(AppString.Menu.BeforeSeparator);
         readonly ToolStripMenuItem TsiEditData = new ToolStripMenuItem(AppString.Menu.InitialData);
 
         private void InitializeComponents()
@@ -205,18 +240,24 @@ namespace ContextMenuManager.Controls
             TsiChangeCommand.CommandCanBeEmpty = true;
 
             ContextMenuStrip.Items.AddRange(new ToolStripItem[] {TsiChangeText,
-                new ToolStripSeparator(), TsiChangeIcon, new ToolStripSeparator(),
-                TsiDetails, new ToolStripSeparator(), TsiDeleteMe });
+                new ToolStripSeparator(), TsiChangeIcon, new ToolStripSeparator(), TsiOtherAttributes,
+                new ToolStripSeparator(), TsiDetails, new ToolStripSeparator(), TsiDeleteMe });
 
-            TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch, new ToolStripSeparator(),
-                TsiEditData,TsiChangeCommand, TsiFileProperties, TsiFileLocation, TsiRegLocation, TsiRegExport });
+            TsiOtherAttributes.DropDownItems.AddRange(new[] { TsiBeforeSeparator, TsiEditData });
+
+            TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch,
+                new ToolStripSeparator(), TsiChangeCommand, TsiFileProperties,
+                TsiFileLocation, TsiRegLocation, TsiRegExport });
 
-            TsiEditData.Click += (sender, e) => EditInitialData();
             ContextMenuStrip.Opening += (sender, e) =>
             {
                 TsiEditData.Visible = CanEditData;
                 TsiChangeCommand.Visible = CanChangeCommand;
+                TsiBeforeSeparator.Enabled = !DefaultBeforeSeparator;
+                TsiBeforeSeparator.Checked = BeforeSeparator;
             };
+            TsiEditData.Click += (sender, e) => EditInitialData();
+            TsiBeforeSeparator.Click += (sender, e) => MoveWithSeparator(!TsiBeforeSeparator.Checked);
             BtnMoveUp.MouseDown += (sender, e) => Owner.MoveItem(this, true);
             BtnMoveDown.MouseDown += (sender, e) => Owner.MoveItem(this, false);
         }
@@ -235,12 +276,26 @@ namespace ContextMenuManager.Controls
             }
         }
 
+        public void SetSortabled(bool isLocked)
+        {
+            BtnMoveDown.Visible = BtnMoveUp.Visible = isLocked && CanSort;
+        }
+
+        private void MoveWithSeparator(bool isBefore)
+        {
+            BeforeSeparator = isBefore;
+            ShellNewList list = (ShellNewList)this.Parent;
+            int index = list.GetItemIndex(list.Separator);
+            list.SetItemIndex(this, index);
+            if(ShellNewLockItem.IsLocked) list.SaveSorting();
+        }
+
         public void DeleteMe()
         {
             RegistryEx.DeleteKeyTree(this.RegPath);
             RegistryEx.DeleteKeyTree(this.BackupPath);
             this.Dispose();
-            if(LockNewItem.IsLocked()) Owner.WriteRegistry();
+            if(ShellNewLockItem.IsLocked) Owner.SaveSorting();
         }
     }
 }

+ 143 - 43
ContextMenuManager/Controls/ShellNewList.cs

@@ -1,9 +1,12 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
+using ContextMenuManager.Controls.Interfaces;
 using Microsoft.Win32;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Security.AccessControl;
+using System.Security.Principal;
 using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls
@@ -11,13 +14,16 @@ namespace ContextMenuManager.Controls
     sealed class ShellNewList : MyList
     {
         public const string ShellNewPath = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Discardable\PostSetup\ShellNew";
-        private static readonly string[] ValueNames = { "NullFile", "Data", "FileName", "Directory", "Command" };
+
+        public ShellNewSeparator Separator;
 
         public void LoadItems()
         {
             this.AddNewItem();
-            this.AddItem(new LockNewItem(this));
-            if(LockNewItem.IsLocked()) this.LoadLockItems();
+            this.AddItem(new ShellNewLockItem(this));
+            Separator = new ShellNewSeparator();
+            this.AddItem(Separator);
+            if(ShellNewLockItem.IsLocked) this.LoadLockItems();
             else this.LoadUnlockItems();
         }
 
@@ -44,11 +50,10 @@ namespace ContextMenuManager.Controls
         {
             foreach(string extension in ShellNewItem.UnableSortExtensions)
             {
-                string str = extensions.Find(ext => ext.Equals(extension, StringComparison.OrdinalIgnoreCase));
-                if(str != null)
+                if(extensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
                 {
-                    extensions.Remove(str);
-                    extensions.Insert(0, str);
+                    extensions.Remove(extension);
+                    extensions.Insert(0, extension);
                 }
             }
             using(RegistryKey root = Registry.ClassesRoot)
@@ -57,7 +62,7 @@ namespace ContextMenuManager.Controls
                 {
                     using(RegistryKey extKey = root.OpenSubKey(extension))
                     {
-                        string defalutOpenMode = extKey.GetValue("")?.ToString();
+                        string defalutOpenMode = extKey?.GetValue("")?.ToString();
                         if(string.IsNullOrEmpty(defalutOpenMode)) continue;
                         using(RegistryKey openModeKey = root.OpenSubKey(defalutOpenMode))
                         {
@@ -75,9 +80,18 @@ namespace ContextMenuManager.Controls
                                 if(tKey != null) snPart = $@"{defalutOpenMode}\{snPart}";
                                 using(RegistryKey snKey = extKey.OpenSubKey(snPart))
                                 {
-                                    if(ValueNames.Any(valueName => snKey?.GetValue(valueName) != null))
+                                    if(ShellNewItem.EffectValueNames.Any(valueName => snKey?.GetValue(valueName) != null))
                                     {
-                                        this.AddItem(new ShellNewItem(this, snKey.Name));
+                                        ShellNewItem item = new ShellNewItem(this, snKey.Name);
+                                        if(item.BeforeSeparator)
+                                        {
+                                            int index2 = this.GetItemIndex(Separator);
+                                            this.InsertItem(item, index2);
+                                        }
+                                        else
+                                        {
+                                            this.AddItem(item);
+                                        }
                                         break;
                                     }
                                 }
@@ -88,45 +102,32 @@ namespace ContextMenuManager.Controls
             }
         }
 
-        public void MoveItem(ShellNewItem item, bool isUp)
+        public void MoveItem(ShellNewItem shellNewItem, bool isUp)
         {
-            int index = this.GetItemIndex(item);
-            int firstIndex = 0;
-            for(int i = 0; i < this.Controls.Count; i++)
-            {
-                Control ctr = this.Controls[i];
-                if(ctr.GetType() == typeof(ShellNewItem) && ((ShellNewItem)ctr).CanSort)
-                {
-                    firstIndex = i; break;
-                }
-            }
-            if(isUp)
-            {
-                if(index > firstIndex)
-                {
-                    this.SetItemIndex(item, index - 1);
-                }
-            }
-            else
+            int index = this.GetItemIndex(shellNewItem);
+            index += isUp ? -1 : 1;
+            if(index == this.Controls.Count) return;
+            Control ctr = this.Controls[index];
+            if(ctr is ShellNewItem item && item.CanSort)
             {
-                if(index < this.Controls.Count - 1)
-                {
-                    this.SetItemIndex(item, index + 1);
-                }
+                this.SetItemIndex(shellNewItem, index);
+                this.SaveSorting();
             }
-            this.WriteRegistry();
         }
 
-        public void WriteRegistry()
+        public void SaveSorting()
         {
             List<string> extensions = new List<string>();
             for(int i = 2; i < this.Controls.Count; i++)
             {
-                extensions.Add(((ShellNewItem)Controls[i]).Extension);
+                if(Controls[i] is ShellNewItem item)
+                {
+                    extensions.Add(item.Extension);
+                }
             }
-            LockNewItem.UnLock();
+            ShellNewLockItem.UnLock();
             Registry.SetValue(ShellNewPath, "Classes", extensions.ToArray());
-            LockNewItem.Lock();
+            ShellNewLockItem.Lock();
         }
 
         private void AddNewItem()
@@ -147,9 +148,9 @@ namespace ContextMenuManager.Controls
                     }
                     foreach(Control ctr in this.Controls)
                     {
-                        if(ctr.GetType() == typeof(ShellNewItem))
+                        if(ctr is ShellNewItem item)
                         {
-                            if(((ShellNewItem)ctr).Extension.Equals(extension, StringComparison.OrdinalIgnoreCase))
+                            if(item.Extension.Equals(extension, StringComparison.OrdinalIgnoreCase))
                             {
                                 MessageBoxEx.Show(AppString.MessageBox.HasBeenAdded);
                                 return;
@@ -174,10 +175,109 @@ namespace ContextMenuManager.Controls
                         {
                             item.ItemText = $"{extension.Substring(1)} file";
                         }
-                        if(LockNewItem.IsLocked()) this.WriteRegistry();
+                        if(ShellNewLockItem.IsLocked) this.SaveSorting();
                     }
                 }
             };
         }
     }
+
+    sealed class ShellNewLockItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, ITsiWebSearchItem
+    {
+        public ShellNewLockItem(ShellNewList list)
+        {
+            this.Owner = list;
+            this.Image = AppImage.Lock;
+            this.Text = AppString.Item.LockNewMenu;
+            this.SetNoClickEvent();
+            BtnShowMenu = new MenuButton(this);
+            ChkVisible = new VisibleCheckBox(this) { Checked = IsLocked };
+            MyToolTip.SetToolTip(ChkVisible, AppString.Tip.LockNewMenu);
+            TsiSearch = new WebSearchMenuItem(this);
+            this.ContextMenuStrip = new ContextMenuStrip();
+            this.ContextMenuStrip.Items.Add(TsiSearch);
+        }
+
+        public MenuButton BtnShowMenu { get; set; }
+        public WebSearchMenuItem TsiSearch { get; set; }
+        public VisibleCheckBox ChkVisible { get; set; }
+        public ShellNewList Owner { get; private set; }
+
+        public bool ItemVisible
+        {
+            get => IsLocked;
+            set
+            {
+                if(value) Owner.SaveSorting();
+                else UnLock();
+                foreach(Control ctr in Owner.Controls)
+                {
+                    if(ctr is ShellNewItem item)
+                    {
+                        item.SetSortabled(value);
+                    }
+                }
+            }
+        }
+
+        public string SearchText => Text;
+
+        public static bool IsLocked
+        {
+            get
+            {
+                using(RegistryKey key = RegistryEx.GetRegistryKey(ShellNewList.ShellNewPath))
+                {
+                    RegistrySecurity rs = key.GetAccessControl();
+                    foreach(RegistryAccessRule rar in rs.GetAccessRules(true, true, typeof(NTAccount)))
+                    {
+                        if(rar.AccessControlType.ToString().Equals("Deny", StringComparison.OrdinalIgnoreCase))
+                        {
+                            if(rar.IdentityReference.ToString().Equals("Everyone", StringComparison.OrdinalIgnoreCase)) return true;
+                        }
+                    }
+                }
+                return false;
+            }
+        }
+
+        public static void Lock()
+        {
+            using(RegistryKey key = RegistryEx.GetRegistryKey(ShellNewList.ShellNewPath, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.ChangePermissions))
+            {
+                RegistrySecurity rs = new RegistrySecurity();
+                RegistryAccessRule rar = new RegistryAccessRule("Everyone", RegistryRights.Delete | RegistryRights.WriteKey, AccessControlType.Deny);
+                rs.AddAccessRule(rar);
+                key.SetAccessControl(rs);
+            }
+        }
+
+        public static void UnLock()
+        {
+            using(RegistryKey key = RegistryEx.GetRegistryKey(ShellNewList.ShellNewPath, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.ChangePermissions))
+            {
+                RegistrySecurity rs = key.GetAccessControl();
+                foreach(RegistryAccessRule rar in rs.GetAccessRules(true, true, typeof(NTAccount)))
+                {
+                    if(rar.AccessControlType.ToString().Equals("Deny", StringComparison.OrdinalIgnoreCase))
+                    {
+                        if(rar.IdentityReference.ToString().Equals("Everyone", StringComparison.OrdinalIgnoreCase))
+                        {
+                            rs.RemoveAccessRule(rar);
+                        }
+                    }
+                }
+                key.SetAccessControl(rs);
+            }
+        }
+    }
+
+    sealed class ShellNewSeparator : MyListItem
+    {
+        public ShellNewSeparator()
+        {
+            this.Text = AppString.Item.Separator;
+            this.HasImage = false;
+        }
+    }
 }

+ 23 - 20
ContextMenuManager/Controls/ShellStoreDialog.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using System;
 using System.Collections.Generic;
 using System.Drawing;
@@ -110,27 +110,30 @@ namespace ContextMenuManager.Controls
                 foreach(StoreShellItem item in list.Controls)
                     if(item.IsSelected) SelectedKeyNames.Add(item.KeyName);
             }
+        }
+    }
 
-            sealed class StoreShellItem : ShellItem
+    sealed class StoreShellItem : ShellItem
+    {
+        public StoreShellItem(string regPath, bool isPublic, bool isSelect = true) : base(regPath)
+        {
+            this.IsPublic = isPublic;
+            if(isSelect)
             {
-                public StoreShellItem(string regPath, bool isPublic) : base(regPath)
-                {
-                    this.IsPublic = isPublic;
-                    this.AddCtr(chkSelected);
-                    ChkVisible.Visible = BtnSubItems.Visible = false;
-                    RegTrustedInstaller.TakeRegTreeOwnerShip(regPath);
-                }
-                public bool IsSelected => chkSelected.Checked;
-                public bool IsPublic { get; set; }
-                readonly CheckBox chkSelected = new CheckBox { AutoSize = true };
-
-                public override void DeleteMe()
-                {
-                    if(IsPublic && MessageBoxEx.Show(AppString.MessageBox.ConfirmDeleteReferenced,
-                        MessageBoxButtons.YesNo) != DialogResult.Yes) return;
-                    base.DeleteMe();
-                }
+                this.AddCtr(chkSelected, 40.DpiZoom());
+                ChkVisible.Visible = BtnShowMenu.Visible = BtnSubItems.Visible = false;
             }
+            RegTrustedInstaller.TakeRegTreeOwnerShip(regPath);
+        }
+        public bool IsSelected => chkSelected.Checked;
+        public bool IsPublic { get; set; }
+        readonly CheckBox chkSelected = new CheckBox { AutoSize = true };
+
+        public override void DeleteMe()
+        {
+            if(IsPublic && MessageBoxEx.Show(AppString.MessageBox.ConfirmDeleteReferenced,
+                MessageBoxButtons.YesNo) != DialogResult.Yes) return;
+            base.DeleteMe();
         }
     }
 }

+ 47 - 18
ContextMenuManager/Controls/ShellSubMenuDialog.cs

@@ -1,9 +1,10 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using ContextMenuManager.Controls.Interfaces;
 using System;
 using System.Collections.Generic;
 using System.Drawing;
+using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Windows.Forms;
@@ -34,23 +35,26 @@ namespace ContextMenuManager.Controls
         {
             public ShellSubMenuForm()
             {
-                this.ShowInTaskbar = this.MinimizeBox = this.MaximizeBox = false;
                 this.StartPosition = FormStartPosition.CenterParent;
-                this.MinimumSize = this.Size = new Size(646, 389).DpiZoom();
-                this.Controls.Add(MlbSubItems);
+                this.ShowInTaskbar = this.MaximizeBox = this.MinimizeBox = false;
+                this.MinimumSize = this.Size = new Size(646, 369).DpiZoom();
+                this.Controls.AddRange(new Control[] { MlbSubItems, StatusBar });
                 this.OnResize(null);
             }
 
             /// <summary>子菜单的父菜单的注册表路径</summary>
             public string ParentPath { get; set; }
             readonly MyListBox MlbSubItems = new MyListBox { Dock = DockStyle.Fill };
+            readonly MyStatusBar StatusBar = new MyStatusBar();
+            private MyList LstSubItems;
 
             protected override void OnLoad(EventArgs e)
             {
                 base.OnLoad(e);
                 bool isPublic = true;
                 string value = GetValue(ParentPath, "SubCommands", null)?.ToString();
-                if(value.IsNullOrWhiteSpace())
+                if(value == null) isPublic = false;
+                else if(value.IsNullOrWhiteSpace())
                 {
                     using(var shellKey = RegistryEx.GetRegistryKey($@"{ParentPath}\shell"))
                     {
@@ -75,14 +79,27 @@ namespace ContextMenuManager.Controls
                 }
                 if(isPublic)
                 {
-                    new PulicMultiItemsList(MlbSubItems).LoadItems(ParentPath);
+                    LstSubItems = new PulicMultiItemsList(MlbSubItems);
+                    ((PulicMultiItemsList)LstSubItems).LoadItems(ParentPath);
                     this.Text += $"({AppString.Dialog.Public})";
                 }
                 else
                 {
-                    new PrivateMultiItemsList(MlbSubItems).LoadItems(ParentPath);
+                    LstSubItems = new PrivateMultiItemsList(MlbSubItems);
+                    ((PrivateMultiItemsList)LstSubItems).LoadItems(ParentPath);
                     this.Text += $"({AppString.Dialog.Private})";
                 }
+                LstSubItems.HoveredItemChanged += (sender, a) =>
+                {
+                    if(!AppConfig.ShowFilePath) return;
+                    MyListItem item = LstSubItems.HoveredItem;
+                    if(item is ITsiFilePathItem pathItem)
+                    {
+                        string path = pathItem.ItemFilePath;
+                        if(File.Exists(path)) { StatusBar.Text = path; return; }
+                    }
+                    StatusBar.Text = item.Text;
+                };
             }
 
             sealed class SubMenuModeForm : Form
@@ -193,7 +210,7 @@ namespace ContextMenuManager.Controls
                     {
                         if(dlg.ShowDialog() != DialogResult.OK) return;
                         SubKeyNames.Add(dlg.NewItemKeyName);
-                        WriteRegistry();
+                        SaveSorting();
                         this.AddItem(new SubShellItem(this, dlg.NewItemKeyName));
                     }
                 }
@@ -210,9 +227,10 @@ namespace ContextMenuManager.Controls
                         if(dlg.ShowDialog() != DialogResult.OK) return;
                         dlg.SelectedKeyNames.ForEach(keyName =>
                         {
+                            if(!SubShellTypeItem.CanAddMore(this)) return;
                             this.AddItem(new SubShellItem(this, keyName));
                             this.SubKeyNames.Add(keyName);
-                            WriteRegistry();
+                            SaveSorting();
                         });
                     }
                 }
@@ -220,11 +238,11 @@ namespace ContextMenuManager.Controls
                 private void AddSeparator()
                 {
                     this.SubKeyNames.Add("|");
-                    WriteRegistry();
+                    SaveSorting();
                     this.AddItem(new SeparatorItem(this));
                 }
 
-                private void WriteRegistry()
+                private void SaveSorting()
                 {
                     SetValue(ParentPath, "SubCommands", string.Join(";", SubKeyNames.ToArray()));
                 }
@@ -248,7 +266,7 @@ namespace ContextMenuManager.Controls
                             this.SubKeyNames.Reverse(index - 1, 2);
                         }
                     }
-                    this.WriteRegistry();
+                    this.SaveSorting();
                 }
 
                 private void DeleteItem(MyListItem item)
@@ -257,7 +275,7 @@ namespace ContextMenuManager.Controls
                     this.Controls.Remove(item);
                     this.Controls[index - 1].Focus();
                     this.SubKeyNames.RemoveAt(index - 1);
-                    this.WriteRegistry();
+                    this.SaveSorting();
                     item.Dispose();
                 }
 
@@ -317,6 +335,7 @@ namespace ContextMenuManager.Controls
                         BtnMoveDown.MouseDown += (sender, e) => Owner.MoveItem(this, false);
                         MyToolTip.SetToolTip(this, AppString.Tip.InvalidItem);
                         MyToolTip.SetToolTip(BtnDelete, AppString.Menu.Delete);
+                        this.SetNoClickEvent();
                     }
 
                     public DeleteButton BtnDelete { get; set; }
@@ -347,7 +366,7 @@ namespace ContextMenuManager.Controls
                 /// <summary>父菜单的注册表路径</summary>
                 public string ParentPath { get; set; }
                 /// <summary>子菜单的Shell项注册表路径</summary>
-                private string ShellPath => $@"{ParentPath}\shell";
+                private string ShellPath { get; set; }
                 /// <summary>父菜单的Shell项注册表路径</summary>
                 private string ParentShellPath => RegistryEx.GetParentPath(ParentPath);
                 /// <summary>菜单所处环境注册表路径</summary>
@@ -358,6 +377,15 @@ namespace ContextMenuManager.Controls
                 public void LoadItems(string parentPath)
                 {
                     this.ParentPath = parentPath;
+                    string sckValue = GetValue(parentPath, "ExtendedSubCommandsKey", null)?.ToString();
+                    if(!sckValue.IsNullOrWhiteSpace())
+                    {
+                        this.ShellPath = $@"HKEY_CLASSES_ROOT\{sckValue}\shell";
+                    }
+                    else
+                    {
+                        this.ShellPath = $@"{parentPath}\shell";
+                    }
                     using(var shellKey = RegistryEx.GetRegistryKey(ShellPath))
                     {
                         if(shellKey == null) return;
@@ -420,8 +448,9 @@ namespace ContextMenuManager.Controls
                         if(dlg.ShowDialog() != DialogResult.OK) return;
                         dlg.SelectedKeyNames.ForEach(keyName =>
                         {
+                            if(!SubShellTypeItem.CanAddMore(this)) return;
                             string srcPath = $@"{dlg.ShellPath}\{keyName}";
-                            string dstPath = ObjectPath.GetNewPathWithIndex($@"{this.ShellPath}\{keyName}", ObjectPath.PathType.Registry);
+                            string dstPath = ObjectPath.GetNewPathWithIndex($@"{ShellPath}\{keyName}", ObjectPath.PathType.Registry);
 
                             RegistryEx.CopyTo(srcPath, dstPath);
                             this.AddItem(new SubShellItem(this, dstPath));
@@ -530,12 +559,12 @@ namespace ContextMenuManager.Controls
                 public SubSeparatorItem()
                 {
                     this.Text = AppString.Item.Separator;
-                    this.Image = AppImage.Separator;
+                    this.HasImage = false;
                     BtnDelete = new DeleteButton(this);
                     BtnMoveDown = new MoveButton(this, false);
                     BtnMoveUp = new MoveButton(this, true);
-                    MyToolTip.SetToolTip(this, AppString.Tip.Separator);
                     MyToolTip.SetToolTip(BtnDelete, AppString.Menu.Delete);
+                    this.SetNoClickEvent();
                 }
 
                 public DeleteButton BtnDelete { get; set; }

+ 120 - 24
ContextMenuManager/Controls/ThirdRulesList.cs

@@ -1,10 +1,9 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using ContextMenuManager.Controls.Interfaces;
 using Microsoft.Win32;
 using System;
 using System.IO;
-using System.Text;
 using System.Xml;
 
 namespace ContextMenuManager.Controls
@@ -27,42 +26,136 @@ namespace ContextMenuManager.Controls
                         else continue;
                     }
 
-                    GroupPathItem groupItem = new GroupPathItem(groupXE.GetAttribute("RegPath"), ObjectPath.PathType.Registry)
+                    GroupPathItem groupItem;
+                    bool isIniGroup = groupXE.HasAttribute("IsIniGroup");
+                    string attribute = isIniGroup ? "FilePath" : "RegPath";
+                    ObjectPath.PathType pathType = isIniGroup ? ObjectPath.PathType.File : ObjectPath.PathType.Registry;
+                    groupItem = new GroupPathItem(groupXE.GetAttribute(attribute), pathType)
                     {
                         Text = groupXE.GetAttribute("Text"),
-                        Image = GuidInfo.GetImage(guid),
+                        Image = GuidInfo.GetImage(guid)
                     };
                     if(groupItem.Text.IsNullOrWhiteSpace()) groupItem.Text = GuidInfo.GetText(guid);
                     this.AddItem(groupItem);
 
+                    string GetRuleFullRegPath(string regPath)
+                    {
+                        if(string.IsNullOrEmpty(regPath)) regPath = groupItem.TargetPath;
+                        else if(regPath.StartsWith("\\")) regPath = groupItem.TargetPath + regPath;
+                        return regPath;
+                    };
+
                     foreach(XmlElement itemXE in groupXE.ChildNodes)
                     {
                         if(!EnhanceMenusList.JudgeOSVersion(itemXE)) continue;
-                        RegRuleItem.ItemInfo itemInfo = new RegRuleItem.ItemInfo
+                        RuleItem ruleItem;
+                        ItemInfo info = new ItemInfo
                         {
                             Text = itemXE.GetAttribute("Text"),
                             Tip = itemXE.GetAttribute("Tip"),
                             RestartExplorer = itemXE.HasAttribute("RestartExplorer"),
                         };
+                        int defaultValue = 0, maxValue = 0, minValue = 0;
+                        if(itemXE.HasAttribute("IsNumberItem"))
+                        {
+                            XmlElement ruleXE = (XmlElement)itemXE.SelectSingleNode("Rule");
+                            defaultValue = ruleXE.HasAttribute("Default") ? Convert.ToInt32(ruleXE.GetAttribute("Default")) : 0;
+                            maxValue = ruleXE.HasAttribute("Max") ? Convert.ToInt32(ruleXE.GetAttribute("Max")) : int.MaxValue;
+                            minValue = ruleXE.HasAttribute("Min") ? Convert.ToInt32(ruleXE.GetAttribute("Min")) : int.MinValue;
+                        }
 
-                        XmlNodeList ruleXNList = itemXE.GetElementsByTagName("Rule");//Rules
-                        RegRuleItem.RegRule[] rules = new RegRuleItem.RegRule[ruleXNList.Count];
-                        for(int i = 0; i < ruleXNList.Count; i++)
+                        if(isIniGroup)
                         {
-                            XmlElement ruleXE = (XmlElement)ruleXNList[i];
-                            rules[i] = new RegRuleItem.RegRule
+                            XmlElement ruleXE = (XmlElement)itemXE.SelectSingleNode("Rule");
+                            string iniPath = ruleXE.GetAttribute("FilePath");
+                            if(iniPath.IsNullOrWhiteSpace()) iniPath = groupItem.TargetPath;
+                            string section = ruleXE.GetAttribute("Section");
+                            string keyName = ruleXE.GetAttribute("KeyName");
+                            if(itemXE.HasAttribute("IsNumberItem"))
+                            {
+                                var rule = new NumberIniRuleItem.IniRule
+                                {
+                                    IniPath = iniPath,
+                                    Section = section,
+                                    KeyName = keyName,
+                                    DefaultValue = defaultValue,
+                                    MaxValue = maxValue,
+                                    MinValue = maxValue
+                                };
+                                ruleItem = new NumberIniRuleItem(rule, info);
+                            }
+                            else if(itemXE.HasAttribute("IsStringItem"))
+                            {
+                                var rule = new StringIniRuleItem.IniRule
+                                {
+                                    IniPath = iniPath,
+                                    Secation = section,
+                                    KeyName = keyName
+                                };
+                                ruleItem = new StringIniRuleItem(rule, info);
+                            }
+                            else
                             {
-                                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;
+                                var rule = new VisbleIniRuleItem.IniRule
+                                {
+                                    IniPath = iniPath,
+                                    Section = section,
+                                    KeyName = keyName,
+                                    TurnOnValue = ruleXE.HasAttribute("On") ? ruleXE.GetAttribute("On") : null,
+                                    TurnOffValue = ruleXE.HasAttribute("Off") ? ruleXE.GetAttribute("Off") : null,
+                                };
+                                ruleItem = new VisbleIniRuleItem(rule, info);
+                            }
                         }
 
-                        this.AddItem(new RegRuleItem(rules, itemInfo) { FoldGroupItem = groupItem, HasImage = false });
+                        else
+                        {
+                            if(itemXE.HasAttribute("IsNumberItem"))
+                            {
+                                XmlElement ruleXE = (XmlElement)itemXE.SelectSingleNode("Rule");
+                                var rule = new NumberRegRuleItem.RegRule
+                                {
+                                    RegPath = GetRuleFullRegPath(ruleXE.GetAttribute("RegPath")),
+                                    ValueName = ruleXE.GetAttribute("ValueName"),
+                                    ValueKind = GetValueKind(ruleXE.GetAttribute("ValueKind")),
+                                    DefaultValue = defaultValue,
+                                    MaxValue = maxValue,
+                                    MinValue = minValue
+                                };
+                                ruleItem = new NumberRegRuleItem(rule, info);
+                            }
+                            else if(itemXE.HasAttribute("IsStringItem"))
+                            {
+                                XmlElement ruleXE = (XmlElement)itemXE.SelectSingleNode("Rule");
+                                var rule = new StringRegRuleItem.RegRule
+                                {
+                                    RegPath = GetRuleFullRegPath(ruleXE.GetAttribute("RegPath")),
+                                    ValueName = ruleXE.GetAttribute("ValueName"),
+                                };
+                                ruleItem = new StringRegRuleItem(rule, info);
+                            }
+                            else
+                            {
+                                XmlNodeList ruleXNList = itemXE.SelectNodes("Rule");
+                                var rules = new VisibleRegRuleItem.RegRule[ruleXNList.Count];
+                                for(int i = 0; i < ruleXNList.Count; i++)
+                                {
+                                    XmlElement ruleXE = (XmlElement)ruleXNList[i];
+                                    rules[i] = new VisibleRegRuleItem.RegRule
+                                    {
+                                        RegPath = GetRuleFullRegPath(ruleXE.GetAttribute("RegPath")),
+                                        ValueName = ruleXE.GetAttribute("ValueName"),
+                                        ValueKind = GetValueKind(ruleXE.GetAttribute("ValueKind")),
+                                        TurnOnValue = ruleXE.HasAttribute("On") ? ruleXE.GetAttribute("On") : null,
+                                        TurnOffValue = ruleXE.HasAttribute("Off") ? ruleXE.GetAttribute("Off") : null,
+                                    };
+                                }
+                                ruleItem = new VisibleRegRuleItem(rules, info);
+                            }
+                        }
+                        this.AddItem(ruleItem);
+                        ruleItem.HasImage = false;
+                        ruleItem.FoldGroupItem = groupItem;
                     }
                     groupItem.IsFold = true;
                     groupItem.HideWhenNoSubItem();
@@ -76,15 +169,18 @@ namespace ContextMenuManager.Controls
             XmlDocument doc1 = new XmlDocument();
             try
             {
-                if(!File.Exists(AppConfig.WebThirdRulesDic))
+                if(File.Exists(AppConfig.WebThirdRulesDic))
+                {
+                    doc1.LoadXml(File.ReadAllText(AppConfig.WebThirdRulesDic, EncodingType.GetType(AppConfig.WebThirdRulesDic)));
+                }
+                else
                 {
-                    File.WriteAllText(AppConfig.WebThirdRulesDic, Properties.Resources.ThirdRulesDic, Encoding.UTF8);
+                    doc1.LoadXml(Properties.Resources.ThirdRulesDic);
                 }
-                doc1.Load(AppConfig.WebThirdRulesDic);
                 if(File.Exists(AppConfig.UserThirdRulesDic))
                 {
                     XmlDocument doc2 = new XmlDocument();
-                    doc2.Load(AppConfig.UserThirdRulesDic);
+                    doc2.LoadXml(File.ReadAllText(AppConfig.UserThirdRulesDic, EncodingType.GetType(AppConfig.UserThirdRulesDic)));
                     foreach(XmlNode xn in doc2.DocumentElement.ChildNodes)
                     {
                         XmlNode node = doc1.ImportNode(xn, true);

+ 111 - 0
ContextMenuManager/Controls/WinXGroupItem.cs

@@ -0,0 +1,111 @@
+using BluePointLilac.Methods;
+using ContextMenuManager.Controls.Interfaces;
+using System.IO;
+using System.Windows.Forms;
+
+namespace ContextMenuManager.Controls
+{
+    sealed class WinXGroupItem : GroupPathItem, IChkVisibleItem, ITsiDeleteItem, ITsiTextItem
+    {
+        public WinXGroupItem(string groupPath) : base(groupPath, ObjectPath.PathType.Directory)
+        {
+            InitializeComponents();
+            this.TargetPath = groupPath;
+        }
+
+        public new string TargetPath
+        {
+            get => base.TargetPath;
+            set
+            {
+                base.TargetPath = value;
+                this.Text = Path.GetFileNameWithoutExtension(value);
+                this.Image = ResourceIcon.GetFolderIcon(value).ToBitmap();
+                ChkVisible.Checked = this.ItemVisible;
+            }
+        }
+
+        public bool ItemVisible
+        {
+            get => (File.GetAttributes(TargetPath) & FileAttributes.Hidden) != FileAttributes.Hidden;
+            set
+            {
+                FileAttributes attributes = File.GetAttributes(TargetPath);
+                if(value) attributes &= ~FileAttributes.Hidden;
+                else attributes |= FileAttributes.Hidden;
+                File.SetAttributes(TargetPath, attributes);
+                if(Directory.GetFiles(TargetPath).Length > 0) ExplorerRestarter.Show();
+            }
+        }
+
+        public string ItemText
+        {
+            get => Path.GetFileNameWithoutExtension(TargetPath);
+            set
+            {
+                string newPath = $@"{WinXList.WinXPath}\{ObjectPath.RemoveIllegalChars(value)}";
+                Directory.Move(TargetPath, newPath);
+                this.TargetPath = newPath;
+                ExplorerRestarter.Show();
+            }
+        }
+
+        public VisibleCheckBox ChkVisible { get; set; }
+        public DeleteMeMenuItem TsiDeleteMe { get; set; }
+        public ChangeTextMenuItem TsiChangeText { get; set; }
+        readonly ToolStripMenuItem TsiRestoreDefault = new ToolStripMenuItem(AppString.Menu.RestoreDefault);
+
+        private string DefaultGroupPath => $@"{WinXList.DefaultWinXPath}\{ItemText}";
+
+        private void InitializeComponents()
+        {
+            ChkVisible = new VisibleCheckBox(this);
+            this.SetCtrIndex(ChkVisible, 1);
+            TsiDeleteMe = new DeleteMeMenuItem(this);
+            TsiChangeText = new ChangeTextMenuItem(this);
+            this.ContextMenuStrip = new ContextMenuStrip();
+            this.ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText,
+                new ToolStripSeparator(), TsiRestoreDefault, new ToolStripSeparator(), TsiDeleteMe });
+            this.ContextMenuStrip.Opening += (sender, e) => TsiRestoreDefault.Enabled = Directory.Exists(DefaultGroupPath);
+            TsiRestoreDefault.Click += (sender, e) => RestoreDefault();
+        }
+
+        private void RestoreDefault()
+        {
+            if(MessageBoxEx.Show(AppString.MessageBox.RestoreDefault, MessageBoxButtons.OKCancel) == DialogResult.OK)
+            {
+                File.SetAttributes(TargetPath, File.GetAttributes(DefaultGroupPath));
+                string[] paths = Directory.GetFiles(TargetPath);
+                foreach(string path in paths)
+                {
+                    File.Delete(path);
+                }
+                paths = Directory.GetFiles(DefaultGroupPath);
+                foreach(string path in paths)
+                {
+                    File.Copy(path, $@"{TargetPath}\{Path.GetFileName(path)}");
+                }
+                WinXList list = (WinXList)this.Parent;
+                list.ClearItems();
+                list.LoadItems();
+                ExplorerRestarter.Show();
+            }
+        }
+
+        public void DeleteMe()
+        {
+            bool flag = Directory.GetFiles(TargetPath, "*.lnk").Length > 0;
+            if(flag && MessageBoxEx.Show(AppString.MessageBox.DeleteGroup, MessageBoxButtons.OKCancel) != DialogResult.OK) return;
+            File.SetAttributes(TargetPath, FileAttributes.Normal);
+            Directory.Delete(TargetPath, true);
+            if(flag)
+            {
+                WinXList list = (WinXList)this.Parent;
+                list.ClearItems();
+                list.LoadItems();
+                ExplorerRestarter.Show();
+            }
+            else this.Dispose();
+        }
+    }
+}

+ 129 - 23
ContextMenuManager/Controls/WinXItem.cs

@@ -1,5 +1,5 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using ContextMenuManager.Controls.Interfaces;
 using System.Drawing;
 using System.IO;
@@ -7,8 +7,8 @@ using System.Windows.Forms;
 
 namespace ContextMenuManager.Controls
 {
-    sealed class WinXItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem,
-        ITsiTextItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiDeleteItem, IFoldSubItem
+    sealed class WinXItem : MyListItem, IChkVisibleItem, IBtnShowMenuItem, IBtnMoveUpDownItem, ITsiAdministratorItem,
+        ITsiTextItem, ITsiWebSearchItem, ITsiFilePathItem, ITsiDeleteItem, IFoldSubItem, ITsiShortcutCommandItem
     {
         public WinXItem(string filePath, IFoldGroupItem group)
         {
@@ -24,7 +24,7 @@ namespace ContextMenuManager.Controls
             set
             {
                 filePath = value;
-                this.Shortcut.FullName = value;
+                this.Shortcut = new WshShortcut(value);
                 this.Text = this.ItemText;
                 this.Image = this.ItemIcon.ToBitmap();
                 ChkVisible.Checked = this.ItemVisible;
@@ -35,8 +35,8 @@ namespace ContextMenuManager.Controls
         {
             get
             {
-                string name = Shortcut.Description.Trim();
-                if(name == string.Empty) name = WinXList.GetItemText(FilePath);
+                string name = Shortcut.Description?.Trim();
+                if(name.IsNullOrWhiteSpace()) name = DesktopIni.GetLocalizedFileNames(FilePath, true);
                 if(name == string.Empty) name = Path.GetFileNameWithoutExtension(FilePath);
                 return name;
             }
@@ -45,7 +45,7 @@ namespace ContextMenuManager.Controls
                 Shortcut.Description = value;
                 Shortcut.Save();
                 this.Text = ResourceString.GetDirectString(value);
-                ExplorerRestarter.NeedRestart = true;
+                ExplorerRestarter.Show();
             }
         }
 
@@ -58,33 +58,39 @@ namespace ContextMenuManager.Controls
                 if(value) attributes &= ~FileAttributes.Hidden;
                 else attributes |= FileAttributes.Hidden;
                 File.SetAttributes(FilePath, attributes);
-                ExplorerRestarter.NeedRestart = true;
+                ExplorerRestarter.Show();
             }
         }
 
-        private string IconLocation
+        public Icon ItemIcon
         {
             get
             {
-                if(Shortcut.IconLocation.StartsWith(","))
-                    Shortcut.IconLocation = $"{Shortcut.TargetPath}{Shortcut.IconLocation}";
-                return Shortcut.IconLocation;
+                Icon icon = ResourceIcon.GetIcon(Shortcut.IconLocation);
+                if(icon == null)
+                {
+                    string path = ItemFilePath;
+                    if(File.Exists(path)) icon = ResourceIcon.GetExtensionIcon(path);
+                    else if(Directory.Exists(path)) icon = ResourceIcon.GetFolderIcon(path);
+                }
+                return icon;
             }
         }
 
-        private WshShortcut Shortcut = new WshShortcut();
-        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;
+                string path = Shortcut.TargetPath;
+                if(!File.Exists(path) && !Directory.Exists(path)) path = FilePath;
+                return path;
             }
         }
 
+        public WshShortcut Shortcut { get; private set; }
+        public string SearchText => $"{AppString.SideBar.WinX} {Text}";
+        private string FileName => Path.GetFileName(FilePath);
+
         public IFoldGroupItem FoldGroupItem { get; set; }
         public VisibleCheckBox ChkVisible { get; set; }
         public MenuButton BtnShowMenu { get; set; }
@@ -92,29 +98,129 @@ namespace ContextMenuManager.Controls
         public WebSearchMenuItem TsiSearch { get; set; }
         public FilePropertiesMenuItem TsiFileProperties { get; set; }
         public FileLocationMenuItem TsiFileLocation { get; set; }
+        public ShortcutCommandMenuItem TsiChangeCommand { get; set; }
+        public RunAsAdministratorItem TsiAdministrator { get; set; }
         public DeleteMeMenuItem TsiDeleteMe { get; set; }
+        public MoveButton BtnMoveUp { get; set; }
+        public MoveButton BtnMoveDown { get; set; }
+
         readonly ToolStripMenuItem TsiDetails = new ToolStripMenuItem(AppString.Menu.Details);
+        readonly ToolStripMenuItem TsiChangeGroup = new ToolStripMenuItem(AppString.Menu.ChangeGroup);
+
 
         private void InitializeComponents()
         {
             BtnShowMenu = new MenuButton(this);
             ChkVisible = new VisibleCheckBox(this);
+            BtnMoveDown = new MoveButton(this, false);
+            BtnMoveUp = new MoveButton(this, true);
             TsiChangeText = new ChangeTextMenuItem(this);
+            TsiChangeCommand = new ShortcutCommandMenuItem(this);
+            TsiAdministrator = new RunAsAdministratorItem(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 });
+            ContextMenuStrip.Items.AddRange(new ToolStripItem[] { TsiChangeText, new ToolStripSeparator(),
+                TsiChangeGroup, new ToolStripSeparator(), TsiAdministrator, new ToolStripSeparator(),
+                TsiDetails, new ToolStripSeparator(), TsiDeleteMe });
 
             TsiDetails.DropDownItems.AddRange(new ToolStripItem[] { TsiSearch,
-                new ToolStripSeparator(), TsiFileProperties, TsiFileLocation });
+                new ToolStripSeparator(), TsiChangeCommand, TsiFileProperties, TsiFileLocation });
+
+            TsiChangeGroup.Click += (sender, e) => ChangeGroup();
+            BtnMoveDown.MouseDown += (sender, e) => MoveItem(false);
+            BtnMoveUp.MouseDown += (sender, e) => MoveItem(true);
+            TsiAdministrator.Click += (sender, e) => {
+                WinXList.HashLnk(this.FilePath);
+                ExplorerRestarter.Show();
+            };
+            TsiChangeCommand.Click += (sender, e) =>
+            {
+                if(TsiChangeCommand.ChangeCommand(Shortcut))
+                {
+                    Image = ItemIcon.ToBitmap();
+                    WinXList.HashLnk(this.FilePath);
+                }
+            };
+        }
+
+        private void ChangeGroup()
+        {
+            using(SelectDialog dlg = new SelectDialog())
+            {
+                dlg.Title = AppString.Dialog.SelectGroup;
+                dlg.Items = WinXList.GetGroupNames();
+                dlg.Selected = this.FoldGroupItem.Text;
+                if(dlg.ShowDialog() != DialogResult.OK) return;
+                if(dlg.Selected == this.FoldGroupItem.Text) return;
+                string dirPath = $@"{WinXList.WinXPath}\{dlg.Selected}";
+                int count = Directory.GetFiles(dirPath, "*.lnk").Length;
+                string num = (count + 1).ToString().PadLeft(2, '0');
+                string partName = this.FileName;
+                int index = partName.IndexOf(" - ");
+                if(index > 0) partName = partName.Substring(index + 3);
+                string lnkPath = $@"{dirPath}\{num} - {partName}";
+                lnkPath = ObjectPath.GetNewPathWithIndex(lnkPath, ObjectPath.PathType.File);
+                string text = DesktopIni.GetLocalizedFileNames(FilePath);
+                DesktopIni.DeleteLocalizedFileNames(FilePath);
+                if(text != string.Empty) DesktopIni.SetLocalizedFileNames(lnkPath, text);
+                File.Move(FilePath, lnkPath);
+                this.FilePath = lnkPath;
+                WinXList list = (WinXList)this.Parent;
+                list.Controls.Remove(this);
+                for(int i = 0; i < list.Controls.Count; i++)
+                {
+                    if(list.Controls[i] is WinXGroupItem groupItem && groupItem.Text == dlg.Selected)
+                    {
+                        list.Controls.Add(this);
+                        list.SetItemIndex(this, i + 1);
+                        this.Visible = !groupItem.IsFold;
+                        this.FoldGroupItem = groupItem;
+                        break;
+                    }
+                }
+                ExplorerRestarter.Show();
+            }
+        }
+
+        private void MoveItem(bool isUp)
+        {
+            WinXList list = (WinXList)this.Parent;
+            int index = list.Controls.GetChildIndex(this);
+            if(index == list.Controls.Count - 1) return;
+            index += isUp ? -1 : 1;
+            Control ctr = list.Controls[index];
+            if(ctr is WinXGroupItem) return;
+            WinXItem item = (WinXItem)ctr;
+            string name1 = DesktopIni.GetLocalizedFileNames(this.FilePath);
+            string name2 = DesktopIni.GetLocalizedFileNames(item.FilePath);
+            DesktopIni.DeleteLocalizedFileNames(this.FilePath);
+            DesktopIni.DeleteLocalizedFileNames(item.FilePath);
+            string fileName1 = $@"{item.FileName.Substring(0, 2)}{this.FileName.Substring(2)}";
+            string fileName2 = $@"{this.FileName.Substring(0, 2)}{item.FileName.Substring(2)}";
+            string dirPath = Path.GetDirectoryName(this.FilePath);
+            string path1 = $@"{dirPath}\{fileName1}";
+            string path2 = $@"{dirPath}\{fileName2}";
+            path1 = ObjectPath.GetNewPathWithIndex(path1, ObjectPath.PathType.File);
+            path2 = ObjectPath.GetNewPathWithIndex(path2, ObjectPath.PathType.File);
+            File.Move(this.FilePath, path1);
+            File.Move(item.FilePath, path2);
+            if(name1 != string.Empty) DesktopIni.SetLocalizedFileNames(path1, name1);
+            if(name1 != string.Empty) DesktopIni.SetLocalizedFileNames(path2, name2);
+            this.FilePath = path1;
+            item.FilePath = path2;
+            list.SetItemIndex(this, index);
+            ExplorerRestarter.Show();
         }
 
         public void DeleteMe()
         {
-            File.Delete(this.FilePath);
+            File.Delete(FilePath);
+            DesktopIni.DeleteLocalizedFileNames(FilePath);
+            ExplorerRestarter.Show();
+            this.Shortcut.Dispose();
             this.Dispose();
         }
     }

+ 175 - 26
ContextMenuManager/Controls/WinXList.cs

@@ -1,53 +1,202 @@
-using BulePointLilac.Controls;
-using BulePointLilac.Methods;
-using ContextMenuManager.Controls.Interfaces;
+using BluePointLilac.Controls;
+using BluePointLilac.Methods;
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.IO;
+using System.Text;
+using System.Windows.Forms;
 
 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 readonly string DefaultWinXPath = Environment.ExpandEnvironmentVariables(@"%HOMEDRIVE%\Users\Default\AppData\Local\Microsoft\Windows\WinX");
 
-        public static string GetItemText(string filePath)
+        public void LoadItems()
         {
-            string dirPath = Path.GetDirectoryName(filePath);
-            string fileName = Path.GetFileName(filePath);
-            if(DesktopIniReaders.TryGetValue(dirPath, out IniReader reader))
+            if(WindowsOsVersion.ISAfterOrEqual8)
             {
-                string name = reader.GetValue("LocalizedFileNames", fileName);
-                name = ResourceString.GetDirectString(name);
-                return name;
+                this.AddNewItem();
+                this.LoadWinXItems();
             }
-            else return string.Empty;
         }
 
-        public void LoadItems()
+        private void LoadWinXItems()
         {
-            if(WindowsOsVersion.ISAfterOrEqual8)
+            string[] dirPaths = Directory.GetDirectories(WinXPath);
+            Array.Reverse(dirPaths);
+            bool sortable = AppConfig.WinXSortable;
+            bool sorted = false;
+            foreach(string dirPath in dirPaths)
+            {
+                WinXGroupItem groupItem = new WinXGroupItem(dirPath);
+                this.AddItem(groupItem);
+                string[] lnkPaths;
+                if(sortable)
+                {
+                    lnkPaths = GetSortedPaths(dirPath, out bool flag);
+                    if(flag) sorted = true;
+                }
+                else
+                {
+                    lnkPaths = Directory.GetFiles(dirPath, "*.lnk");
+                    Array.Reverse(lnkPaths);
+                }
+                foreach(string path in lnkPaths)
+                {
+                    WinXItem winXItem = new WinXItem(path, groupItem);
+                    winXItem.BtnMoveDown.Visible = winXItem.BtnMoveUp.Visible = sortable;
+                    this.AddItem(winXItem);
+                }
+                groupItem.IsFold = true;
+            }
+            if(sorted)
             {
-                DesktopIniReaders.Clear();
-                Array.ForEach(new DirectoryInfo(WinXPath).GetDirectories(), di => LoadSubDirItems(di));
+                ExplorerRestarter.Show();
+                MessageBoxEx.Show(AppString.MessageBox.WinXSorted);
             }
         }
 
-        private void LoadSubDirItems(DirectoryInfo di)
+        private void AddNewItem()
         {
-            GroupPathItem groupItem = new GroupPathItem(di.FullName, ObjectPath.PathType.Directory)
+            NewItem newItem = new NewItem();
+            this.AddItem(newItem);
+            PictureButton btnCreateDir = new PictureButton(AppImage.NewFolder);
+            MyToolTip.SetToolTip(btnCreateDir, AppString.Tip.CreateGroup);
+            newItem.AddCtr(btnCreateDir);
+            btnCreateDir.MouseDown += (sender, e) => CreateNewGroup();
+            newItem.AddNewItem += (sender, e) =>
             {
-                Text = Path.GetFileNameWithoutExtension(di.FullName),
-                Image = ResourceIcon.GetFolderIcon(di.FullName).ToBitmap()
+                using(NewLnkFileDialog dlg1 = new NewLnkFileDialog())
+                {
+                    if(dlg1.ShowDialog() != DialogResult.OK) return;
+                    using(SelectDialog dlg2 = new SelectDialog())
+                    {
+                        dlg2.Title = AppString.Dialog.SelectGroup;
+                        dlg2.Items = GetGroupNames();
+                        dlg2.Selected = dlg2.Items[0];
+                        if(dlg2.ShowDialog() != DialogResult.OK) return;
+                        string dirPath = $@"{WinXPath}\{dlg2.Selected}";
+                        string extension = Path.GetExtension(dlg1.ItemFilePath).ToLower();
+                        string fileName = Path.GetFileNameWithoutExtension(dlg1.ItemFilePath);
+                        int count = Directory.GetFiles(dirPath, "*.lnk").Length;
+                        string index = (count + 1).ToString().PadLeft(2, '0');
+                        string lnkName = $"{index} - {fileName}.lnk";
+                        string lnkPath = $@"{dirPath}\{lnkName}";
+                        WshShortcut shortcut;
+                        if(extension == ".lnk")
+                        {
+                            File.Copy(dlg1.ItemFilePath, lnkPath);
+                            shortcut = new WshShortcut(lnkPath);
+                        }
+                        else
+                        {
+                            shortcut = new WshShortcut(lnkPath)
+                            {
+                                TargetPath = dlg1.ItemFilePath,
+                                Arguments = dlg1.Arguments,
+                                WorkingDirectory = Path.GetDirectoryName(dlg1.ItemFilePath)
+                            };
+                        }
+                        shortcut.Description = dlg1.ItemText;
+                        shortcut.Save();
+                        shortcut.Dispose();
+                        DesktopIni.SetLocalizedFileNames(lnkPath, dlg1.ItemText);
+                        HashLnk(lnkPath);
+                        foreach(MyListItem ctr in this.Controls)
+                        {
+                            if(ctr is WinXGroupItem groupItem && groupItem.Text == dlg2.Selected)
+                            {
+                                WinXItem item = new WinXItem(lnkPath, groupItem) { Visible = !groupItem.IsFold };
+                                item.BtnMoveDown.Visible = item.BtnMoveUp.Visible = AppConfig.WinXSortable;
+                                this.InsertItem(item, this.GetItemIndex(groupItem) + 1);
+                                break;
+                            }
+                        }
+                        ExplorerRestarter.Show();
+                    }
+                }
             };
-            this.AddItem(groupItem);
-            string iniPath = $@"{di.FullName}\desktop.ini";
-            DesktopIniReaders.Add(di.FullName, new IniReader(iniPath));
-            Array.ForEach(di.GetFiles(), fi =>
+        }
+
+        private void CreateNewGroup()
+        {
+            string dirPath = ObjectPath.GetNewPathWithIndex($@"{WinXPath}\Group", ObjectPath.PathType.Directory, 1);
+            Directory.CreateDirectory(dirPath);
+            string iniPath = $@"{dirPath}\desktop.ini";
+            File.WriteAllText(iniPath, string.Empty, Encoding.Unicode);
+            File.SetAttributes(dirPath, File.GetAttributes(dirPath) | FileAttributes.ReadOnly);
+            File.SetAttributes(iniPath, File.GetAttributes(iniPath) | FileAttributes.Hidden | FileAttributes.System);
+            this.InsertItem(new WinXGroupItem(dirPath), 1);
+        }
+
+        public static void HashLnk(string lnkPath)
+        {
+            using(FileStream fs = new FileStream(AppConfig.HashLnkExePath, FileMode.OpenOrCreate))
             {
-                if(fi.Extension.ToLower() == ".lnk") this.AddItem(new WinXItem(fi.FullName, groupItem));
-            });
+                byte[] buffer;
+                //Any CPU编译条件下,64bit操作系统IntPtr.Size = 8
+                if(IntPtr.Size == 8 && !lnkPath.StartsWith(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%")))
+                {
+                    buffer = Properties.Resources.HashLnk_64;
+                }
+                else
+                {
+                    buffer = Properties.Resources.HashLnk_32;
+                }
+                fs.Write(buffer, 0, buffer.Length);
+            }
+
+            using(Process process = new Process())
+            {
+                process.StartInfo = new ProcessStartInfo
+                {
+                    FileName = AppConfig.HashLnkExePath,
+                    Arguments = $"\"{lnkPath}\"",
+                    UseShellExecute = false,
+                    CreateNoWindow = true,
+                    WindowStyle = ProcessWindowStyle.Hidden
+                };
+                process.Start();
+                process.WaitForExit();
+            }
+        }
+
+        public static string[] GetGroupNames()
+        {
+            List<string> items = new List<string>();
+            DirectoryInfo winxDi = new DirectoryInfo(WinXPath);
+            Array.ForEach(winxDi.GetDirectories(), di => items.Add(di.Name));
+            items.Reverse();
+            return items.ToArray();
+        }
+
+        private static string[] GetSortedPaths(string groupPath, out bool sorted)
+        {
+            sorted = false;
+            List<string> sortedPaths = new List<string>();
+            string[] paths = Directory.GetFiles(groupPath, "*.lnk");
+            for(int i = paths.Length - 1; i >= 0; i--)
+            {
+                string srcPath = paths[i];
+                string name = Path.GetFileName(srcPath);
+                int index = name.IndexOf(" - ");
+                if(index >= 2 && int.TryParse(name.Substring(0, index), out int num) && num == i + 1)
+                {
+                    sortedPaths.Add(srcPath); continue;
+                }
+                string dstPath = $@"{groupPath}\{(i + 1).ToString().PadLeft(2, '0')} - {name.Substring(index + 3)}";
+                dstPath = ObjectPath.GetNewPathWithIndex(dstPath, ObjectPath.PathType.File);
+                string value = DesktopIni.GetLocalizedFileNames(srcPath);
+                DesktopIni.DeleteLocalizedFileNames(srcPath);
+                if(value != string.Empty) DesktopIni.SetLocalizedFileNames(dstPath, value);
+                File.Move(srcPath, dstPath);
+                sortedPaths.Add(dstPath);
+                sorted = true;
+            }
+            return sortedPaths.ToArray();
         }
     }
 }

+ 2 - 2
ContextMenuManager/GuidInfo.cs

@@ -1,4 +1,4 @@
-using BulePointLilac.Methods;
+using BluePointLilac.Methods;
 using ContextMenuManager.Controls;
 using Microsoft.Win32;
 using System;
@@ -27,7 +27,7 @@ namespace ContextMenuManager
         static GuidInfo()
         {
             //将Skype添加到字典
-            Guid skypeGuid = new Guid(RegRuleItem.SkypeGuidStr);
+            Guid skypeGuid = new Guid(VisibleRegRuleItem.SkypeGuid);
             FilePathDic.Add(skypeGuid, null);
             ItemTextDic.Add(skypeGuid, AppString.Item.ShareWithSkype);
             ItemImageDic.Add(skypeGuid, AppImage.Skype);

+ 56 - 52
ContextMenuManager/MainForm.cs

@@ -1,7 +1,9 @@
-using BulePointLilac.Controls;
+using BluePointLilac.Controls;
 using ContextMenuManager.Controls;
+using ContextMenuManager.Controls.Interfaces;
 using System;
 using System.Drawing;
+using System.IO;
 using System.Linq;
 using System.Windows.Forms;
 
@@ -16,21 +18,22 @@ namespace ContextMenuManager
             this.ForeColor = Color.FromArgb(80, 80, 80);
             this.Controls.Add(new ExplorerRestarter());
             appSettingBox.Owner = shellList.Owner = shellNewList.Owner = sendToList.Owner = openWithList.Owner
-                = winXList.Owner = guidBlockedList.Owner = enhanceMenusList.Owner = thirdRuleList.Owner = MainBody;
+                = winXList.Owner = guidBlockedList.Owner = enhanceMenusList.Owner = thirdRuleList.Owner = iEList.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();
-            ((RefreshButton)ToolBarButtons[3]).ClickMe += (sender, e) => SideBar.SelectIndex = SideBar.SelectIndex;
+            ToolBarButtons[3].MouseDown += (sender, e) => SwitchItem();
             ToolBar.AddButtons(ToolBarButtons);
             ToolBar.SelectedIndex = 0;
+            if(AppConfig.ShowFilePath) ShowFilePath();
         }
 
         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 RefreshButton(),//刷新
+            new MyToolBarButton(AppImage.Refresh,AppString.ToolBar.Refresh){ CanBeSelected = false },//刷新
             new MyToolBarButton(AppImage.About, AppString.ToolBar.About)//关于
         };
         readonly ShellList shellList = new ShellList();
@@ -41,6 +44,7 @@ namespace ContextMenuManager
         readonly GuidBlockedList guidBlockedList = new GuidBlockedList();
         readonly EnhanceMenusList enhanceMenusList = new EnhanceMenusList();
         readonly ThirdRulesList thirdRuleList = new ThirdRulesList();
+        readonly IEList iEList = new IEList();
         readonly ReadOnlyRichTextBox aboutMeBox = new ReadOnlyRichTextBox
         {
             Text = AppString.Other.AboutApp
@@ -92,51 +96,43 @@ namespace ContextMenuManager
             AppString.SideBar.UwpLnk,
             AppString.SideBar.ExeFile,
             null,
-            AppString.SideBar.TextFile,
-            AppString.SideBar.DocumentFile,
-            AppString.SideBar.ImageFile,
-            AppString.SideBar.VideoFile,
-            AppString.SideBar.AudioFile,
-            AppString.SideBar.ImageDirectory,
-            AppString.SideBar.VideoDirectory,
-            AppString.SideBar.AudioDirectory,
+            AppString.SideBar.CustomExtension,
+            AppString.SideBar.PerceivedType,
+            AppString.SideBar.DirectoryType,
             null,
-            AppString.SideBar.UnknownType,
-            null,
-            AppString.SideBar.CustomType
+            AppString.SideBar.UnknownType
         };
         static readonly string[] TypeItemInfos = {
             AppString.StatusBar.LnkFile,
             AppString.StatusBar.UwpLnk,
             AppString.StatusBar.ExeFile,
             null,
-            AppString.StatusBar.TextFile,
-            AppString.StatusBar.DocumentFile,
-            AppString.StatusBar.ImageFile,
-            AppString.StatusBar.VideoFile,
-            AppString.StatusBar.AudioFile,
-            AppString.StatusBar.ImageDirectory,
-            AppString.StatusBar.VideoDirectory,
-            AppString.StatusBar.AudioDirectory,
-            null,
-            AppString.StatusBar.UnknownType,
+            AppString.StatusBar.CustomExtension,
+            AppString.StatusBar.PerceivedType,
+            AppString.StatusBar.DirectoryType,
             null,
-            AppString.StatusBar.CustomType
+            AppString.StatusBar.UnknownType
         };
 
         static readonly string[] OtherRuleItems = {
             AppString.SideBar.EnhanceMenu,
             AppString.SideBar.ThirdRules,
             null,
+            AppString.SideBar.DragDrop,
             AppString.SideBar.PublicReferences,
-            AppString.SideBar.GuidBlocked
+            AppString.SideBar.GuidBlocked,
+            null,
+            AppString.SideBar.IEMenu
         };
         static readonly string[] OtherRuleItemInfos = {
             AppString.StatusBar.EnhanceMenu,
             AppString.StatusBar.ThirdRules,
             null,
+            AppString.StatusBar.DragDrop,
             AppString.StatusBar.PublicReferences,
-            AppString.StatusBar.GuidBlocked
+            AppString.StatusBar.GuidBlocked,
+            null,
+            AppString.StatusBar.IEMenu
         };
 
         static readonly string[] AboutItems = {
@@ -167,18 +163,11 @@ namespace ContextMenuManager
             ShellList.Scenes.UwpLnk,
             ShellList.Scenes.ExeFile,
             null,
-            ShellList.Scenes.TextFile,
-            ShellList.Scenes.DocumentFile,
-            ShellList.Scenes.ImageFile,
-            ShellList.Scenes.VideoFile,
-            ShellList.Scenes.AudioFile,
-            ShellList.Scenes.ImageDirectory,
-            ShellList.Scenes.VideoDirectory,
-            ShellList.Scenes.AudioDirectory,
-            null,
-            ShellList.Scenes.UnknownType,
+            ShellList.Scenes.CustomExtension,
+            ShellList.Scenes.PerceivedType,
+            ShellList.Scenes.DirectoryType,
             null,
-            ShellList.Scenes.CustomType
+            ShellList.Scenes.UnknownType
         };
 
         private void HideAllParts()
@@ -245,6 +234,27 @@ namespace ContextMenuManager
             StatusBar.Text = MyStatusBar.DefaultText;
         }
 
+        private void ShowFilePath()
+        {
+            foreach(MyList list in new MyList[] { shellList, shellNewList, sendToList, openWithList, winXList, guidBlockedList, iEList })
+            {
+                list.HoveredItemChanged += (sender, e) =>
+                {
+                    MyListItem item = list.HoveredItem;
+                    if(item is ITsiFilePathItem pathItem)
+                    {
+                        string path = pathItem.ItemFilePath;
+                        if(File.Exists(path)) { StatusBar.Text = path; return; }
+                    }
+                    if(item is GuidBlockedItem guidItem)
+                    {
+                        StatusBar.Text = guidItem.Value; return;
+                    }
+                    StatusBar.Text = item.Text;
+                };
+            }
+        }
+
         private void SwitchGeneralItem()
         {
             switch(SideBar.SelectIndex)
@@ -261,6 +271,7 @@ namespace ContextMenuManager
                     if(SideBar.SelectIndex <= 9)
                     {
                         shellList.Scene = GeneralShellScenes[SideBar.SelectIndex];
+                        shellList.LoadItems();
                         shellList.Visible = true;
                     }
                     break;
@@ -270,6 +281,7 @@ namespace ContextMenuManager
         private void SwitchTypeItem()
         {
             shellList.Scene = (ShellList.Scenes)TypeShellScenes[SideBar.SelectIndex];
+            shellList.LoadItems();
             shellList.Visible = true;
         }
 
@@ -282,9 +294,13 @@ namespace ContextMenuManager
                 case 1:
                     thirdRuleList.LoadItems(); thirdRuleList.Visible = true; break;
                 case 3:
-                    shellList.Scene = ShellList.Scenes.CommandStore; shellList.Visible = true; break;
+                    shellList.Scene = ShellList.Scenes.DragDrop; shellList.LoadItems(); shellList.Visible = true; break;
                 case 4:
+                    shellList.Scene = ShellList.Scenes.CommandStore; shellList.LoadItems(); shellList.Visible = true; break;
+                case 5:
                     guidBlockedList.LoadItems(); guidBlockedList.Visible = true; break;
+                case 7:
+                    iEList.LoadItems(); iEList.Visible = true; break;
             }
         }
 
@@ -320,17 +336,5 @@ namespace ContextMenuManager
             Array.ForEach(strs, str => maxWidth = Math.Max(maxWidth, SideBar.GetItemWidth(str)));
             SideBar.Width = maxWidth;
         }
-
-        sealed class RefreshButton : MyToolBarButton
-        {
-            public RefreshButton() : base(AppImage.Refresh, AppString.ToolBar.Refresh) { }
-
-            public EventHandler ClickMe;
-
-            protected override void OnMouseDown(MouseEventArgs e)
-            {
-                ClickMe?.Invoke(null, null);
-            }
-        }
     }
 }

+ 5 - 2
ContextMenuManager/Program.cs

@@ -1,4 +1,5 @@
-using System;
+using BluePointLilac.Methods;
+using System;
 using System.Windows.Forms;
 
 namespace ContextMenuManager
@@ -12,8 +13,10 @@ namespace ContextMenuManager
     static class Program
     {
         [STAThread]
-        static void Main()
+        static void Main(string[] args)
         {
+            bool isRestart = args.Length > 0 && args[0] == "Restart";
+            if(!isRestart && SingleInstance.IsRunning()) return;
             Updater.PeriodicUpdate();
             Application.EnableVisualStyles();
             Application.SetCompatibleTextRenderingDefault(false);

+ 3 - 3
ContextMenuManager/Properties/AssemblyInfo.cs

@@ -6,10 +6,10 @@ using System.Runtime.InteropServices;
 [assembly: AssemblyConfiguration("")]
 [assembly: AssemblyCompany("蓝点llilac")]
 [assembly: AssemblyProduct("Windows右键管理")]
-[assembly: AssemblyCopyright("Copyright @ 2020 蓝点lilac")]
+[assembly: AssemblyCopyright("Copyright @ 2020-2021 蓝点lilac")]
 [assembly: AssemblyTrademark("蓝点llilac")]
 [assembly: AssemblyCulture("")]
 [assembly: ComVisible(false)]
 [assembly: Guid("35190ec1-2515-488d-a2e9-825d6ff67aa2")]
-[assembly: AssemblyVersion("2.2.0.0")]
-[assembly: AssemblyFileVersion("2.2.0.0")]
+[assembly: AssemblyVersion("3.0.0.0")]
+[assembly: AssemblyFileVersion("3.0.0.0")]

+ 52 - 33
ContextMenuManager/Properties/Resources.Designer.cs

@@ -19,7 +19,7 @@ namespace ContextMenuManager.Properties {
     // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
     // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
     // (以 /str 作为命令选项),或重新生成 VS 项目。
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+    [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 {
@@ -47,8 +47,8 @@ namespace ContextMenuManager.Properties {
         }
         
         /// <summary>
-        ///   重写当前线程的 CurrentUICulture 属性
-        ///   重写当前线程的 CurrentUICulture 属性
+        ///   重写当前线程的 CurrentUICulture 属性,对
+        ///   使用此强类型资源类的所有资源查找执行重写。
         /// </summary>
         [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
         internal static global::System.Globalization.CultureInfo Culture {
@@ -101,10 +101,10 @@ namespace ContextMenuManager.Properties {
         }
         
         /// <summary>
-        ///   查找类似 ;此文件为ContextMenuManager程序的显示文本字典
-        ///;如果你想要帮助作者为此程序添加其他语言字典, 可以修改此文本并保存在.\config\languages文件夹内, 
-        ///;比如美国英语字典保存为en-US.ini, 并给[General]\Language赋值 en-US English
-        ///;可以在Github或Gitee上Fork该项目并提交申请给我,或者直接发送文件到邮箱[email protected]
+        ///   查找类似 ;此文件为 ContextMenuManager Windows右键管理程序 的显示文本字典
+        ///;翻译:可帮助作者为此程序提供翻译并提交到Github,以下内容中等号右侧内容替换为翻译文本,
+        ///;General-Translator为翻译贡献者,General-Language为语言名称,如en-US 美国英语
+        ///;翻译文件保存在Config\languages目录中,文件名保存为en-US.ini
         ///;翻译说明:暂时不翻译的值保留为空即可,字典内赋值换行请使用\n进行转义
         ///
         ///[General]
@@ -125,7 +125,8 @@ namespace ContextMenuManager.Properties {
         ///Directory = 目录
         ///Background = 目录背景
         ///Desktop = 桌面背景
-        ///Drive = 磁盘 [字符串的其余部分被截断]&quot;; 的本地化字符串。
+        ///Drive = 磁盘分区
+        ///AllObjects  [字符串的其余部分被截断]&quot;; 的本地化字符串。
         /// </summary>
         internal static string AppLanguageDic {
             get {
@@ -136,9 +137,9 @@ namespace ContextMenuManager.Properties {
         /// <summary>
         ///   查找 System.Drawing.Bitmap 类型的本地化资源。
         /// </summary>
-        internal static System.Drawing.Bitmap CustomType {
+        internal static System.Drawing.Bitmap Custom {
             get {
-                object obj = ResourceManager.GetObject("CustomType", resourceCulture);
+                object obj = ResourceManager.GetObject("Custom", resourceCulture);
                 return ((System.Drawing.Bitmap)(obj));
             }
         }
@@ -167,16 +168,14 @@ namespace ContextMenuManager.Properties {
         ///   查找类似 &lt;?xml version=&apos;1.0&apos; encoding=&apos;utf-8&apos; ?&gt;
         ///&lt;!--此文件为常用右键菜单字典,
         ///Tip属性为鼠标悬浮在开关上时的提示信息,从每个Item节点开始, 子元素Value表示该项的注册表键值,目前仅支持REG_SZ、REG_DWORD、REG_EXPAND_SZ的键值类型,
-        ///子元素SubKey的所有子元素是该项的子项,项名即为元素名; 每一Item项和SubKey的所有子元素的属性Default为该注册表项默认值,不放在Value\REG_SZ元素里面是为了防止与可能存在的键名为Default的键产生冲突--&gt;
+        ///子元素SubKey的所有子元素是该项的子项,项名即为元素名; 每一Item项和SubKey的所有子元素的属性Default为该注册表项默认值,不放在Value\REG_SZ元素里面是为了防止与可能存在的键名为Default的键产生冲突
+        ///由于Shell项太过复杂,程序只根据注册表项名判断存在即启用,故同一场景下不允许有相同KeyName属性的Shell项目,ShellEx项只要Guid符合则为启用--&gt;
         ///&lt;Data&gt;
         ///  &lt;File&gt;
         ///    &lt;Shell&gt;
-        ///      &lt;Item KeyName=&apos;CopyAsPath&apos; Tip=&apos;系统原生菜单项需按住Shift显示,&amp;#x000A;此项可以直接显示&apos;&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;imageres.dll,-5302&apos;/&gt;
-        ///        &lt;/Value&gt;
-        ///        &lt;SubKey&gt;
-        ///           [字符串的其余部分被截断]&quot;; 的本地化字符串。
+        ///          &lt;REG [字符串的其余部分被截断]&quot;; 的本地化字符串。
         /// </summary>
         internal static string EnhanceMenusDic {
             get {
@@ -213,6 +212,26 @@ namespace ContextMenuManager.Properties {
             }
         }
         
+        /// <summary>
+        ///   查找 System.Byte[] 类型的本地化资源。
+        /// </summary>
+        internal static byte[] HashLnk_32 {
+            get {
+                object obj = ResourceManager.GetObject("HashLnk_32", resourceCulture);
+                return ((byte[])(obj));
+            }
+        }
+        
+        /// <summary>
+        ///   查找 System.Byte[] 类型的本地化资源。
+        /// </summary>
+        internal static byte[] HashLnk_64 {
+            get {
+                object obj = ResourceManager.GetObject("HashLnk_64", resourceCulture);
+                return ((byte[])(obj));
+            }
+        }
+        
         /// <summary>
         ///   查找 System.Drawing.Bitmap 类型的本地化资源。
         /// </summary>
@@ -233,6 +252,16 @@ namespace ContextMenuManager.Properties {
             }
         }
         
+        /// <summary>
+        ///   查找 System.Drawing.Bitmap 类型的本地化资源。
+        /// </summary>
+        internal static System.Drawing.Bitmap NewFolder {
+            get {
+                object obj = ResourceManager.GetObject("NewFolder", resourceCulture);
+                return ((System.Drawing.Bitmap)(obj));
+            }
+        }
+        
         /// <summary>
         ///   查找 System.Drawing.Bitmap 类型的本地化资源。
         /// </summary>
@@ -266,9 +295,9 @@ namespace ContextMenuManager.Properties {
         /// <summary>
         ///   查找 System.Drawing.Bitmap 类型的本地化资源。
         /// </summary>
-        internal static System.Drawing.Bitmap SeparatorItem {
+        internal static System.Drawing.Bitmap Select {
             get {
-                object obj = ResourceManager.GetObject("SeparatorItem", resourceCulture);
+                object obj = ResourceManager.GetObject("Select", resourceCulture);
                 return ((System.Drawing.Bitmap)(obj));
             }
         }
@@ -314,13 +343,13 @@ namespace ContextMenuManager.Properties {
         }
         
         /// <summary>
-        ///   查找类似 &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
+        ///   查找类似 &lt;?xml version=&apos;1.0&apos; encoding=&apos;utf-8&apos; ?&gt;
         ///&lt;!--每个程序为一个Group,Text为Group项显示文本,Guid用于判断用户是否安装此程序并决定是否显示该Group,不设置Guid则为常驻菜单,RegPath为程序相关注册表主路径;
         ///其相关菜单项目设置作为一个Item子元素,Item的Text为该Item项显示文本,Tip属性为鼠标悬浮在开关上时的提示信息,需要重启资源管理器生效则添加属性RestartExplorer;
-        ///Item的子元素Rule为相关注册表内容,RegPath省略则默认为Group主路径,以\开头则为Group主路径的子项路径;ValueName为相关键名,On为启用键值,Off为禁用键值;
-        ///每个Item可能受多个注册表Rule影响,按照顺序进行键值判定;程序优先判定为On,即只要所有Rule不匹配Off键值就判定为On,键值类型不符时也判定为On;
-        ///ValueKind为键值类型,默认键值类型ValueKind为REG_DWORD,为默认值时可省略,目前仅支持REG_SZ、REG_DWORD、REG_EXPAND_SZ的键值类型--&gt;
-        ///&lt; [字符串的其余部分被截断]&quot;; 的本地化字符串。
+        ///Item的子元素Rule为相关注册表内容,RegPath省略则默认为Group主路径,以\开头则为Group主路径的子项路径;
+        ///ValueName为相关键名,On为启用键值,Off为禁用键值;不设置On或Off属性时,其值为null,对应注册表键值不存在;
+        ///每个Item可能受多个注册表Rule影响,按照顺序进行键值判定;判定规则:当有多条规则时,前面的规则注册表键值匹配On则为On,匹配Off则为Off,并终止判断,都不匹配时继续往下判断,若所有规则都不匹配则为On
+        ///ValueKind为键值类型,默认键值类型ValueKind为REG_D [字符串的其余部分被截断]&quot;; 的本地化字符串。
         /// </summary>
         internal static string ThirdRulesDic {
             get {
@@ -358,16 +387,6 @@ namespace ContextMenuManager.Properties {
             }
         }
         
-        /// <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>

+ 13 - 7
ContextMenuManager/Properties/Resources.resx

@@ -133,8 +133,8 @@
   <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 name="Custom" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\custom.png;System.Drawing.Bitmap, System.Drawing, Version=2.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>
@@ -148,12 +148,21 @@
   <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="HashLnk_32" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\hashlnk\hashlnk_32.exe;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="HashLnk_64" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\hashlnk\hashlnk_64.exe;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</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="NewFolder" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\newfolder.png;System.Drawing.Bitmap, System.Drawing, Version=2.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>
@@ -163,8 +172,8 @@
   <data name="Refresh" type="System.Resources.ResXFileRef, System.Windows.Forms">
     <value>resources\images\refresh.png;System.Drawing.Bitmap, System.Drawing, Version=2.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 name="Select" type="System.Resources.ResXFileRef, System.Windows.Forms">
+    <value>resources\images\select.png;System.Drawing.Bitmap, System.Drawing, Version=2.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>
@@ -190,9 +199,6 @@
   <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>

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно