Mutex 是 C# 最稳妥的跨进程单实例方案,通过创建命名互斥量判断实例唯一性:成功创建并 WaitOne(0) 返回 true 则运行,否则退出;需设 firstOwner=false、加 Local 前缀防跨会话冲突,并在退出前 ReleaseMutex。

怎么用 Mutex 检查并阻止重复启动
直接靠 Mutex 实现单实例,是 C# 最稳妥的跨进程方案。它不依赖窗口句柄或进程名匹配,也不怕用户改名启动,本质是系统级命名同步对象。
关键不是“锁住程序”,而是“尝试创建一个带名字的互斥量”:成功说明是首个实例;失败且 WaitOne(0) 返回 false,就该退出。
- 必须用
new Mutex(false, "YourApp.Unique.Name")—— 第一个参数设为false,避免当前线程自动获得所有权(否则后续判断逻辑会错) - 名字建议含公司 / 产品标识,避免和其他软件冲突,比如
"MyCompany.MyTool.SingleInstance" - 创建后立刻调用
mutex.WaitOne(0)判断是否抢到了——返回true才继续运行,false就直接return或Environment.Exit() - 别忘了在主窗体关闭时调用
mutex.ReleaseMutex(),否则下次启动仍会认为被占用(尤其调试时容易卡死)
static class Program {[STAThread] static void Main() { using (var mutex = new Mutex(false, "MyApp.Single")) {if (!mutex.WaitOne(0)) {MessageBox.Show(" 程序已在运行 "); return; } <pre class="brush:php;toolbar:false;"> Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MainForm()); mutex.ReleaseMutex(); // 必须释放} }
}
为什么不能只靠 Process.GetProcessesByName
这个方法看似简单,实际在多用户、UAC、沙盒或 .NET Core/.NET 5+ 跨平台场景下完全不可靠。
- 不同用户会话下的同名进程互相不可见(比如服务账户 vs 普通用户)
- Windows 10/11 启用“快速启动”后,上次关机残留的进程可能未彻底清理,导致误判
- .NET 6+ 在 Linux/macOS 上根本没
Process.GetProcessesByName的完整实现 - 用户把 exe 改个名就能绕过——而
Mutex名字是你硬编码的,改名无效
Mutex 创建失败的常见错误信息和应对
遇到 UnauthorizedAccessException 或 IOException 通常不是代码写错了,而是权限或路径问题。
-
System.UnauthorizedAccessException: Access to the path is denied.—— 多见于以管理员身份运行旧版程序后遗留了内核对象,普通用户再启就无权访问。解决:重启电脑,或改用更安全的名字前缀(如加"Local") -
System.IO.IOException: The mutex name already exists.—— 这其实是正常现象,表示互斥量已被创建,不是错误。重点看WaitOne返回值,而不是构造函数是否抛异常 - 调试时反复启动容易卡住:因为上一次没执行到
ReleaseMutex()就崩溃了。建议开发阶段加超时(如WaitOne(1000))并记录日志,上线后再切回0
WinForms 主窗体已启动后如何激活旧实例
单纯退出新实例不够友好。想把焦点切回去,得配合 Windows API 和进程通信。
- 旧实例需要监听命名管道或 WM_COPYDATA 消息,新实例通过
FindWindow+SetForegroundWindow唤起它 - 不要在
Main里做窗口查找——此时旧实例可能还没完成初始化。推荐用PostMessage发个自定义消息(如WM_USER + 100),由主窗体的WndProc处理并激活 - 注意 UAC 隔离:高权限进程无法向低权限进程发消息。如果程序需要管理员权限,所有实例都得统一以管理员方式启动,否则激活失败
Mutex 的名字作用域、释放时机、跨会话可见性这些细节,稍不注意就会让单实例逻辑在特定机器上失效。特别是 ReleaseMutex 忘写、名字没加 Local 前缀、或者在非 UI 线程里调用 SetForegroundWindow——这些问题不会报错,但会让程序“偶尔双开”或“唤不起旧窗口”,排查起来特别费时间。