├── Resource └── menu.png ├── MenuFunctions ├── MenuFunctions │ ├── key.snk │ ├── packages.config │ ├── Properties │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── MenuFunctions.csproj │ └── ExplorerHelper.cs ├── 实现运行效果 │ ├── packages.config │ ├── App.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── 实现运行效果.csproj │ ├── ExplorerHelper.cs │ └── Program.cs ├── 按键测试2 │ ├── packages.config │ ├── App.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── 按键测试2.csproj │ └── Program.cs ├── MenuFunctions_Panel │ ├── packages.config │ ├── App.config │ ├── Properties │ │ ├── Settings.settings │ │ ├── Settings.Designer.cs │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── App.xaml.cs │ ├── App.xaml │ ├── 项目说明.txt │ ├── README.md │ ├── 快速入门.md │ ├── 示例配置.json │ ├── MenuFunctions_Panel.csproj │ ├── Models │ │ └── MenuItemConfig.cs │ ├── MainWindow.xaml.cs │ ├── ViewModels │ │ └── MainViewModel.cs │ └── MainWindow.xaml ├── 按键测试 │ ├── App.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── 按键测试.csproj │ └── Program.cs ├── 测试 │ ├── App.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Program.cs │ ├── 测试.csproj │ ├── ExplorerHelper.cs │ └── RunProgram.cs ├── 测试复选框 │ ├── App.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── 测试复选框.csproj │ └── Program.cs ├── 测试输入控件 │ ├── App.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── 测试输入控件.csproj │ └── Program.cs ├── 卸载计算机全部sharpshelldll.ps1 └── MenuFunctions.sln ├── .gitignore └── README.md /Resource/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlyclxy/MenuFunctions/main/Resource/menu.png -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onlyclxy/MenuFunctions/main/MenuFunctions/MenuFunctions/key.snk -------------------------------------------------------------------------------- /MenuFunctions/实现运行效果/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /MenuFunctions/按键测试2/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MenuFunctions/按键测试/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MenuFunctions/测试/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MenuFunctions/实现运行效果/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MenuFunctions/按键测试2/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MenuFunctions/测试复选框/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MenuFunctions/测试输入控件/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 忽略所有 .exe 文件 2 | *.exe 3 | *.rar 4 | *.zip 5 | *.7z 6 | *.dll 7 | git.txt 8 | 9 | 10 | # 忽略 Visual Studio 临时文件和用户特定文件 11 | *.user 12 | *.suo 13 | .vs/ 14 | 15 | # 忽略 NuGet Packages 16 | packages/ 17 | 18 | # 忽略 bin 目录 19 | bin/ 20 | obj/ 21 | Debug/ 22 | Release/ 23 | MenuFunctions_StableVersion/ 24 | 25 | 26 | # 更多忽略规则... 27 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace MenuFunctions_Panel 10 | { 11 | /// 12 | /// App.xaml 的交互逻辑 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/App.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /MenuFunctions/测试复选框/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("测试复选框")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("测试复选框")] 13 | [assembly: AssemblyCopyright("Copyright © 2024")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("436a0c07-a5a9-49a0-82ef-13fd9817ce6a")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /MenuFunctions/测试/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("测试")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("h3d.com")] 12 | [assembly: AssemblyProduct("测试")] 13 | [assembly: AssemblyCopyright("Copyright © h3d.com 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("4e0da51c-ece6-40d6-a199-c2beb44e8b15")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /MenuFunctions/测试输入控件/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("测试输入控件")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("测试输入控件")] 13 | [assembly: AssemblyCopyright("Copyright © 2024")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("91648df0-52ef-499c-b21e-fc312be51449")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /MenuFunctions/按键测试/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("按键测试")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("h3d.com")] 12 | [assembly: AssemblyProduct("按键测试")] 13 | [assembly: AssemblyCopyright("Copyright © h3d.com 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("ab5c5856-cd60-4994-b75f-01542d99f05d")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /MenuFunctions/实现运行效果/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("实现运行效果")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("h3d.com")] 12 | [assembly: AssemblyProduct("实现运行效果")] 13 | [assembly: AssemblyCopyright("Copyright © h3d.com 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("81868e36-4bff-43ef-9010-e436789b19ee")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /MenuFunctions/按键测试2/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("按键测试2")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("h3d.com")] 12 | [assembly: AssemblyProduct("按键测试2")] 13 | [assembly: AssemblyCopyright("Copyright © h3d.com 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("0ccad1a0-93f4-468f-a1e2-03e3c6c1bc08")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("AuroraContextMenu")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("AuroraContextMenu")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | //将 ComVisible 设置为 false 将使此程序集中的类型 18 | //对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(true)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("e117fbc0-7ab4-4c15-a4a9-646e97854821")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, 33 | // 方法是按如下所示使用“*”: : 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /MenuFunctions/测试/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace 测试 9 | { 10 | internal class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | 15 | 16 | string customParameter = ""; // 预留的自定义参数 17 | // 循环接受输入并处理 18 | while (true) 19 | { 20 | Console.WriteLine("请输入命令(输入'exit'退出):"); 21 | string input = Console.ReadLine(); 22 | 23 | if (input.ToLower() == "exit") 24 | { 25 | break; 26 | } 27 | 28 | 29 | try 30 | { 31 | Process.Start(input); 32 | } 33 | catch (Exception ex) 34 | { 35 | Console.WriteLine("直接执行命令时发生错误: " + ex.Message); 36 | Console.WriteLine("尝试通过cmd.exe执行..."); 37 | 38 | RunProgram.ProcessInputCommand(input, customParameter); 39 | } 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace MenuFunctions_Panel.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 功能介绍: 2 | 可以分别设置显示在文件右键菜单,目录菜单,文件夹背景菜单 3 | 可直接运行程序,路径,网址,cmd命令 4 | 传递多路径到同一程序 5 | 图标可以设置常见图片格式,ico,png,gif,jpg,bmp,exe,msi,msc,com,lnk,网址 6 | 自定义传参,比如设置一个参数-k 这个可以分别传给所有的地址, 也可以附加在所有地址的最后面 7 | 可以设定按照固定的扩展名来显示菜单 8 | 多层级菜单,或者置顶到一级菜单 9 | 10 | 扩展功能 11 | 复制路径到剪贴板功能 12 | 打开所在文件夹并且选中文件或目录 (这个本来用explorer select可以实现,但是这个启动新进程会特别慢.所以增加这个功能.) 13 | 14 | 15 | ![示例图片](Resource/menu.png) 16 | 17 | 18 | 19 | 第一次运行:注册dll,会将MenuFunctions.dll注册到系统,启用右键菜单. 20 | MenuConfig.json是配置文件,不可删.修改里面配置会实时生效. 21 | 建议你电脑装个notepad3.这样如果你配置写错了.他会标记错误.而普通记事本你配置写错了也不知道错哪了 22 | 如果要卸载的话,点击卸载dll. 23 | 注意这个路径你注册后不可改位置.别到时候给文件夹移动走了.. 那没准右键菜单就直接报错了..可以先卸载,再挪文件夹位置. 然后再注册 24 | 注意这个东西有固态盘请一定放固态盘. 包括图标用的图标,要不然每次这个右键都会读硬盘.读配置文件和图片. 你放普通硬盘慢的话,没准会卡菜单.. 当然只是没准. 其实配置文件现在30多kb. 读个30kb我觉得还是轻轻松松..要是30kb读着都慢.那可能是硬盘坏了.. 25 | 文件夹内任何文件都不可删除. 26 | 27 | 更新 2023-12-7 28 | 1 路径现在改到添加到所有参数的后面,这样很多命令就正常了. 比如python.exe 显示路径.py 选中路径 这种好几个叠起来的就可以正常运行了. 29 | 2.图标现在除了常用图片外,改为获取全部格式的ico图标.就比如你见一个文件是json图标挺好看,你可以把这个json文件的路径填进去,然后就变成了json的图标.. 30 | 3.增加使用cmd运行命令.这样可以直接在ProgramPath输入命令从而用cmd执行 31 | 4.cmd命令的一些拓展,可以隐藏cmd窗口启动,或者保持cmd不关闭.方便各种用途.比如你给文件设置隐藏属性,可能就不需要显示cmd窗口. 比如你要ping ip,就需要保持窗口不要一闪而过. 32 | 5.增加菜单分割线 33 | 6.增加自检弹窗. 这个可以检测实际运行的命令和路径是否符合预期. 34 | 7.增加菜单文本动态显示路径功能.就是那个压缩包.你选中文件点压缩包,会直接显示解压到xxx,或者压缩到xxx. 这个,可以把路径,或者文件名等传给菜单 35 | 36 | 这个配置文件模板 放到了原始Json文件备份里,名字是MenuConfig_new.json,有兴趣可以改名用这个试试 37 | 另外查了查GPT说是win11的多标签获取路径好像弄不了,先这样把.测试这个测试的我要吐啊... -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/项目说明.txt: -------------------------------------------------------------------------------- 1 | ═══════════════════════════════════════════════════════════ 2 | 右键菜单配置编辑器 - MenuFunctions_Panel 3 | ═══════════════════════════════════════════════════════════ 4 | 5 | 【项目简介】 6 | 这是一个 WPF 可视化工具,用于编辑 SharpShell 右键菜单的 JSON 配置文件。 7 | 替代手动编写 JSON,提供类似 PC Quicker 的友好编辑界面。 8 | 9 | 【主要文件】 10 | ├─ MenuFunctions_Panel.exe 主程序 11 | ├─ Newtonsoft.Json.dll JSON 处理库 12 | ├─ README.md 完整功能说明 13 | ├─ 快速入门.md 快速上手指南 14 | └─ 示例配置.json 示例配置文件 15 | 16 | 【技术架构】 17 | - 框架:WPF + .NET Framework 4.8 18 | - 模式:MVVM (Model-View-ViewModel) 19 | - 依赖:Newtonsoft.Json 13.0.3 20 | 21 | 【核心功能】 22 | ✓ 可视化编辑 JSON 配置 23 | ✓ 树形显示菜单结构 24 | ✓ 支持多级子菜单 25 | ✓ 拖拽式排序(上移/下移) 26 | ✓ 文件浏览器集成 27 | ✓ 自动加载配置 28 | ✓ 实时预览和保存 29 | 30 | 【界面特色】 31 | 参考 PC Quicker 风格: 32 | - 左侧:树形菜单结构 33 | - 右侧:详细配置面板(分组折叠) 34 | - 顶部:工具栏(常用操作) 35 | - 底部:状态栏(当前文件) 36 | 37 | 【配置项支持】 38 | 完整支持 MenuItemConfig 的所有属性: 39 | □ 基本信息:文本、可见性、排序 40 | □ 显示位置:文件/文件夹/背景/根菜单 41 | □ 程序设置:路径、参数、图标 42 | □ 文件类型:扩展名过滤 43 | □ 高级选项:引号、参数方式 44 | □ CMD 设置:隐藏窗口、保持打开 45 | 46 | 【使用流程】 47 | 1. 运行程序 → 自动加载或手动打开配置 48 | 2. 编辑配置 → 使用图形界面修改 49 | 3. 保存文件 → 生成格式化的 JSON 50 | 4. 应用配置 → 复制到 DLL 目录,重启资源管理器 51 | 52 | 【开发信息】 53 | 开发日期:2025-11-01 54 | 开发框架:Visual Studio 2022 55 | 目标平台:Windows 10/11 56 | 项目类型:WPF Application 57 | 58 | 【相关文档】 59 | - README.md:详细功能说明和技术文档 60 | - 快速入门.md:5分钟上手指南 61 | - 示例配置.json:参考示例 62 | 63 | ═══════════════════════════════════════════════════════════ 64 | 如有问题,请参考文档或查看源代码 65 | ═══════════════════════════════════════════════════════════ 66 | 67 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // 有关程序集的一般信息由以下 8 | // 控制。更改这些特性值可修改 9 | // 与程序集关联的信息。 10 | [assembly: AssemblyTitle("MenuFunctions_Panel")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("MenuFunctions_Panel")] 15 | [assembly: AssemblyCopyright("Copyright © 2025")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // 将 ComVisible 设置为 false 会使此程序集中的类型 20 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 21 | //请将此类型的 ComVisible 特性设置为 true。 22 | [assembly: ComVisible(false)] 23 | 24 | //若要开始生成可本地化的应用程序,请设置 25 | //.csproj 文件中的 CultureYouAreCodingWith 26 | //在 中。例如,如果你使用的是美国英语。 27 | //使用的是美国英语,请将 设置为 en-US。 然后取消 28 | //对以下 NeutralResourceLanguage 特性的注释。 更新 29 | //以下行中的“en-US”以匹配项目文件中的 UICulture 设置。 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //主题特定资源词典所处位置 36 | //(未在页面中找到资源时使用, 37 | //或应用程序资源字典中找到时使用) 38 | ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置 39 | //(未在页面中找到资源时使用, 40 | //、应用程序或任何主题专用资源字典中找到时使用) 41 | )] 42 | 43 | 44 | // 程序集的版本信息由下列四个值组成: 45 | // 46 | // 主版本 47 | // 次版本 48 | // 生成号 49 | // 修订号 50 | // 51 | [assembly: AssemblyVersion("1.0.0.0")] 52 | [assembly: AssemblyFileVersion("1.0.0.0")] 53 | -------------------------------------------------------------------------------- /MenuFunctions/测试复选框/测试复选框.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {436A0C07-A5A9-49A0-82EF-13FD9817CE6A} 8 | Exe 9 | 测试复选框 10 | 测试复选框 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /MenuFunctions/测试输入控件/测试输入控件.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {91648DF0-52EF-499C-B21E-FC312BE51449} 8 | Exe 9 | 测试输入控件 10 | 测试输入控件 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /MenuFunctions/按键测试/按键测试.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {AB5C5856-CD60-4994-B75F-01542D99F05D} 8 | Exe 9 | 按键测试 10 | 按键测试 11 | v4.8 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本:4.0.30319.42000 5 | // 6 | // 对此文件的更改可能会导致不正确的行为,并且如果 7 | // 重新生成代码,这些更改将会丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace MenuFunctions.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// 一个强类型的资源类,用于查找本地化的字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// 返回此类使用的缓存的 ResourceManager 实例。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MenuFunctions.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 重写当前线程的 CurrentUICulture 属性,对 51 | /// 使用此强类型资源类的所有资源查找执行重写。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // 此代码由工具生成。 4 | // 运行时版本: 4.0.30319.42000 5 | // 6 | // 对此文件的更改可能导致不正确的行为,如果 7 | // 重新生成代码,则所做更改将丢失。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace MenuFunctions_Panel.Properties 12 | { 13 | 14 | 15 | /// 16 | /// 强类型资源类,用于查找本地化字符串等。 17 | /// 18 | // 此类是由 StronglyTypedResourceBuilder 19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen 21 | // (以 /str 作为命令选项),或重新生成 VS 项目。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// 返回此类使用的缓存 ResourceManager 实例。 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MenuFunctions_Panel.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// 重写当前线程的 CurrentUICulture 属性,对 56 | /// 使用此强类型资源类的所有资源查找执行重写。 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /MenuFunctions/按键测试2/按键测试2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {0CCAD1A0-93F4-468F-A1E2-03E3C6C1BC08} 8 | Exe 9 | 按键测试2 10 | 按键测试2 11 | v4.8 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | ..\packages\InputSimulatorCore.1.0.5\lib\netstandard2.0\WindowsInput.dll 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /MenuFunctions/测试/测试.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {4E0DA51C-ECE6-40D6-A199-C2BEB44E8B15} 8 | Exe 9 | 测试 10 | 测试 11 | v4.8 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B} 57 | 1 58 | 0 59 | 0 60 | tlbimp 61 | False 62 | True 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/README.md: -------------------------------------------------------------------------------- 1 | # 右键菜单配置编辑器 2 | 3 | 这是一个用于可视化编辑 SharpShell 右键菜单配置的 WPF 工具。 4 | 5 | ## 功能特性 6 | 7 | - ✅ **可视化编辑**:通过图形界面编辑 JSON 配置,无需手动编写 8 | - ✅ **树形结构**:清晰显示菜单层级结构,支持子菜单 9 | - ✅ **拖拽排序**:使用上移/下移按钮调整菜单项顺序 10 | - ✅ **完整配置**:支持所有配置选项,包括显示位置、CMD 设置等 11 | - ✅ **文件浏览**:集成文件浏览器,方便选择程序和图标 12 | - ✅ **自动加载**:启动时自动查找并加载配置文件 13 | 14 | ## 界面布局 15 | 16 | ### 顶部工具栏 17 | - 📂 **打开**:打开现有的 MenuConfig.json 文件 18 | - 💾 **保存**:保存配置到文件 19 | - ➕ **新增**:添加根菜单项 20 | - 📑 **添加子项**:为选中项添加子菜单 21 | - ➖ **分隔线**:添加菜单分隔线 22 | - 🗑️ **删除**:删除选中的菜单项 23 | - ⬆️ **上移** / ⬇️ **下移**:调整菜单项顺序 24 | 25 | ### 左侧面板 - 菜单结构 26 | 显示所有菜单项的树形结构,支持: 27 | - 查看菜单层级关系 28 | - 选择要编辑的菜单项 29 | - 查看菜单项状态(分隔线、隐藏等) 30 | 31 | ### 右侧面板 - 详细配置 32 | 分为多个可折叠区域: 33 | 34 | #### 📌 基本信息 35 | - **菜单文本**:显示在右键菜单中的文字(支持动态变量) 36 | - **是否为分隔线**:设置为菜单分隔线 37 | - **是否可见**:控制菜单项的显示/隐藏 38 | - **排序序号**:菜单项的排序值 39 | 40 | #### 📍 显示位置 41 | - **显示在根菜单**:直接显示在右键菜单根部 42 | - **显示在文件上**:右键点击文件时显示 43 | - **显示在文件夹上**:右键点击文件夹时显示 44 | - **显示在文件夹背景**:在文件夹空白处右键时显示 45 | 46 | #### ⚙️ 程序设置 47 | - **程序路径**:要执行的程序(支持预设命令如 `copypath`) 48 | - **命令参数**:传递给程序的参数 49 | - **图标路径**:菜单项图标(支持 .ico、.png、.exe 等) 50 | 51 | #### 📄 文件类型过滤 52 | - 指定适用的文件扩展名(如 `.txt, .doc`) 53 | - 留空表示适用于所有文件 54 | 55 | #### 🔧 高级选项 56 | - **参数跟在每条路径后面**:控制参数的拼接方式 57 | - **路径使用引号**:为文件路径添加双引号 58 | - **只使用程序路径**:不传递文件路径,只运行程序 59 | - **添加文件类型参数**:在末尾添加 `&file` 或 `&folder` 标识 60 | - **显示完整路径和命令**:调试模式,执行前显示完整命令 61 | 62 | #### 💻 CMD 设置 63 | - **通过 CMD 运行程序**:使用 cmd.exe 执行命令 64 | - **隐藏 CMD 窗口**:不显示命令行窗口 65 | - **保持 CMD 窗口打开**:命令执行后保持窗口(/K 参数) 66 | 67 | ## 动态变量 68 | 69 | 在"菜单文本"中可以使用以下动态变量: 70 | 71 | - `${filename}` - 文件名(无扩展名) 72 | - `${filename-single}` - 第一个文件名 73 | - `${extension}` - 文件扩展名 74 | - `${extension-single}` - 第一个文件的扩展名 75 | - `${parentdir}` - 父目录路径 76 | - `${parentdir-single}` - 第一个文件的父目录 77 | - `${fullpath}` - 完整路径 78 | - `${fullpath-single}` - 第一个文件的完整路径 79 | 80 | 示例:`复制 ${filename} 到剪贴板` 81 | 82 | ## 预设命令 83 | 84 | 在"程序路径"中可以使用预设命令: 85 | 86 | - `copypath` - 复制路径到剪贴板 87 | - `openfolderandselectitems` - 打开文件夹并选中项目 88 | - `messagebox.show` - 显示消息框 89 | 90 | ## 使用流程 91 | 92 | 1. **打开配置**: 93 | - 点击"打开"按钮选择 `MenuConfig.json` 94 | - 或将配置文件放在程序同目录,自动加载 95 | 96 | 2. **编辑菜单**: 97 | - 点击"新增"添加新菜单项 98 | - 在左侧树中选择要编辑的项 99 | - 在右侧面板修改配置 100 | - 使用"添加子项"创建子菜单 101 | 102 | 3. **调整顺序**: 103 | - 选中要移动的菜单项 104 | - 点击"上移"或"下移"按钮 105 | 106 | 4. **保存配置**: 107 | - 点击"保存"按钮 108 | - 配置会保存为格式化的 JSON 文件 109 | 110 | 5. **应用配置**: 111 | - 将生成的 `MenuConfig.json` 放到右键菜单 DLL 所在目录 112 | - 重新注册 SharpShell 扩展或重启资源管理器 113 | 114 | ## 快捷操作 115 | 116 | - **复制菜单配置**:先创建一个模板项,编辑后可在 JSON 中复制 117 | - **批量编辑**:直接在 JSON 中编辑,然后用此工具验证和调整 118 | - **测试配置**:使用"显示完整路径和命令"选项测试命令是否正确 119 | 120 | ## 注意事项 121 | 122 | 1. 保存前请确保所有必填字段已填写 123 | 2. 文件路径建议使用绝对路径或环境变量 124 | 3. 修改配置后需要重新注册 SharpShell 扩展才能生效 125 | 4. 建议在修改前备份原配置文件 126 | 127 | ## 技术细节 128 | 129 | - **开发框架**:WPF (.NET Framework 4.8) 130 | - **数据绑定**:MVVM 架构 131 | - **JSON 处理**:Newtonsoft.Json 13.0.3 132 | - **配置格式**:与 SharpShell MenuFunctions 项目兼容 133 | 134 | ## 开发者 135 | 136 | 此工具是 SharpShell 右键菜单扩展项目的配套工具,简化了 JSON 配置的编辑过程。 137 | 138 | -------------------------------------------------------------------------------- /MenuFunctions/测试复选框/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | 5 | namespace WindowTextExtractor 6 | { 7 | public class Program 8 | { 9 | [DllImport("user32.dll", CharSet = CharSet.Auto)] 10 | static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); 11 | 12 | [DllImport("user32.dll", CharSet = CharSet.Auto)] 13 | static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, uint lParam); 14 | 15 | [DllImport("user32.dll")] 16 | [return: MarshalAs(UnmanagedType.Bool)] 17 | static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr lParam); 18 | 19 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 20 | static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); 21 | 22 | private delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter); 23 | 24 | const uint BM_GETCHECK = 0x00F0; 25 | const uint BM_SETCHECK = 0x00F1; 26 | const uint BST_CHECKED = 0x0001; 27 | const uint BST_UNCHECKED = 0x0000; 28 | 29 | public static void Main(string[] args) 30 | { 31 | // 写死的参数 32 | string handle = "205930588"; 33 | IntPtr hWnd = new IntPtr(Convert.ToInt32(handle)); // 字符串句柄转成句柄变量 34 | 35 | // 将所有复选框设置为未选中状态 36 | bool result = DisableAllCheckBoxes(hWnd); 37 | if (result) 38 | { 39 | Console.WriteLine("操作成功"); 40 | } 41 | else 42 | { 43 | Console.WriteLine("操作失败"); 44 | } 45 | Console.ReadLine(); 46 | } 47 | 48 | public static bool DisableAllCheckBoxes(IntPtr hWnd) 49 | { 50 | bool operationResult = false; 51 | 52 | EnumWindowProc callback = (childHandle, parameter) => 53 | { 54 | StringBuilder classText = new StringBuilder(256); 55 | GetClassName(childHandle, classText, classText.Capacity); 56 | 57 | Console.WriteLine($"找到控件,句柄: {childHandle}, 类名: {classText}"); 58 | 59 | if (classText.ToString() == "Button" || classText.ToString().Contains("BUTTON")) 60 | { 61 | // 获取当前复选框状态 62 | IntPtr checkState = SendMessage(childHandle, BM_GETCHECK, IntPtr.Zero, IntPtr.Zero); 63 | Console.WriteLine($"找到复选框,当前状态: {checkState}"); 64 | 65 | // 设置复选框状态为未选中 66 | if (checkState != (IntPtr)BST_UNCHECKED) 67 | { 68 | SendMessage(childHandle, BM_SETCHECK, (IntPtr)BST_UNCHECKED, IntPtr.Zero); 69 | IntPtr newCheckState = SendMessage(childHandle, BM_GETCHECK, IntPtr.Zero, IntPtr.Zero); 70 | Console.WriteLine($"新状态: {newCheckState}"); 71 | operationResult = (newCheckState == (IntPtr)BST_UNCHECKED); 72 | } 73 | } 74 | 75 | return true; // 继续枚举 76 | }; 77 | 78 | EnumChildWindows(hWnd, callback, IntPtr.Zero); 79 | return operationResult; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /MenuFunctions/按键测试2/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Threading.Tasks; 4 | using System.Windows.Forms; 5 | using WindowsInput.Native; 6 | using WindowsInput; 7 | 8 | using System.Diagnostics; 9 | 10 | using System.Threading; 11 | 12 | using System.Text; 13 | 14 | 15 | 16 | public class PushKey 17 | { 18 | static void Main() 19 | { 20 | 21 | //string handle = ProgramRunner.StartProcessAndGetHandle("notepad.exe"); 22 | string handle = "4138042"; 23 | Console.WriteLine(handle); 24 | //Thread.Sleep(2000); // 稍微延迟,以模拟正常的按键速度 25 | WindowMessageSender.SendStringToWindow(handle, "直接变"); 26 | 27 | //WindowMessageSender_ADD.SendStringToWindow(handle, "nihao111你好"); 28 | //SendKeys.SendWait("abc"); 29 | 30 | 31 | 32 | } 33 | } 34 | 35 | 36 | public class ProgramRunner 37 | { 38 | [DllImport("user32.dll")] 39 | private static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 40 | 41 | public static string StartProcessAndGetHandle(string filePath) 42 | { 43 | Process process = new Process(); 44 | process.StartInfo.FileName = filePath; 45 | process.Start(); 46 | 47 | process.WaitForInputIdle(); // 等待程序准备好接收用户输入 48 | IntPtr handle = process.MainWindowHandle; 49 | 50 | // 如果 MainWindowHandle 不可用,可以尝试使用 FindWindow 方法 51 | if (handle == IntPtr.Zero) 52 | { 53 | handle = FindWindow(null, process.MainWindowTitle); 54 | } 55 | 56 | return handle.ToString(); 57 | } 58 | } 59 | 60 | public class WindowMessageSender 61 | { 62 | 63 | [DllImport("user32.dll", CharSet = CharSet.Unicode)] 64 | private static extern IntPtr SendMessageW(IntPtr hWnd, uint Msg, IntPtr wParam, string lParam); 65 | 66 | private const uint WM_SETTEXT = 0x000C; 67 | 68 | public static void SendStringToWindow(string windowHandle, string message) 69 | { 70 | IntPtr hWnd = new IntPtr(Convert.ToInt32(windowHandle)); 71 | SendMessageW(hWnd, WM_SETTEXT, IntPtr.Zero, message); 72 | } 73 | 74 | } 75 | 76 | public class WindowMessageSender_ADD 77 | { 78 | [DllImport("user32.dll", CharSet = CharSet.Unicode)] 79 | private static extern IntPtr SendMessageW(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); 80 | 81 | 82 | private const uint WM_CHAR = 0x0102; 83 | 84 | public static void SendStringToWindow(string windowHandle, string message) 85 | { 86 | IntPtr hWnd = new IntPtr(Convert.ToInt32(windowHandle)); 87 | foreach (char c in message) 88 | { 89 | SendMessageW(hWnd, WM_CHAR, (IntPtr)c, IntPtr.Zero); 90 | Thread.Sleep(0); // 可以调整这个延迟 91 | } 92 | } 93 | } 94 | 95 | 96 | 97 | public class ScintillaHelper 98 | { 99 | [DllImport("user32.dll", CharSet = CharSet.Unicode)] 100 | private static extern IntPtr SendMessageW(IntPtr hWnd, int Msg, IntPtr wParam, string lParam); 101 | 102 | private const int SCI_SETTEXT = 2181; 103 | 104 | public static void SendStringToScintilla(string windowHandle, string message) 105 | { 106 | IntPtr hWnd = new IntPtr(Convert.ToInt32(windowHandle)); 107 | SendMessageW(hWnd, SCI_SETTEXT, IntPtr.Zero, message); 108 | } 109 | } -------------------------------------------------------------------------------- /MenuFunctions/实现运行效果/实现运行效果.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {81868E36-4BFF-43EF-9010-E436789B19EE} 8 | Exe 9 | 实现运行效果 10 | 实现运行效果 11 | v4.8 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ..\packages\Toolbox.0.2.2\lib\net40\Toolbox.Extensions.dll 46 | 47 | 48 | ..\packages\Toolbox.0.2.2\lib\net40\Toolbox.Logger.dll 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B} 63 | 1 64 | 0 65 | 0 66 | tlbimp 67 | False 68 | True 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /MenuFunctions/卸载计算机全部sharpshelldll.ps1: -------------------------------------------------------------------------------- 1 | # 自动提权部分 2 | if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { 3 | Write-Host "需要管理员权限,正在尝试以管理员身份重新启动..." 4 | Start-Process powershell -ArgumentList "-ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs 5 | exit 6 | } 7 | 8 | Write-Host "===============================" 9 | Write-Host " 正在清理 SharpShell 残留注册项..." 10 | Write-Host "===============================" 11 | 12 | # 自动结束 Explorer 13 | Write-Host "" 14 | Write-Host "正在结束 explorer.exe..." 15 | Get-Process explorer -ErrorAction SilentlyContinue | Stop-Process -Force 16 | 17 | $views = @("Registry64", "Registry32") 18 | 19 | foreach ($view in $views) { 20 | Write-Host "" 21 | Write-Host "[$view] 清理中..." 22 | 23 | # 打开 HKCR\CLSID,找 InprocServer32 指向 mscoree.dll 且带 SharpShell 信息的项 24 | $base = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::ClassesRoot, $view) 25 | $clsid = $base.OpenSubKey("CLSID",$true) 26 | if ($clsid -eq $null) { 27 | Write-Host " 未找到 CLSID 节点。" 28 | continue 29 | } 30 | 31 | foreach ($sub in $clsid.GetSubKeyNames()) { 32 | if ($sub -notmatch '^{.*}$') { continue } 33 | 34 | try { 35 | $key = $clsid.OpenSubKey($sub,$false) 36 | if ($null -eq $key) { continue } 37 | 38 | $inproc = $key.OpenSubKey("InprocServer32",$false) 39 | if ($null -ne $inproc) { 40 | $asm = $inproc.GetValue("Assembly") 41 | $code = $inproc.GetValue("CodeBase") 42 | 43 | if (($asm -and $asm -like "*SharpShell*") -or ($code -and $code -like "*SharpShell*")) { 44 | try { 45 | $clsid.DeleteSubKeyTree($sub,$false) 46 | Write-Host " 删除 HKCR\CLSID\$sub" 47 | } catch { 48 | Write-Host (" 删除失败 {0}: {1}" -f $sub, $_.Exception.Message) 49 | } 50 | } 51 | $inproc.Close() 52 | } 53 | $key.Close() 54 | } catch { 55 | Write-Host (" 遍历出错 {0}: {1}" -f $sub, $_.Exception.Message) 56 | } 57 | } 58 | $clsid.Close() 59 | $base.Close() 60 | 61 | # 清理 Approved 列表 62 | Write-Host " 清理 Shell Extensions\Approved..." 63 | $lm = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, $view) 64 | $approved = $lm.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved",$true) 65 | if ($approved) { 66 | foreach ($v in $approved.GetValueNames()) { 67 | $val = $approved.GetValue($v) 68 | if ($val -like "*SharpShell*" -or $v -like "*SharpShell*") { 69 | try { 70 | $approved.DeleteValue($v,$false) 71 | Write-Host " 删除 Approved 值:$v" 72 | } catch { 73 | Write-Host (" 删除失败 {0}: {1}" -f $v, $_.Exception.Message) 74 | } 75 | } 76 | } 77 | $approved.Close() 78 | } 79 | $lm.Close() 80 | } 81 | 82 | Write-Host "" 83 | Write-Host "正在重新启动 explorer.exe..." 84 | Start-Process "explorer.exe" 85 | 86 | Write-Host "" 87 | Write-Host "====================================" 88 | Write-Host " SharpShell 清理完成 ✅" 89 | Write-Host " 已自动重启资源管理器。" 90 | Write-Host "====================================" 91 | Start-Sleep -Seconds 3 92 | -------------------------------------------------------------------------------- /MenuFunctions/测试输入控件/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using System.Text; 4 | using System.Windows.Forms; 5 | 6 | namespace WindowTextExtractor 7 | { 8 | public class Program 9 | { 10 | [DllImport("user32.dll", CharSet = CharSet.Auto)] 11 | static extern bool SendMessageString(IntPtr hWnd, uint Msg, IntPtr wParam, string lParam); 12 | 13 | [DllImport("user32.dll", CharSet = CharSet.Auto)] 14 | static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, StringBuilder lParam); 15 | 16 | [DllImport("user32.dll")] 17 | [return: MarshalAs(UnmanagedType.Bool)] 18 | static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr lParam); 19 | 20 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 21 | static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); 22 | 23 | private delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter); 24 | 25 | const uint WM_SETTEXT = 0x000C; 26 | const uint WM_GETTEXT = 0x000D; 27 | const uint WM_GETTEXTLENGTH = 0x000E; 28 | 29 | public static void Main(string[] args) 30 | { 31 | if (args.Length < 1) 32 | { 33 | Console.WriteLine("Usage: WindowTextExtractor "); 34 | return; 35 | } 36 | 37 | string handle = args[0]; 38 | IntPtr hWnd = new IntPtr(Convert.ToInt32(handle)); //字符串句柄转成句柄变量 39 | 40 | //// 示例调用获取控件文本的方法 41 | //string text = GetTextFromWindow(handle, "WindowsForms10.EDIT.app.0.3c3ecb7_r24_ad1", 27); 42 | //Console.WriteLine(text); 43 | 44 | for (int i = 1; i <= 27; i++) 45 | { 46 | //WindowsForms10.EDIT.app.0.1e35270_r24_ad1 这个可能会变化, 去spy++里找到这个编辑框 ,然后获取他的新类名,改到下面这个地方 47 | string text = GetTextFromWindow(handle, "WindowsForms10.EDIT.app.0.1e35270_r32_ad1", i); 48 | if (text!=null && text.StartsWith("..")) 49 | { 50 | Console.WriteLine(text); 51 | break; 52 | } 53 | } 54 | } 55 | 56 | 57 | 58 | public static string GetTextFromWindow(string windowHandle, string className, int instance) 59 | { 60 | IntPtr hWnd = new IntPtr(Convert.ToInt32(windowHandle)); 61 | int count = 0; 62 | string windowText = null; 63 | 64 | EnumWindowProc callback = (childHandle, parameter) => 65 | { 66 | StringBuilder classText = new StringBuilder(256); 67 | GetClassName(childHandle, classText, classText.Capacity); 68 | 69 | if (classText.ToString() == className) 70 | { 71 | if (count == instance) 72 | { 73 | int length = (int)SendMessage(childHandle, WM_GETTEXTLENGTH, IntPtr.Zero, null); 74 | StringBuilder sb = new StringBuilder(length + 1); 75 | SendMessage(childHandle, WM_GETTEXT, (IntPtr)sb.Capacity, sb); 76 | windowText = sb.ToString(); 77 | return false; // Stop enumeration 78 | } 79 | count++; 80 | } 81 | 82 | return true; // Continue enumeration 83 | }; 84 | 85 | EnumChildWindows(hWnd, callback, IntPtr.Zero); 86 | return windowText; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/快速入门.md: -------------------------------------------------------------------------------- 1 | # 快速入门指南 2 | 3 | ## 第一次使用 4 | 5 | ### 1. 启动程序 6 | 运行 `MenuFunctions_Panel.exe`,程序会自动尝试加载配置文件。 7 | 8 | ### 2. 加载示例配置 9 | 如果自动加载失败,点击工具栏的 **📂 打开** 按钮,选择: 10 | - `示例配置.json` - 查看示例 11 | - 或你的 `MenuConfig.json` 文件 12 | 13 | ### 3. 界面介绍 14 | ``` 15 | ┌─────────────────────────────────────────────────┐ 16 | │ 📂打开 💾保存 ➕新增 📑添加子项 ➖分隔线 │ ← 工具栏 17 | ├──────────┬──────────────────────────────────────┤ 18 | │ │ │ 19 | │ 菜单 │ 详细配置面板 │ 20 | │ 结构 │ │ 21 | │ 树 │ 📌 基本信息 │ 22 | │ │ 📍 显示位置 │ 23 | │ │ ⚙️ 程序设置 │ 24 | │ │ 📄 文件类型过滤 │ 25 | │ │ 🔧 高级选项 │ 26 | │ │ 💻 CMD 设置 │ 27 | └──────────┴──────────────────────────────────────┘ 28 | ``` 29 | 30 | ## 常见操作 31 | 32 | ### 添加新菜单项 33 | 34 | 1. 点击 **➕ 新增** 按钮 35 | 2. 在右侧面板编辑配置: 36 | - **菜单文本**:输入显示名称,如"用记事本打开" 37 | - **程序路径**:点击"浏览..."选择程序,如 `notepad.exe` 38 | - **图标路径**:选择图标文件(可选) 39 | 3. 设置显示位置(勾选需要的选项) 40 | 4. 点击 **💾 保存** 41 | 42 | ### 创建子菜单 43 | 44 | 1. 在左侧树中选择父菜单项 45 | 2. 点击 **📑 添加子项** 按钮 46 | 3. 编辑子菜单的配置 47 | 4. 重复此过程可创建多级菜单 48 | 49 | ### 调整顺序 50 | 51 | 1. 在左侧树中选择要移动的菜单项 52 | 2. 点击 **⬆️ 上移** 或 **⬇️ 下移** 按钮 53 | 3. 同级菜单项会按 Order 字段排序 54 | 55 | ### 添加分隔线 56 | 57 | 点击 **➖ 分隔线** 按钮即可添加一个菜单分隔线。 58 | 59 | ### 删除菜单项 60 | 61 | 1. 选择要删除的菜单项 62 | 2. 点击 **🗑️ 删除** 按钮 63 | 3. 确认删除 64 | 65 | ## 实用技巧 66 | 67 | ### 💡 技巧 1:复制路径到剪贴板 68 | 69 | ```json 70 | { 71 | "Text": "复制路径", 72 | "ProgramPath": "copypath", 73 | "ShowInRootMenu": true 74 | } 75 | ``` 76 | 77 | - **ProgramPath** 设为 `copypath`(预设命令) 78 | - 勾选"显示在根菜单"可直接显示在右键菜单顶层 79 | 80 | ### 💡 技巧 2:用特定程序打开文件 81 | 82 | ```json 83 | { 84 | "Text": "用记事本打开", 85 | "ProgramPath": "notepad.exe", 86 | "FileTypes": [".txt", ".log", ".ini"], 87 | "ShowOnFiles": true, 88 | "ShowOnFolderBackground": false 89 | } 90 | ``` 91 | 92 | - 指定文件类型,只在 .txt/.log/.ini 文件上显示 93 | - 不在文件夹背景显示 94 | 95 | ### 💡 技巧 3:在当前文件夹打开 CMD 96 | 97 | ```json 98 | { 99 | "Text": "在此处打开命令窗口", 100 | "ProgramPath": "cmd.exe", 101 | "ShowOnFolderBackground": true, 102 | "ShowOnFolder": true, 103 | "ShowOnFiles": false 104 | } 105 | ``` 106 | 107 | - 只在文件夹和文件夹背景显示 108 | - 不在文件上显示 109 | 110 | ### 💡 技巧 4:执行批处理脚本 111 | 112 | ```json 113 | { 114 | "Text": "运行清理脚本", 115 | "ProgramPath": "C:\\Scripts\\cleanup.bat", 116 | "RunningProgramWithCMD": true, 117 | "HideCmdWindow": true, 118 | "ShowOnFolderBackground": true 119 | } 120 | ``` 121 | 122 | - 通过 CMD 运行批处理 123 | - 隐藏 CMD 窗口,后台执行 124 | 125 | ### 💡 技巧 5:使用动态变量 126 | 127 | ```json 128 | { 129 | "Text": "重命名 ${filename}", 130 | "ProgramPath": "MyRenamer.exe" 131 | } 132 | ``` 133 | 134 | 菜单文本会动态显示为"重命名 文件名"。 135 | 136 | 支持的变量: 137 | - `${filename}` - 文件名(无扩展名) 138 | - `${extension}` - 扩展名 139 | - `${fullpath}` - 完整路径 140 | - `${parentdir}` - 父目录 141 | 142 | ## 配置检查清单 143 | 144 | 保存前请确认: 145 | 146 | - ✅ 菜单文本已填写 147 | - ✅ 程序路径正确(或使用预设命令) 148 | - ✅ 显示位置至少勾选一项 149 | - ✅ 如果有文件类型限制,格式正确(如 `.txt`) 150 | - ✅ 图标路径正确(可选) 151 | 152 | ## 应用配置 153 | 154 | 1. 保存 `MenuConfig.json` 到: 155 | ``` 156 | MenuFunctions\bin\Release\MenuConfig.json 157 | ``` 158 | 159 | 2. 如果是新配置,需要重新注册 SharpShell: 160 | ```powershell 161 | # 注册(管理员权限) 162 | regasm /codebase MenuFunctions.dll 163 | srm install MenuFunctions.dll 164 | ``` 165 | 166 | 3. 如果只是修改配置,重启资源管理器即可: 167 | ```powershell 168 | # 重启资源管理器 169 | taskkill /f /im explorer.exe 170 | start explorer.exe 171 | ``` 172 | 173 | ## 故障排除 174 | 175 | ### 问题:无法加载配置文件 176 | - 检查文件是否为有效的 JSON 格式 177 | - 使用 JSON 验证工具检查语法 178 | 179 | ### 问题:修改后不生效 180 | - 确保配置文件保存在正确位置 181 | - 重启资源管理器 182 | - 检查 DLL 是否正确注册 183 | 184 | ### 问题:菜单项不显示 185 | - 检查"是否可见"选项是否勾选 186 | - 检查显示位置设置是否正确 187 | - 检查文件类型过滤是否过于严格 188 | 189 | ## 下一步 190 | 191 | - 📖 阅读完整的 [README.md](README.md) 了解所有功能 192 | - 🔧 参考 [示例配置.json](示例配置.json) 学习更多配置方式 193 | - 💻 查看主项目的 MenuFunctions.cs 了解实现细节 194 | 195 | 祝使用愉快! 🎉 196 | 197 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34202.233 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MenuFunctions", "MenuFunctions\MenuFunctions.csproj", "{E117FBC0-7AB4-4C15-A4A9-646E97854821}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "实现运行效果", "实现运行效果\实现运行效果.csproj", "{81868E36-4BFF-43EF-9010-E436789B19EE}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "测试", "测试\测试.csproj", "{4E0DA51C-ECE6-40D6-A199-C2BEB44E8B15}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "按键测试", "按键测试\按键测试.csproj", "{AB5C5856-CD60-4994-B75F-01542D99F05D}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "按键测试2", "按键测试2\按键测试2.csproj", "{0CCAD1A0-93F4-468F-A1E2-03E3C6C1BC08}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "测试输入控件", "测试输入控件\测试输入控件.csproj", "{91648DF0-52EF-499C-B21E-FC312BE51449}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "测试复选框", "测试复选框\测试复选框.csproj", "{436A0C07-A5A9-49A0-82EF-13FD9817CE6A}" 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MenuFunctions_Panel", "MenuFunctions_Panel\MenuFunctions_Panel.csproj", "{6E33F453-89DB-4A0B-8D6E-DBB7CA31BBC3}" 21 | EndProject 22 | Global 23 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 24 | Debug|Any CPU = Debug|Any CPU 25 | Release|Any CPU = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {E117FBC0-7AB4-4C15-A4A9-646E97854821}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {E117FBC0-7AB4-4C15-A4A9-646E97854821}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {E117FBC0-7AB4-4C15-A4A9-646E97854821}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {E117FBC0-7AB4-4C15-A4A9-646E97854821}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {81868E36-4BFF-43EF-9010-E436789B19EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {81868E36-4BFF-43EF-9010-E436789B19EE}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {81868E36-4BFF-43EF-9010-E436789B19EE}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {81868E36-4BFF-43EF-9010-E436789B19EE}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {4E0DA51C-ECE6-40D6-A199-C2BEB44E8B15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {4E0DA51C-ECE6-40D6-A199-C2BEB44E8B15}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {4E0DA51C-ECE6-40D6-A199-C2BEB44E8B15}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {4E0DA51C-ECE6-40D6-A199-C2BEB44E8B15}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {AB5C5856-CD60-4994-B75F-01542D99F05D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {AB5C5856-CD60-4994-B75F-01542D99F05D}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {AB5C5856-CD60-4994-B75F-01542D99F05D}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {AB5C5856-CD60-4994-B75F-01542D99F05D}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {0CCAD1A0-93F4-468F-A1E2-03E3C6C1BC08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {0CCAD1A0-93F4-468F-A1E2-03E3C6C1BC08}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {0CCAD1A0-93F4-468F-A1E2-03E3C6C1BC08}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {0CCAD1A0-93F4-468F-A1E2-03E3C6C1BC08}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {91648DF0-52EF-499C-B21E-FC312BE51449}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {91648DF0-52EF-499C-B21E-FC312BE51449}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {91648DF0-52EF-499C-B21E-FC312BE51449}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {91648DF0-52EF-499C-B21E-FC312BE51449}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {436A0C07-A5A9-49A0-82EF-13FD9817CE6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {436A0C07-A5A9-49A0-82EF-13FD9817CE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {436A0C07-A5A9-49A0-82EF-13FD9817CE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {436A0C07-A5A9-49A0-82EF-13FD9817CE6A}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {6E33F453-89DB-4A0B-8D6E-DBB7CA31BBC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {6E33F453-89DB-4A0B-8D6E-DBB7CA31BBC3}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {6E33F453-89DB-4A0B-8D6E-DBB7CA31BBC3}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {6E33F453-89DB-4A0B-8D6E-DBB7CA31BBC3}.Release|Any CPU.Build.0 = Release|Any CPU 60 | EndGlobalSection 61 | GlobalSection(SolutionProperties) = preSolution 62 | HideSolutionNode = FALSE 63 | EndGlobalSection 64 | GlobalSection(ExtensibilityGlobals) = postSolution 65 | SolutionGuid = {F8BAF846-176F-4FD5-B99A-B131DB174BF3} 66 | EndGlobalSection 67 | EndGlobal 68 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/示例配置.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Text": "复制路径", 4 | "Command": "", 5 | "ProgramPath": "copypath", 6 | "SubItems": [], 7 | "FileTypes": [], 8 | "ShowInRootMenu": true, 9 | "IconPath": "shell32.dll", 10 | "IsVisible": true, 11 | "ShowOnFiles": true, 12 | "AppendCommandToEachPath": true, 13 | "ShowOnFolderBackground": true, 14 | "ShowOnFolder": true, 15 | "OnlyUsingProgram": false, 16 | "RunningProgramWithCMD": false, 17 | "HideCmdWindow": false, 18 | "KeepCmdWindows": false, 19 | "IsSeparator": false, 20 | "DisplayCompletePathAndCommand": false, 21 | "UseQuotes": true, 22 | "Order": 0, 23 | "AddFileTypeParameter": false 24 | }, 25 | { 26 | "Text": "用记事本打开", 27 | "Command": "", 28 | "ProgramPath": "notepad.exe", 29 | "SubItems": [], 30 | "FileTypes": [ 31 | ".txt", 32 | ".log", 33 | ".ini", 34 | ".cfg" 35 | ], 36 | "ShowInRootMenu": false, 37 | "IconPath": "notepad.exe", 38 | "IsVisible": true, 39 | "ShowOnFiles": true, 40 | "AppendCommandToEachPath": false, 41 | "ShowOnFolderBackground": false, 42 | "ShowOnFolder": false, 43 | "OnlyUsingProgram": false, 44 | "RunningProgramWithCMD": false, 45 | "HideCmdWindow": false, 46 | "KeepCmdWindows": false, 47 | "IsSeparator": false, 48 | "DisplayCompletePathAndCommand": false, 49 | "UseQuotes": true, 50 | "Order": 1, 51 | "AddFileTypeParameter": false 52 | }, 53 | { 54 | "Text": "文件操作", 55 | "Command": "", 56 | "ProgramPath": "", 57 | "SubItems": [ 58 | { 59 | "Text": "打开文件位置", 60 | "Command": "", 61 | "ProgramPath": "openfolderandselectitems", 62 | "SubItems": [], 63 | "FileTypes": [], 64 | "ShowInRootMenu": false, 65 | "IconPath": "", 66 | "IsVisible": true, 67 | "ShowOnFiles": true, 68 | "AppendCommandToEachPath": true, 69 | "ShowOnFolderBackground": false, 70 | "ShowOnFolder": false, 71 | "OnlyUsingProgram": false, 72 | "RunningProgramWithCMD": false, 73 | "HideCmdWindow": false, 74 | "KeepCmdWindows": false, 75 | "IsSeparator": false, 76 | "DisplayCompletePathAndCommand": false, 77 | "UseQuotes": false, 78 | "Order": 0, 79 | "AddFileTypeParameter": false 80 | }, 81 | { 82 | "Text": "分隔线", 83 | "Command": "", 84 | "ProgramPath": "", 85 | "SubItems": [], 86 | "FileTypes": [], 87 | "ShowInRootMenu": false, 88 | "IconPath": "", 89 | "IsVisible": true, 90 | "ShowOnFiles": true, 91 | "AppendCommandToEachPath": true, 92 | "ShowOnFolderBackground": true, 93 | "ShowOnFolder": true, 94 | "OnlyUsingProgram": false, 95 | "RunningProgramWithCMD": false, 96 | "HideCmdWindow": false, 97 | "KeepCmdWindows": false, 98 | "IsSeparator": true, 99 | "DisplayCompletePathAndCommand": false, 100 | "UseQuotes": true, 101 | "Order": 1, 102 | "AddFileTypeParameter": false 103 | }, 104 | { 105 | "Text": "在命令行中打开", 106 | "Command": "", 107 | "ProgramPath": "cmd.exe", 108 | "SubItems": [], 109 | "FileTypes": [], 110 | "ShowInRootMenu": false, 111 | "IconPath": "cmd.exe", 112 | "IsVisible": true, 113 | "ShowOnFiles": false, 114 | "AppendCommandToEachPath": false, 115 | "ShowOnFolderBackground": true, 116 | "ShowOnFolder": true, 117 | "OnlyUsingProgram": false, 118 | "RunningProgramWithCMD": false, 119 | "HideCmdWindow": false, 120 | "KeepCmdWindows": false, 121 | "IsSeparator": false, 122 | "DisplayCompletePathAndCommand": false, 123 | "UseQuotes": true, 124 | "Order": 2, 125 | "AddFileTypeParameter": false 126 | } 127 | ], 128 | "FileTypes": [], 129 | "ShowInRootMenu": false, 130 | "IconPath": "", 131 | "IsVisible": true, 132 | "ShowOnFiles": true, 133 | "AppendCommandToEachPath": true, 134 | "ShowOnFolderBackground": true, 135 | "ShowOnFolder": true, 136 | "OnlyUsingProgram": false, 137 | "RunningProgramWithCMD": false, 138 | "HideCmdWindow": false, 139 | "KeepCmdWindows": false, 140 | "IsSeparator": false, 141 | "DisplayCompletePathAndCommand": false, 142 | "UseQuotes": true, 143 | "Order": 2, 144 | "AddFileTypeParameter": false 145 | } 146 | ] 147 | 148 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/MenuFunctions_Panel.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {6E33F453-89DB-4A0B-8D6E-DBB7CA31BBC3} 8 | WinExe 9 | MenuFunctions_Panel 10 | MenuFunctions_Panel 11 | v4.8 12 | 512 13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 4 15 | true 16 | true 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 4.0 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | MSBuild:Compile 59 | 60 | 61 | MSBuild:Compile 62 | Designer 63 | 64 | 65 | App.xaml 66 | Code 67 | 68 | 69 | MainWindow.xaml 70 | Code 71 | 72 | 73 | 74 | 75 | 76 | 77 | Code 78 | 79 | 80 | True 81 | True 82 | Resources.resx 83 | 84 | 85 | True 86 | Settings.settings 87 | True 88 | 89 | 90 | ResXFileCodeGenerator 91 | Resources.Designer.cs 92 | 93 | 94 | 95 | SettingsSingleFileGenerator 96 | Settings.Designer.cs 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions/MenuFunctions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E117FBC0-7AB4-4C15-A4A9-646E97854821} 8 | Library 9 | Properties 10 | MenuFunctions 11 | MenuFunctions 12 | v4.8 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | true 35 | 36 | 37 | key.snk 38 | 39 | 40 | 41 | ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll 42 | 43 | 44 | ..\packages\SharpShell.2.7.2\lib\net40-client\SharpShell.dll 45 | True 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | True 65 | True 66 | Resources.resx 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | ResXFileCodeGenerator 76 | Resources.Designer.cs 77 | 78 | 79 | 80 | 81 | {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B} 82 | 1 83 | 0 84 | 0 85 | tlbimp 86 | False 87 | True 88 | 89 | 90 | {EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B} 91 | 1 92 | 1 93 | 0 94 | tlbimp 95 | False 96 | True 97 | 98 | 99 | 100 | 101 | 102 | 103 | 110 | -------------------------------------------------------------------------------- /MenuFunctions/实现运行效果/ExplorerHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace 实现运行效果 11 | { 12 | internal class ExplorerHelper 13 | { 14 | 15 | [DllImport("shell32.dll", CharSet = CharSet.Auto)] 16 | static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo); 17 | 18 | [DllImport("shell32.dll")] 19 | static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, IntPtr apidl, uint dwFlags); 20 | 21 | [DllImport("shell32.dll")] 22 | static extern int SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr bindingContext, out IntPtr pidl, uint sfgaoIn, out uint psfgaoOut); 23 | 24 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 25 | public struct SHELLEXECUTEINFO 26 | { 27 | public int cbSize; 28 | public uint fMask; 29 | public IntPtr hwnd; 30 | [MarshalAs(UnmanagedType.LPTStr)] 31 | public string lpVerb; 32 | [MarshalAs(UnmanagedType.LPTStr)] 33 | public string lpFile; 34 | [MarshalAs(UnmanagedType.LPTStr)] 35 | public string lpParameters; 36 | [MarshalAs(UnmanagedType.LPTStr)] 37 | public string lpDirectory; 38 | public int nShow; 39 | public IntPtr hInstApp; 40 | public IntPtr lpIDList; 41 | [MarshalAs(UnmanagedType.LPTStr)] 42 | public string lpClass; 43 | public IntPtr hkeyClass; 44 | public uint dwHotKey; 45 | public IntPtr hIcon; 46 | public IntPtr hProcess; 47 | } 48 | 49 | private const int SW_SHOW = 5; 50 | 51 | // 打开文件夹并选择文件 52 | public static void OpenFolderAndSelectItem(string filePath) 53 | { 54 | IntPtr pidl = IntPtr.Zero; 55 | SHParseDisplayName(filePath, IntPtr.Zero, out pidl, 0, out _); 56 | if (pidl != IntPtr.Zero) 57 | { 58 | SHOpenFolderAndSelectItems(pidl, 0, IntPtr.Zero, 0); 59 | Marshal.FreeCoTaskMem(pidl); 60 | } 61 | } 62 | 63 | // 打开文件夹 64 | public static void OpenFolder(string folderPath) 65 | { 66 | SHELLEXECUTEINFO sei = new SHELLEXECUTEINFO 67 | { 68 | cbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO)), 69 | lpVerb = "open", 70 | lpFile = folderPath, 71 | nShow = SW_SHOW 72 | }; 73 | ShellExecuteEx(ref sei); 74 | } 75 | 76 | // 打开项目,可以是文件夹、可执行文件、URL或其他文件类型 77 | public static void OpenItem(string path) 78 | { 79 | 80 | 81 | // 只取第一行作为路径 82 | path = path.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); 83 | 84 | if (string.IsNullOrEmpty(path)) 85 | { 86 | throw new ArgumentException("提供的路径无效或为空。"); 87 | } 88 | 89 | // 验证路径是否合法 90 | string extension = string.Empty; 91 | try 92 | { 93 | extension = Path.GetExtension(path); 94 | } 95 | catch (ArgumentException ex) 96 | { 97 | // 如果获取扩展名时出现异常,打印出错的路径并继续执行 98 | Console.WriteLine($"获取扩展名时出错。传入的路径为: {path}。错误信息: {ex.Message}"); 99 | return; // 由于无法获取扩展名,此处选择退出方法 100 | } 101 | 102 | 103 | 104 | // 如果没有扩展名,我们假定它是一个文件夹 105 | if (string.IsNullOrEmpty(extension)) 106 | { 107 | OpenFolder(path); 108 | } 109 | else if (File.Exists(path)) // 如果路径指向一个文件 110 | { 111 | extension = extension.ToLowerInvariant(); 112 | 113 | switch (extension) 114 | { 115 | case ".exe": 116 | // 如果是可执行文件,启动该应用程序 117 | Process.Start(path); 118 | break; 119 | case ".url": 120 | // 如果是URL文件,用默认浏览器打开它 121 | Process.Start(path); 122 | break; 123 | default: 124 | // 对于其他文件类型,使用默认程序打开 125 | // 对于其他文件类型,使用默认程序打开 126 | var sei = new SHELLEXECUTEINFO 127 | { 128 | cbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO)), 129 | lpVerb = "open", 130 | lpFile = path, 131 | nShow = SW_SHOW 132 | }; 133 | ShellExecuteEx(ref sei); // 注意这里使用了 ref 关键字 134 | 135 | break; 136 | } 137 | } 138 | else 139 | { 140 | // 如果文件不存在或不被识别,显示“打开方式”对话框 141 | // 对于其他文件类型,使用默认程序打开 142 | var sei = new SHELLEXECUTEINFO 143 | { 144 | cbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO)), 145 | lpVerb = "open", 146 | lpFile = path, 147 | nShow = SW_SHOW 148 | }; 149 | ShellExecuteEx(ref sei); // 注意这里使用了 ref 关键字 150 | 151 | } 152 | } 153 | } 154 | } 155 | 156 | -------------------------------------------------------------------------------- /MenuFunctions/测试/ExplorerHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | 11 | 12 | 13 | namespace 测试 14 | { 15 | public class ExplorerHelper 16 | { 17 | 18 | [DllImport("shell32.dll", CharSet = CharSet.Auto)] 19 | static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo); 20 | 21 | [DllImport("shell32.dll")] 22 | static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, IntPtr apidl, uint dwFlags); 23 | 24 | [DllImport("shell32.dll")] 25 | static extern int SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr bindingContext, out IntPtr pidl, uint sfgaoIn, out uint psfgaoOut); 26 | 27 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 28 | public struct SHELLEXECUTEINFO 29 | { 30 | public int cbSize; 31 | public uint fMask; 32 | public IntPtr hwnd; 33 | [MarshalAs(UnmanagedType.LPTStr)] 34 | public string lpVerb; 35 | [MarshalAs(UnmanagedType.LPTStr)] 36 | public string lpFile; 37 | [MarshalAs(UnmanagedType.LPTStr)] 38 | public string lpParameters; 39 | [MarshalAs(UnmanagedType.LPTStr)] 40 | public string lpDirectory; 41 | public int nShow; 42 | public IntPtr hInstApp; 43 | public IntPtr lpIDList; 44 | [MarshalAs(UnmanagedType.LPTStr)] 45 | public string lpClass; 46 | public IntPtr hkeyClass; 47 | public uint dwHotKey; 48 | public IntPtr hIcon; 49 | public IntPtr hProcess; 50 | } 51 | 52 | private const int SW_SHOW = 5; 53 | 54 | // 打开文件夹并选择文件 55 | public static void OpenFolderAndSelectItem(string filePath) 56 | { 57 | IntPtr pidl = IntPtr.Zero; 58 | SHParseDisplayName(filePath, IntPtr.Zero, out pidl, 0, out _); 59 | if (pidl != IntPtr.Zero) 60 | { 61 | SHOpenFolderAndSelectItems(pidl, 0, IntPtr.Zero, 0); 62 | Marshal.FreeCoTaskMem(pidl); 63 | } 64 | } 65 | 66 | // 打开文件夹 67 | public static void OpenFolder(string folderPath) 68 | { 69 | SHELLEXECUTEINFO sei = new SHELLEXECUTEINFO 70 | { 71 | cbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO)), 72 | lpVerb = "open", 73 | lpFile = folderPath, 74 | nShow = SW_SHOW 75 | }; 76 | ShellExecuteEx(ref sei); 77 | } 78 | 79 | // 打开项目,可以是文件夹、可执行文件、URL或其他文件类型 80 | public static void OpenItem(string path) 81 | { 82 | 83 | 84 | // 只取第一行作为路径 85 | path = path.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); 86 | 87 | if (string.IsNullOrEmpty(path)) 88 | { 89 | throw new ArgumentException("提供的路径无效或为空。"); 90 | } 91 | 92 | // 验证路径是否合法 93 | string extension = string.Empty; 94 | try 95 | { 96 | extension = Path.GetExtension(path); 97 | } 98 | catch (ArgumentException ex) 99 | { 100 | // 如果获取扩展名时出现异常,打印出错的路径并继续执行 101 | Console.WriteLine($"获取扩展名时出错。传入的路径为: {path}。错误信息: {ex.Message}"); 102 | return; // 由于无法获取扩展名,此处选择退出方法 103 | } 104 | 105 | 106 | 107 | // 如果没有扩展名,我们假定它是一个文件夹 108 | if (string.IsNullOrEmpty(extension)) 109 | { 110 | OpenFolder(path); 111 | } 112 | else if (File.Exists(path)) // 如果路径指向一个文件 113 | { 114 | extension = extension.ToLowerInvariant(); 115 | 116 | switch (extension) 117 | { 118 | case ".exe": 119 | // 如果是可执行文件,启动该应用程序 120 | Process.Start(path); 121 | break; 122 | case ".url": 123 | // 如果是URL文件,用默认浏览器打开它 124 | Process.Start(path); 125 | break; 126 | default: 127 | // 对于其他文件类型,使用默认程序打开 128 | // 对于其他文件类型,使用默认程序打开 129 | var sei = new SHELLEXECUTEINFO 130 | { 131 | cbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO)), 132 | lpVerb = "open", 133 | lpFile = path, 134 | nShow = SW_SHOW 135 | }; 136 | ShellExecuteEx(ref sei); // 注意这里使用了 ref 关键字 137 | 138 | break; 139 | } 140 | } 141 | else 142 | { 143 | // 如果文件不存在或不被识别,显示“打开方式”对话框 144 | // 对于其他文件类型,使用默认程序打开 145 | var sei = new SHELLEXECUTEINFO 146 | { 147 | cbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO)), 148 | lpVerb = "open", 149 | lpFile = path, 150 | nShow = SW_SHOW 151 | }; 152 | ShellExecuteEx(ref sei); // 注意这里使用了 ref 关键字 153 | 154 | } 155 | } 156 | } 157 | } 158 | 159 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions/ExplorerHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | 11 | 12 | 13 | namespace MenuFunctions 14 | { 15 | public class ExplorerHelper 16 | { 17 | 18 | [DllImport("shell32.dll", CharSet = CharSet.Auto)] 19 | static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo); 20 | 21 | [DllImport("shell32.dll")] 22 | static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, IntPtr apidl, uint dwFlags); 23 | 24 | [DllImport("shell32.dll")] 25 | static extern int SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr bindingContext, out IntPtr pidl, uint sfgaoIn, out uint psfgaoOut); 26 | 27 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 28 | public struct SHELLEXECUTEINFO 29 | { 30 | public int cbSize; 31 | public uint fMask; 32 | public IntPtr hwnd; 33 | [MarshalAs(UnmanagedType.LPTStr)] 34 | public string lpVerb; 35 | [MarshalAs(UnmanagedType.LPTStr)] 36 | public string lpFile; 37 | [MarshalAs(UnmanagedType.LPTStr)] 38 | public string lpParameters; 39 | [MarshalAs(UnmanagedType.LPTStr)] 40 | public string lpDirectory; 41 | public int nShow; 42 | public IntPtr hInstApp; 43 | public IntPtr lpIDList; 44 | [MarshalAs(UnmanagedType.LPTStr)] 45 | public string lpClass; 46 | public IntPtr hkeyClass; 47 | public uint dwHotKey; 48 | public IntPtr hIcon; 49 | public IntPtr hProcess; 50 | } 51 | 52 | private const int SW_SHOW = 5; 53 | 54 | // 打开文件夹并选择文件 55 | public static void OpenFolderAndSelectItem(string filePath) 56 | { 57 | IntPtr pidl = IntPtr.Zero; 58 | SHParseDisplayName(filePath, IntPtr.Zero, out pidl, 0, out _); 59 | if (pidl != IntPtr.Zero) 60 | { 61 | SHOpenFolderAndSelectItems(pidl, 0, IntPtr.Zero, 0); 62 | Marshal.FreeCoTaskMem(pidl); 63 | } 64 | } 65 | 66 | // 打开文件夹 67 | public static void OpenFolder(string folderPath) 68 | { 69 | SHELLEXECUTEINFO sei = new SHELLEXECUTEINFO 70 | { 71 | cbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO)), 72 | lpVerb = "open", 73 | lpFile = folderPath, 74 | nShow = SW_SHOW 75 | }; 76 | ShellExecuteEx(ref sei); 77 | } 78 | 79 | // 打开项目,可以是文件夹、可执行文件、URL或其他文件类型 80 | public static void OpenItem(string path) 81 | { 82 | 83 | 84 | // 只取第一行作为路径 85 | path = path.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(); 86 | 87 | if (string.IsNullOrEmpty(path)) 88 | { 89 | throw new ArgumentException("提供的路径无效或为空。"); 90 | } 91 | 92 | // 验证路径是否合法 93 | string extension = string.Empty; 94 | try 95 | { 96 | extension = Path.GetExtension(path); 97 | } 98 | catch (ArgumentException ex) 99 | { 100 | // 如果获取扩展名时出现异常,打印出错的路径并继续执行 101 | Console.WriteLine($"获取扩展名时出错。传入的路径为: {path}。错误信息: {ex.Message}"); 102 | return; // 由于无法获取扩展名,此处选择退出方法 103 | } 104 | 105 | 106 | 107 | // 如果没有扩展名,我们假定它是一个文件夹 108 | if (string.IsNullOrEmpty(extension)) 109 | { 110 | OpenFolder(path); 111 | } 112 | else if (File.Exists(path)) // 如果路径指向一个文件 113 | { 114 | extension = extension.ToLowerInvariant(); 115 | 116 | switch (extension) 117 | { 118 | case ".exe": 119 | // 如果是可执行文件,启动该应用程序 120 | Process.Start(path); 121 | break; 122 | case ".url": 123 | // 如果是URL文件,用默认浏览器打开它 124 | Process.Start(path); 125 | break; 126 | default: 127 | // 对于其他文件类型,使用默认程序打开 128 | // 对于其他文件类型,使用默认程序打开 129 | var sei = new SHELLEXECUTEINFO 130 | { 131 | cbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO)), 132 | lpVerb = "open", 133 | lpFile = path, 134 | nShow = SW_SHOW 135 | }; 136 | ShellExecuteEx(ref sei); // 注意这里使用了 ref 关键字 137 | 138 | break; 139 | } 140 | } 141 | else 142 | { 143 | // 如果文件不存在或不被识别,显示“打开方式”对话框 144 | // 对于其他文件类型,使用默认程序打开 145 | var sei = new SHELLEXECUTEINFO 146 | { 147 | cbSize = Marshal.SizeOf(typeof(SHELLEXECUTEINFO)), 148 | lpVerb = "open", 149 | lpFile = path, 150 | nShow = SW_SHOW 151 | }; 152 | ShellExecuteEx(ref sei); // 注意这里使用了 ref 关键字 153 | 154 | } 155 | } 156 | } 157 | } 158 | 159 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/Models/MenuItemConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.ComponentModel; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace MenuFunctions_Panel.Models 8 | { 9 | /// 10 | /// 菜单项配置类,对应 JSON 配置文件的数据结构 11 | /// 12 | public class MenuItemConfig : INotifyPropertyChanged 13 | { 14 | private string _text; 15 | private string _command; 16 | private string _programPath; 17 | private ObservableCollection _subItems; 18 | private List _fileTypes; 19 | private bool _showInRootMenu; 20 | private string _iconPath; 21 | private bool _isVisible = true; 22 | private bool _showOnFiles = true; 23 | private bool _appendCommandToEachPath = true; 24 | private bool _showOnFolderBackground = true; 25 | private bool _showOnFolder = true; 26 | private bool _onlyUsingProgram; 27 | private bool _runningProgramWithCMD; 28 | private bool _hideCmdWindow; 29 | private bool _keepCmdWindows; 30 | private bool _isSeparator; 31 | private bool _displayCompletePathAndCommand; 32 | private bool _useQuotes = true; 33 | private int _order; 34 | private bool _addFileTypeParameter; 35 | 36 | public string Text 37 | { 38 | get => _text; 39 | set { _text = value; OnPropertyChanged(); } 40 | } 41 | 42 | public string Command 43 | { 44 | get => _command; 45 | set { _command = value; OnPropertyChanged(); } 46 | } 47 | 48 | public string ProgramPath 49 | { 50 | get => _programPath; 51 | set { _programPath = value; OnPropertyChanged(); } 52 | } 53 | 54 | public ObservableCollection SubItems 55 | { 56 | get => _subItems; 57 | set { _subItems = value; OnPropertyChanged(); } 58 | } 59 | 60 | public List FileTypes 61 | { 62 | get => _fileTypes; 63 | set { _fileTypes = value; OnPropertyChanged(); } 64 | } 65 | 66 | public bool ShowInRootMenu 67 | { 68 | get => _showInRootMenu; 69 | set { _showInRootMenu = value; OnPropertyChanged(); } 70 | } 71 | 72 | public string IconPath 73 | { 74 | get => _iconPath; 75 | set { _iconPath = value; OnPropertyChanged(); } 76 | } 77 | 78 | public bool IsVisible 79 | { 80 | get => _isVisible; 81 | set { _isVisible = value; OnPropertyChanged(); } 82 | } 83 | 84 | public bool ShowOnFiles 85 | { 86 | get => _showOnFiles; 87 | set { _showOnFiles = value; OnPropertyChanged(); } 88 | } 89 | 90 | public bool AppendCommandToEachPath 91 | { 92 | get => _appendCommandToEachPath; 93 | set { _appendCommandToEachPath = value; OnPropertyChanged(); } 94 | } 95 | 96 | public bool ShowOnFolderBackground 97 | { 98 | get => _showOnFolderBackground; 99 | set { _showOnFolderBackground = value; OnPropertyChanged(); } 100 | } 101 | 102 | public bool ShowOnFolder 103 | { 104 | get => _showOnFolder; 105 | set { _showOnFolder = value; OnPropertyChanged(); } 106 | } 107 | 108 | public bool OnlyUsingProgram 109 | { 110 | get => _onlyUsingProgram; 111 | set { _onlyUsingProgram = value; OnPropertyChanged(); } 112 | } 113 | 114 | public bool RunningProgramWithCMD 115 | { 116 | get => _runningProgramWithCMD; 117 | set { _runningProgramWithCMD = value; OnPropertyChanged(); } 118 | } 119 | 120 | public bool HideCmdWindow 121 | { 122 | get => _hideCmdWindow; 123 | set { _hideCmdWindow = value; OnPropertyChanged(); } 124 | } 125 | 126 | public bool KeepCmdWindows 127 | { 128 | get => _keepCmdWindows; 129 | set { _keepCmdWindows = value; OnPropertyChanged(); } 130 | } 131 | 132 | public bool IsSeparator 133 | { 134 | get => _isSeparator; 135 | set { _isSeparator = value; OnPropertyChanged(); } 136 | } 137 | 138 | public bool DisplayCompletePathAndCommand 139 | { 140 | get => _displayCompletePathAndCommand; 141 | set { _displayCompletePathAndCommand = value; OnPropertyChanged(); } 142 | } 143 | 144 | public bool UseQuotes 145 | { 146 | get => _useQuotes; 147 | set { _useQuotes = value; OnPropertyChanged(); } 148 | } 149 | 150 | public int Order 151 | { 152 | get => _order; 153 | set { _order = value; OnPropertyChanged(); } 154 | } 155 | 156 | public bool AddFileTypeParameter 157 | { 158 | get => _addFileTypeParameter; 159 | set { _addFileTypeParameter = value; OnPropertyChanged(); } 160 | } 161 | 162 | public event PropertyChangedEventHandler PropertyChanged; 163 | 164 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 165 | { 166 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 167 | } 168 | 169 | public MenuItemConfig() 170 | { 171 | SubItems = new ObservableCollection(); 172 | FileTypes = new List(); 173 | } 174 | } 175 | } 176 | 177 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Controls; 10 | using System.Windows.Data; 11 | using System.Windows.Documents; 12 | using System.Windows.Input; 13 | using System.Windows.Media; 14 | using System.Windows.Media.Imaging; 15 | using System.Windows.Navigation; 16 | using System.Windows.Shapes; 17 | using MenuFunctions_Panel.Models; 18 | using MenuFunctions_Panel.ViewModels; 19 | using Microsoft.Win32; 20 | 21 | namespace MenuFunctions_Panel 22 | { 23 | /// 24 | /// MainWindow.xaml 的交互逻辑 25 | /// 26 | public partial class MainWindow : Window 27 | { 28 | private MainViewModel _viewModel; 29 | 30 | public MainWindow() 31 | { 32 | InitializeComponent(); 33 | 34 | _viewModel = new MainViewModel(); 35 | this.DataContext = _viewModel; 36 | 37 | // 尝试自动加载配置文件 38 | AutoLoadConfig(); 39 | } 40 | 41 | private void AutoLoadConfig() 42 | { 43 | // 尝试从 MenuFunctions 项目的 bin/Release 目录加载配置 44 | var possiblePaths = new[] 45 | { 46 | System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MenuConfig.json"), 47 | System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "MenuFunctions", "bin", "Release", "MenuConfig.json"), 48 | System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "..", "MenuFunctions", "bin", "Release", "MenuConfig.json") 49 | }; 50 | 51 | foreach (var path in possiblePaths) 52 | { 53 | try 54 | { 55 | var fullPath = System.IO.Path.GetFullPath(path); 56 | if (File.Exists(fullPath)) 57 | { 58 | _viewModel.LoadConfig(fullPath); 59 | return; 60 | } 61 | } 62 | catch { } 63 | } 64 | } 65 | 66 | private void LoadConfig_Click(object sender, RoutedEventArgs e) 67 | { 68 | var dialog = new OpenFileDialog 69 | { 70 | Filter = "JSON 文件|*.json|所有文件|*.*", 71 | Title = "打开配置文件" 72 | }; 73 | 74 | if (dialog.ShowDialog() == true) 75 | { 76 | _viewModel.LoadConfig(dialog.FileName); 77 | } 78 | } 79 | 80 | private void SaveConfig_Click(object sender, RoutedEventArgs e) 81 | { 82 | if (string.IsNullOrEmpty(_viewModel.ConfigFilePath)) 83 | { 84 | var dialog = new SaveFileDialog 85 | { 86 | Filter = "JSON 文件|*.json|所有文件|*.*", 87 | Title = "保存配置文件", 88 | FileName = "MenuConfig.json" 89 | }; 90 | 91 | if (dialog.ShowDialog() == true) 92 | { 93 | _viewModel.SaveConfig(dialog.FileName); 94 | } 95 | } 96 | else 97 | { 98 | _viewModel.SaveConfig(_viewModel.ConfigFilePath); 99 | } 100 | } 101 | 102 | private void AddMenuItem_Click(object sender, RoutedEventArgs e) 103 | { 104 | _viewModel.AddMenuItem(); 105 | } 106 | 107 | private void AddSubMenuItem_Click(object sender, RoutedEventArgs e) 108 | { 109 | _viewModel.AddSubMenuItem(); 110 | } 111 | 112 | private void AddSeparator_Click(object sender, RoutedEventArgs e) 113 | { 114 | _viewModel.AddSeparator(); 115 | } 116 | 117 | private void DeleteMenuItem_Click(object sender, RoutedEventArgs e) 118 | { 119 | _viewModel.DeleteMenuItem(); 120 | } 121 | 122 | private void MoveUp_Click(object sender, RoutedEventArgs e) 123 | { 124 | _viewModel.MoveUp(); 125 | } 126 | 127 | private void MoveDown_Click(object sender, RoutedEventArgs e) 128 | { 129 | _viewModel.MoveDown(); 130 | } 131 | 132 | private void MenuTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) 133 | { 134 | _viewModel.SelectedItem = e.NewValue as MenuItemConfig; 135 | } 136 | 137 | private void BrowseProgram_Click(object sender, RoutedEventArgs e) 138 | { 139 | var dialog = new OpenFileDialog 140 | { 141 | Filter = "可执行文件|*.exe|所有文件|*.*", 142 | Title = "选择程序" 143 | }; 144 | 145 | if (dialog.ShowDialog() == true) 146 | { 147 | if (_viewModel.SelectedItem != null) 148 | { 149 | _viewModel.SelectedItem.ProgramPath = dialog.FileName; 150 | } 151 | } 152 | } 153 | 154 | private void BrowseIcon_Click(object sender, RoutedEventArgs e) 155 | { 156 | var dialog = new OpenFileDialog 157 | { 158 | Filter = "图标文件|*.ico;*.png;*.jpg;*.jpeg;*.bmp|所有文件|*.*", 159 | Title = "选择图标" 160 | }; 161 | 162 | if (dialog.ShowDialog() == true) 163 | { 164 | if (_viewModel.SelectedItem != null) 165 | { 166 | _viewModel.SelectedItem.IconPath = dialog.FileName; 167 | } 168 | } 169 | } 170 | } 171 | 172 | /// 173 | /// 反向布尔值到可见性转换器 174 | /// 175 | public class InverseBooleanToVisibilityConverter : IValueConverter 176 | { 177 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 178 | { 179 | if (value is bool boolValue) 180 | { 181 | return boolValue ? Visibility.Collapsed : Visibility.Visible; 182 | } 183 | return Visibility.Collapsed; 184 | } 185 | 186 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 187 | { 188 | throw new NotImplementedException(); 189 | } 190 | } 191 | 192 | /// 193 | /// 非空判断转换器 194 | /// 195 | public class NotNullConverter : IValueConverter 196 | { 197 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 198 | { 199 | return value != null; 200 | } 201 | 202 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 203 | { 204 | throw new NotImplementedException(); 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /MenuFunctions/实现运行效果/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using 实现运行效果; 7 | using System.Runtime.InteropServices; 8 | using IWshRuntimeLibrary; // 需要添加 COM 引用:Windows Script Host Object Model 9 | 10 | class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | string customParameter = ""; // 预留的自定义参数 15 | // 循环接受输入并处理 16 | while (true) 17 | { 18 | Console.WriteLine("请输入命令(输入'exit'退出):"); 19 | string input = Console.ReadLine(); 20 | 21 | if (input.ToLower() == "exit") 22 | { 23 | break; 24 | } 25 | 26 | ProcessInputCommand(input, customParameter); 27 | } 28 | } 29 | 30 | static void ProcessInputCommand(string input, string customParameter) 31 | { 32 | // 检查并提前处理 -keep 和 -hide 参数 33 | bool hideWindow = input.Contains("-hide") || input.Contains("-隐藏"); 34 | bool keepWindowOpen = input.Contains("-keep") || input.Contains("-保持"); 35 | 36 | // 如果存在这些参数,先移除它们 37 | if (hideWindow || keepWindowOpen) 38 | { 39 | input = input.Replace("-hide", "").Replace("-隐藏", "") 40 | .Replace("-keep", "").Replace("-保持", "").Trim(); 41 | } 42 | 43 | var commands = input.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); 44 | 45 | foreach (var command in commands) 46 | { 47 | HandleCommand(command, customParameter, hideWindow, keepWindowOpen); 48 | } 49 | } 50 | 51 | static void HandleCommand(string command, string customParameter, bool hideWindow, bool keepWindowOpen) 52 | { 53 | var parts = SplitCommandLine(command); 54 | if (parts.Count == 0) 55 | { 56 | return; 57 | } 58 | 59 | var commandName = parts[0]; 60 | var arguments = string.Join(" ", parts.Skip(1)); 61 | var envFiles = GetExecutableFilesFromEnvironmentVariables(); 62 | 63 | var fullPath = envFiles.FirstOrDefault(file => Path.GetFileNameWithoutExtension(file).Equals(commandName, StringComparison.OrdinalIgnoreCase)); 64 | if (!string.IsNullOrEmpty(fullPath)) 65 | { 66 | // 检查是否是快捷方式,并获取目标路径 67 | if (Path.GetExtension(fullPath).ToLower() == ".lnk") 68 | { 69 | fullPath = GetShortcutTarget(fullPath); 70 | } 71 | 72 | if (!string.IsNullOrEmpty(fullPath)) 73 | { 74 | // 添加引号以处理路径中的空格 75 | fullPath = "\"" + fullPath + "\""; 76 | 77 | if (hideWindow || keepWindowOpen) 78 | { 79 | ExecuteCommandOrApplication(fullPath + " " + arguments, envFiles, customParameter, hideWindow, keepWindowOpen); 80 | } 81 | else 82 | { 83 | var processStartInfo = new ProcessStartInfo 84 | { 85 | FileName = fullPath, 86 | Arguments = arguments, 87 | UseShellExecute = false 88 | }; 89 | Process.Start(processStartInfo); 90 | } 91 | } 92 | } 93 | else if (System.IO.File.Exists(commandName) || Directory.Exists(commandName)) 94 | { 95 | Process.Start(commandName, arguments); 96 | } 97 | else if (IsWebUrl(command)) 98 | { 99 | ExplorerHelper.OpenItem(command); 100 | } 101 | else 102 | { 103 | ExecuteCommandOrApplication(command, envFiles, customParameter, hideWindow, keepWindowOpen); 104 | } 105 | } 106 | 107 | static string GetShortcutTarget(string shortcutPath) 108 | { 109 | if (System.IO.File.Exists(shortcutPath)) 110 | { 111 | WshShell shell = new WshShell(); 112 | IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(shortcutPath); 113 | return shortcut.TargetPath; 114 | } 115 | return null; 116 | } 117 | 118 | 119 | 120 | 121 | static List SplitCommandLine(string commandLine) 122 | { 123 | var parts = new List(); 124 | var currentPart = ""; 125 | var inQuotes = false; 126 | 127 | foreach (var c in commandLine) 128 | { 129 | if (c == '\"') 130 | { 131 | inQuotes = !inQuotes; 132 | } 133 | else if (c == ' ' && !inQuotes) 134 | { 135 | if (!string.IsNullOrEmpty(currentPart)) 136 | { 137 | parts.Add(currentPart); 138 | currentPart = ""; 139 | } 140 | } 141 | else 142 | { 143 | currentPart += c; 144 | } 145 | } 146 | 147 | if (!string.IsNullOrEmpty(currentPart)) 148 | { 149 | parts.Add(currentPart); 150 | } 151 | 152 | return parts.Select(p => TrimMatchingQuotes(p)).ToList(); 153 | } 154 | 155 | static string TrimMatchingQuotes(string input) 156 | { 157 | if ((input.Length >= 2) && 158 | (input[0] == '\"') && (input[input.Length - 1] == '\"')) 159 | { 160 | return input.Substring(1, input.Length - 2); 161 | } 162 | return input; 163 | } 164 | 165 | static List GetExecutableFiles() 166 | { 167 | var files = new List(); 168 | // 添加环境变量的路径下的文件 169 | // 添加程序运行目录下的文件 170 | // 请根据您的需求和环境添加相应的代码来填充这个列表 171 | return files; 172 | } 173 | 174 | static bool IsLocalPath(string path) 175 | { 176 | return Path.IsPathRooted(path) && !Path.GetPathRoot(path).StartsWith("\\\\"); 177 | } 178 | 179 | static bool IsNetworkPath(string path) 180 | { 181 | return path.StartsWith("\\\\"); 182 | } 183 | 184 | static bool IsWebUrl(string url) 185 | { 186 | // 首先检查是否为标准的HTTP或HTTPS URL 187 | if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) 188 | { 189 | return true; 190 | } 191 | 192 | // 检查是否含有反斜杠,如果有,则不是网址 193 | if (url.Contains('\\')) 194 | { 195 | return false; 196 | } 197 | 198 | // 没有 HTTP/HTTPS 前缀的情况 199 | string domainPart = url.Contains('/') ? url.Split('/')[0] : url; 200 | int dotCount = domainPart.Count(c => c == '.'); 201 | 202 | // 如果有两个或者更多的点,则认为是网址 203 | return dotCount >= 2; 204 | } 205 | 206 | 207 | 208 | static void ExecuteCommandOrApplication(string command, List files, string customParameter, bool hideWindow, bool keepWindowOpen) 209 | { 210 | try 211 | { 212 | var parts = SplitCommandLine(command); 213 | var commandName = parts[0]; 214 | var arguments = string.Join(" ", parts.Skip(1)).Trim(); 215 | 216 | var processStartInfo = new ProcessStartInfo("cmd") 217 | { 218 | UseShellExecute = false, 219 | CreateNoWindow = hideWindow, 220 | RedirectStandardOutput = hideWindow, 221 | RedirectStandardError = hideWindow 222 | }; 223 | 224 | string cmdArguments = $"/c {commandName} {arguments} {customParameter}".Trim(); 225 | if (keepWindowOpen) 226 | { 227 | cmdArguments += " && pause"; 228 | } 229 | 230 | processStartInfo.Arguments = cmdArguments; 231 | 232 | Process.Start(processStartInfo); 233 | } 234 | catch (Exception ex) 235 | { 236 | Console.WriteLine("执行命令时出错: " + ex.Message); 237 | // 这里可以添加更多错误处理逻辑 238 | } 239 | } 240 | 241 | 242 | static List GetExecutableFilesFromEnvironmentVariables() 243 | { 244 | var paths = new List(); 245 | var systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); 246 | var userPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.User); 247 | 248 | // 添加系统环境变量路径中的文件 249 | paths.AddRange(GetFilesFromPath(systemPath)); 250 | // 添加用户环境变量路径中的文件 251 | paths.AddRange(GetFilesFromPath(userPath)); 252 | 253 | return paths.Distinct().ToList(); 254 | } 255 | 256 | static IEnumerable GetFilesFromPath(string path) 257 | { 258 | var files = new List(); 259 | if (!string.IsNullOrEmpty(path)) 260 | { 261 | var directories = path.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); 262 | foreach (var dir in directories) 263 | { 264 | try 265 | { 266 | if (Directory.Exists(dir)) 267 | { 268 | // 获取可执行文件和快捷方式 269 | files.AddRange(Directory.EnumerateFiles(dir, "*.exe")); 270 | files.AddRange(Directory.EnumerateFiles(dir, "*.lnk")); 271 | } 272 | } 273 | catch (Exception ex) 274 | { 275 | // 异常处理 276 | } 277 | } 278 | } 279 | return files; 280 | } 281 | 282 | 283 | 284 | } 285 | -------------------------------------------------------------------------------- /MenuFunctions/测试/RunProgram.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Runtime.InteropServices; 9 | using IWshRuntimeLibrary; // 需要添加 COM 引用:Windows Script Host Object Model 10 | 11 | namespace 测试 12 | { 13 | public class RunProgram 14 | { 15 | static void MainStart(string[] args) 16 | { 17 | 18 | // 循环接受输入并处理 19 | while (true) 20 | { 21 | Console.WriteLine("请输入命令(输入'exit'退出):"); 22 | string input = Console.ReadLine(); 23 | 24 | if (input.ToLower() == "exit") 25 | { 26 | break; 27 | } 28 | string customParameter = ""; // 预留的自定义参数 29 | ProcessInputCommand(input, customParameter); 30 | } 31 | } 32 | 33 | public static void ProcessInputCommand(string input, string customParameter) 34 | { 35 | // 检查并提前处理 -keep 和 -hide 参数 36 | bool hideWindow = input.Contains("-hide") || input.Contains("-隐藏"); 37 | bool keepWindowOpen = input.Contains("-keep") || input.Contains("-保持"); 38 | 39 | // 如果存在这些参数,先移除它们 40 | if (hideWindow || keepWindowOpen) 41 | { 42 | input = input.Replace("-hide", "").Replace("-隐藏", "") 43 | .Replace("-keep", "").Replace("-保持", "").Trim(); 44 | } 45 | 46 | var commands = input.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); 47 | 48 | foreach (var command in commands) 49 | { 50 | HandleCommand(command, customParameter, hideWindow, keepWindowOpen); 51 | } 52 | } 53 | 54 | static void HandleCommand(string command, string customParameter, bool hideWindow, bool keepWindowOpen) 55 | { 56 | var parts = SplitCommandLine(command); 57 | if (parts.Count == 0) 58 | { 59 | return; 60 | } 61 | 62 | var commandName = parts[0]; 63 | var arguments = string.Join(" ", parts.Skip(1)); 64 | var envFiles = GetExecutableFilesFromEnvironmentVariables(); 65 | 66 | var fullPath = envFiles.FirstOrDefault(file => Path.GetFileNameWithoutExtension(file).Equals(commandName, StringComparison.OrdinalIgnoreCase)); 67 | if (!string.IsNullOrEmpty(fullPath)) 68 | { 69 | // 检查是否是快捷方式,并获取目标路径 70 | if (Path.GetExtension(fullPath).ToLower() == ".lnk") 71 | { 72 | fullPath = GetShortcutTarget(fullPath); 73 | } 74 | 75 | if (!string.IsNullOrEmpty(fullPath)) 76 | { 77 | // 添加引号以处理路径中的空格 78 | fullPath = "\"" + fullPath + "\""; 79 | 80 | if (hideWindow || keepWindowOpen) 81 | { 82 | ExecuteCommandOrApplication(fullPath + " " + arguments, envFiles, customParameter, hideWindow, keepWindowOpen); 83 | } 84 | else 85 | { 86 | var processStartInfo = new ProcessStartInfo 87 | { 88 | FileName = fullPath, 89 | Arguments = arguments, 90 | UseShellExecute = false 91 | }; 92 | Process.Start(processStartInfo); 93 | } 94 | } 95 | } 96 | else if (System.IO.File.Exists(commandName) || Directory.Exists(commandName)) 97 | { 98 | Process.Start(commandName, arguments); 99 | } 100 | else if (IsWebUrl(command)) 101 | { 102 | ExplorerHelper.OpenItem(command); 103 | } 104 | else 105 | { 106 | ExecuteCommandOrApplication(command, envFiles, customParameter, hideWindow, keepWindowOpen); 107 | } 108 | } 109 | 110 | static string GetShortcutTarget(string shortcutPath) 111 | { 112 | if (System.IO.File.Exists(shortcutPath)) 113 | { 114 | WshShell shell = new WshShell(); 115 | IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(shortcutPath); 116 | return shortcut.TargetPath; 117 | } 118 | return null; 119 | } 120 | 121 | 122 | 123 | 124 | static List SplitCommandLine(string commandLine) 125 | { 126 | var parts = new List(); 127 | var currentPart = ""; 128 | var inQuotes = false; 129 | 130 | foreach (var c in commandLine) 131 | { 132 | if (c == '\"') 133 | { 134 | inQuotes = !inQuotes; 135 | } 136 | else if (c == ' ' && !inQuotes) 137 | { 138 | if (!string.IsNullOrEmpty(currentPart)) 139 | { 140 | parts.Add(currentPart); 141 | currentPart = ""; 142 | } 143 | } 144 | else 145 | { 146 | currentPart += c; 147 | } 148 | } 149 | 150 | if (!string.IsNullOrEmpty(currentPart)) 151 | { 152 | parts.Add(currentPart); 153 | } 154 | 155 | return parts.Select(p => TrimMatchingQuotes(p)).ToList(); 156 | } 157 | 158 | static string TrimMatchingQuotes(string input) 159 | { 160 | if ((input.Length >= 2) && 161 | (input[0] == '\"') && (input[input.Length - 1] == '\"')) 162 | { 163 | return input.Substring(1, input.Length - 2); 164 | } 165 | return input; 166 | } 167 | 168 | static List GetExecutableFiles() 169 | { 170 | var files = new List(); 171 | // 添加环境变量的路径下的文件 172 | // 添加程序运行目录下的文件 173 | // 请根据您的需求和环境添加相应的代码来填充这个列表 174 | return files; 175 | } 176 | 177 | static bool IsLocalPath(string path) 178 | { 179 | return Path.IsPathRooted(path) && !Path.GetPathRoot(path).StartsWith("\\\\"); 180 | } 181 | 182 | static bool IsNetworkPath(string path) 183 | { 184 | return path.StartsWith("\\\\"); 185 | } 186 | 187 | static bool IsWebUrl(string url) 188 | { 189 | // 首先检查是否为标准的HTTP或HTTPS URL 190 | if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) 191 | { 192 | return true; 193 | } 194 | 195 | // 检查是否含有反斜杠,如果有,则不是网址 196 | if (url.Contains('\\')) 197 | { 198 | return false; 199 | } 200 | 201 | // 没有 HTTP/HTTPS 前缀的情况 202 | string domainPart = url.Contains('/') ? url.Split('/')[0] : url; 203 | int dotCount = domainPart.Count(c => c == '.'); 204 | 205 | // 如果有两个或者更多的点,则认为是网址 206 | return dotCount >= 2; 207 | } 208 | 209 | 210 | 211 | static void ExecuteCommandOrApplication(string command, List files, string customParameter, bool hideWindow, bool keepWindowOpen) 212 | { 213 | try 214 | { 215 | var parts = SplitCommandLine(command); 216 | var commandName = parts[0]; 217 | var arguments = string.Join(" ", parts.Skip(1)).Trim(); 218 | 219 | var processStartInfo = new ProcessStartInfo("cmd") 220 | { 221 | UseShellExecute = false, 222 | CreateNoWindow = hideWindow, 223 | RedirectStandardOutput = hideWindow, 224 | RedirectStandardError = hideWindow 225 | }; 226 | 227 | string cmdArguments = $"/c {commandName} {arguments} {customParameter}".Trim(); 228 | if (keepWindowOpen) 229 | { 230 | cmdArguments += " && pause"; 231 | } 232 | 233 | processStartInfo.Arguments = cmdArguments; 234 | 235 | Process.Start(processStartInfo); 236 | } 237 | catch (Exception ex) 238 | { 239 | Console.WriteLine("执行命令时出错: " + ex.Message); 240 | // 这里可以添加更多错误处理逻辑 241 | } 242 | } 243 | 244 | 245 | static List GetExecutableFilesFromEnvironmentVariables() 246 | { 247 | var paths = new List(); 248 | var systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); 249 | var userPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.User); 250 | 251 | // 添加系统环境变量路径中的文件 252 | paths.AddRange(GetFilesFromPath(systemPath)); 253 | // 添加用户环境变量路径中的文件 254 | paths.AddRange(GetFilesFromPath(userPath)); 255 | 256 | return paths.Distinct().ToList(); 257 | } 258 | 259 | static IEnumerable GetFilesFromPath(string path) 260 | { 261 | var files = new List(); 262 | if (!string.IsNullOrEmpty(path)) 263 | { 264 | var directories = path.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); 265 | foreach (var dir in directories) 266 | { 267 | try 268 | { 269 | if (Directory.Exists(dir)) 270 | { 271 | // 获取可执行文件和快捷方式 272 | files.AddRange(Directory.EnumerateFiles(dir, "*.exe")); 273 | files.AddRange(Directory.EnumerateFiles(dir, "*.lnk")); 274 | } 275 | } 276 | catch (Exception ex) 277 | { 278 | Console.WriteLine("执行命令时出错: " + ex.Message); 279 | // 异常处理 280 | } 281 | } 282 | } 283 | return files; 284 | } 285 | 286 | 287 | 288 | } 289 | 290 | } 291 | 292 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/ViewModels/MainViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.ComponentModel; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.CompilerServices; 7 | using System.Windows; 8 | using MenuFunctions_Panel.Models; 9 | using Newtonsoft.Json; 10 | 11 | namespace MenuFunctions_Panel.ViewModels 12 | { 13 | public class MainViewModel : INotifyPropertyChanged 14 | { 15 | private ObservableCollection _menuItems; 16 | private MenuItemConfig _selectedItem; 17 | private string _configFilePath; 18 | private string _fileTypesText; 19 | 20 | public ObservableCollection MenuItems 21 | { 22 | get => _menuItems; 23 | set { _menuItems = value; OnPropertyChanged(); } 24 | } 25 | 26 | public MenuItemConfig SelectedItem 27 | { 28 | get => _selectedItem; 29 | set 30 | { 31 | _selectedItem = value; 32 | OnPropertyChanged(); 33 | UpdateFileTypesText(); 34 | } 35 | } 36 | 37 | public string FileTypesText 38 | { 39 | get => _fileTypesText; 40 | set 41 | { 42 | _fileTypesText = value; 43 | OnPropertyChanged(); 44 | UpdateFileTypesList(); 45 | } 46 | } 47 | 48 | public string ConfigFilePath 49 | { 50 | get => _configFilePath; 51 | set { _configFilePath = value; OnPropertyChanged(); } 52 | } 53 | 54 | public event PropertyChangedEventHandler PropertyChanged; 55 | 56 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 57 | { 58 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 59 | } 60 | 61 | public MainViewModel() 62 | { 63 | MenuItems = new ObservableCollection(); 64 | } 65 | 66 | private void UpdateFileTypesText() 67 | { 68 | if (SelectedItem?.FileTypes != null && SelectedItem.FileTypes.Count > 0) 69 | { 70 | FileTypesText = string.Join(", ", SelectedItem.FileTypes); 71 | } 72 | else 73 | { 74 | FileTypesText = ""; 75 | } 76 | } 77 | 78 | private void UpdateFileTypesList() 79 | { 80 | if (SelectedItem != null) 81 | { 82 | if (string.IsNullOrWhiteSpace(FileTypesText)) 83 | { 84 | SelectedItem.FileTypes = new System.Collections.Generic.List(); 85 | } 86 | else 87 | { 88 | var types = FileTypesText.Split(new[] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries) 89 | .Select(s => s.Trim()) 90 | .ToList(); 91 | SelectedItem.FileTypes = types; 92 | } 93 | } 94 | } 95 | 96 | public void LoadConfig(string filePath) 97 | { 98 | try 99 | { 100 | if (!File.Exists(filePath)) 101 | { 102 | MessageBox.Show($"配置文件不存在: {filePath}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); 103 | return; 104 | } 105 | 106 | var json = File.ReadAllText(filePath); 107 | var items = JsonConvert.DeserializeObject>(json); 108 | 109 | if (items != null) 110 | { 111 | // 转换子项为 ObservableCollection 112 | ConvertSubItemsToObservable(items); 113 | MenuItems = items; 114 | ConfigFilePath = filePath; 115 | } 116 | } 117 | catch (Exception ex) 118 | { 119 | MessageBox.Show($"加载配置文件失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); 120 | } 121 | } 122 | 123 | private void ConvertSubItemsToObservable(ObservableCollection items) 124 | { 125 | foreach (var item in items) 126 | { 127 | if (item.SubItems != null && !(item.SubItems is ObservableCollection)) 128 | { 129 | var observableSubItems = new ObservableCollection(item.SubItems); 130 | item.SubItems = observableSubItems; 131 | } 132 | 133 | if (item.SubItems != null && item.SubItems.Count > 0) 134 | { 135 | ConvertSubItemsToObservable(item.SubItems); 136 | } 137 | } 138 | } 139 | 140 | public void SaveConfig(string filePath) 141 | { 142 | try 143 | { 144 | var json = JsonConvert.SerializeObject(MenuItems, Formatting.Indented); 145 | File.WriteAllText(filePath, json); 146 | ConfigFilePath = filePath; 147 | MessageBox.Show("配置保存成功!", "提示", MessageBoxButton.OK, MessageBoxImage.Information); 148 | } 149 | catch (Exception ex) 150 | { 151 | MessageBox.Show($"保存配置文件失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); 152 | } 153 | } 154 | 155 | public void AddMenuItem() 156 | { 157 | var newItem = new MenuItemConfig 158 | { 159 | Text = "新建菜单项", 160 | IsVisible = true, 161 | ShowOnFiles = true, 162 | ShowOnFolderBackground = true, 163 | ShowOnFolder = true, 164 | UseQuotes = true, 165 | AppendCommandToEachPath = true, 166 | Order = MenuItems.Count 167 | }; 168 | 169 | MenuItems.Add(newItem); 170 | SelectedItem = newItem; 171 | } 172 | 173 | public void AddSubMenuItem() 174 | { 175 | if (SelectedItem == null) 176 | { 177 | MessageBox.Show("请先选择一个父菜单项", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); 178 | return; 179 | } 180 | 181 | var newItem = new MenuItemConfig 182 | { 183 | Text = "新建子菜单项", 184 | IsVisible = true, 185 | ShowOnFiles = true, 186 | ShowOnFolderBackground = true, 187 | ShowOnFolder = true, 188 | UseQuotes = true, 189 | AppendCommandToEachPath = true, 190 | Order = SelectedItem.SubItems.Count 191 | }; 192 | 193 | if (SelectedItem.SubItems == null) 194 | { 195 | SelectedItem.SubItems = new ObservableCollection(); 196 | } 197 | 198 | SelectedItem.SubItems.Add(newItem); 199 | } 200 | 201 | public void DeleteMenuItem() 202 | { 203 | if (SelectedItem == null) 204 | { 205 | MessageBox.Show("请先选择要删除的菜单项", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); 206 | return; 207 | } 208 | 209 | var result = MessageBox.Show($"确定要删除菜单项 '{SelectedItem.Text}' 吗?", 210 | "确认删除", MessageBoxButton.YesNo, MessageBoxImage.Question); 211 | 212 | if (result == MessageBoxResult.Yes) 213 | { 214 | RemoveItemFromCollection(MenuItems, SelectedItem); 215 | } 216 | } 217 | 218 | private bool RemoveItemFromCollection(ObservableCollection collection, MenuItemConfig item) 219 | { 220 | if (collection.Contains(item)) 221 | { 222 | collection.Remove(item); 223 | return true; 224 | } 225 | 226 | foreach (var parentItem in collection) 227 | { 228 | if (parentItem.SubItems != null && RemoveItemFromCollection(parentItem.SubItems, item)) 229 | { 230 | return true; 231 | } 232 | } 233 | 234 | return false; 235 | } 236 | 237 | public void MoveUp() 238 | { 239 | if (SelectedItem == null) return; 240 | 241 | var collection = FindParentCollection(MenuItems, SelectedItem); 242 | if (collection != null) 243 | { 244 | var index = collection.IndexOf(SelectedItem); 245 | if (index > 0) 246 | { 247 | collection.Move(index, index - 1); 248 | UpdateOrder(collection); 249 | } 250 | } 251 | } 252 | 253 | public void MoveDown() 254 | { 255 | if (SelectedItem == null) return; 256 | 257 | var collection = FindParentCollection(MenuItems, SelectedItem); 258 | if (collection != null) 259 | { 260 | var index = collection.IndexOf(SelectedItem); 261 | if (index < collection.Count - 1) 262 | { 263 | collection.Move(index, index + 1); 264 | UpdateOrder(collection); 265 | } 266 | } 267 | } 268 | 269 | private ObservableCollection FindParentCollection( 270 | ObservableCollection collection, MenuItemConfig item) 271 | { 272 | if (collection.Contains(item)) 273 | { 274 | return collection; 275 | } 276 | 277 | foreach (var parentItem in collection) 278 | { 279 | if (parentItem.SubItems != null) 280 | { 281 | var result = FindParentCollection(parentItem.SubItems, item); 282 | if (result != null) 283 | { 284 | return result; 285 | } 286 | } 287 | } 288 | 289 | return null; 290 | } 291 | 292 | private void UpdateOrder(ObservableCollection collection) 293 | { 294 | for (int i = 0; i < collection.Count; i++) 295 | { 296 | collection[i].Order = i; 297 | } 298 | } 299 | 300 | public void AddSeparator() 301 | { 302 | var separator = new MenuItemConfig 303 | { 304 | Text = "分隔线", 305 | IsSeparator = true, 306 | IsVisible = true, 307 | Order = MenuItems.Count 308 | }; 309 | 310 | MenuItems.Add(separator); 311 | } 312 | } 313 | } 314 | 315 | -------------------------------------------------------------------------------- /MenuFunctions/MenuFunctions_Panel/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 39 | 45 | 46 | 52 | 58 | 64 | 70 | 71 | 77 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 |