单例模式代码理解

cs 复制代码
 #region 在进程中查找是否已经有实例在运行`
   //确保程序只运行一个实例
   public static Process RunningInstance()`
   {`
       Process currentProcess = Process.GetCurrentProcess();  //当前运行中的程序`
       Process[] Processes = Process.GetProcessesByName(currentProcess.ProcessName);     //从操作系统中查看是否有当前程序机`
       //  遍历与当前进程名称相同的进程列表`
       foreach (Process process in Processes)`
       {
           //  如果实例已经存在,则忽略当前进程`
           if (process.Id != currentProcess.Id)    //如果当前进程ID和`
           {
               //保证要打开的进程 同 已经存在的进程来自同一个文件路径`
               if (Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == currentProcess.MainModule.FileName)`
               {
                   // 返回另一个进程实例`
                   return process;`
               }
         }
       }
       return null; //找不到其他进程实例,返回nulL。          
   }
   #endregion

这段代码中有一个很隐晦的错误。

先来解释

问题1

Process.GetCurrentProcess() 返回的是当前执行这行代码的程序本身的进程对象,而不是 Visual Studio(IDE)的进程。

  • 当你通过 Visual Studio 按 F5 (启动调试)或 Ctrl+F5 (不调试直接启动)运行你的应用程序时,操作系统会为你的应用程序(例如 MyApp.exe)创建一个新的进程。

  • 你的代码(Process.GetCurrentProcess())是在这个新进程(MyApp.exe)中执行的,因此它返回的是这个应用程序进程的信息。

  • Visual Studio(devenv.exe)是另一个独立的进程 ,它与你的应用程序进程是两个不同的实体。GetCurrentProcess() 永远不会返回 Visual Studio 的进程。

🔍 常见误解澄清

有些开发者可能会混淆,因为 Visual Studio 启动了程序,但启动后程序就独立运行了(除非附加了调试器)。 GetCurrentProcess() 获取的是代码所在进程,与启动它的父进程(Visual Studio)无关。

如果你想获取启动当前进程的父进程(即 Visual Studio) ,这需要通过其他方式(如 WMI 或调用 Windows API),并且需要管理员权限,且不总是可靠。Process 类没有直接提供获取父进程的简单方法。

问题2

cs 复制代码
 Process[] Processes = Process.GetProcessesByName(currentProcess.ProcessName);

作用: 扫描整个本地计算机 (所有用户会话、所有桌面、后台服务),查找所有进程名称 与当前进程名称相同的进程,并将它们以 Process 对象数组的形式返回。

  • 范围:整个操作系统,不是只针对当前用户,也不是只针对当前会话。

  • 返回值 :数组 ,可以包含0个、1个或多个进程对象。

  • 进程名称 :不带扩展名,例如 "QQ"、"WeChat"、"MyApp"。

如果一个程序允许运行多个实例(例如你可以打开多个记事本、多个QQ聊天窗口),那么:

cs 复制代码
 Process[] notepads = Process.GetProcessesByName("notepad");
  • 如果你打开了3个记事本,notepads 数组的长度就是 3。

  • 每个元素对应一个正在运行的 notepad.exe 进程对象。

所以,当你用当前进程名去查找时,返回的数组一定至少包含当前进程本身(只要当前进程还在运行),还可能包含其他已经运行的同名实例。

问题三

cs 复制代码
  if (Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == currentProcess.MainModule.FileName)

这段代码的逻辑存在根本性的方向错误 ,导致它**永远无法实现"保证要打开的进程同已经存在的进程来自同一个文件路径"**这一目标。

❌ 错误核心:比较对象完全错误

原本意图 : 遍历到的 process 是另一个同名进程(候选已存在实例),需要判断该进程的可执行文件路径 是否与当前程序的路径一致。 若一致,则认为它们是同一个应用程序的不同实例,应返回该进程。

实际代码:
cs 复制代码
 if (Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == currentProcess.MainModule.FileName)
  • 左值 :当前程序集的位置(Assembly.GetExecutingAssembly().Location),也就是当前进程的可执行文件路径。

  • 右值 :currentProcess.MainModule.FileName,这同样是当前进程的可执行文件路径。

currentProcess 是 Process.GetCurrentProcess(),也就是当前进程本身。 currentProcess.MainModule.FileName 与 Assembly.GetExecutingAssembly().Location 指向同一个文件

结果:

这个条件永远为 true (除非路径规范化问题导致字符串不等)。 因此,任何与当前进程同名的进程(无论其真实路径如何)都会被误判为"来自同一个文件路径",并被作为已存在的实例返回。

理解:

if判断的逻辑是,我在我的操作系统中找到很多个和当前执行程序一样的放入到数组中,只要数组中的一个程序,如果数组中的第一个程序的ID不和当前执行程序的ID相同,并且它们的地址相同,就直接返回当前操作系统中的程序,结束该方法,如果第一个ID和当前执行的程序ID相同的话就返回null,代表用当前执行程序即可。

纠错:
✅ 代码真实逻辑(假设路径比较错误已修正)
cs 复制代码
foreach (Process process in Processes)      // 遍历所有同名进程
{
    // 条件1:不是当前进程自己
    if (process.Id != currentProcess.Id)
    {
        // 条件2:该进程的exe路径与当前程序exe路径相同
        if (process.MainModule.FileName == 当前程序路径)
        {
            return process;   // ✔ 找到另一个真实实例,返回它
        }
    }
    // 如果process.Id == currentProcess.Id → 跳过,继续检查下一个
}
return null;  // 没有找到其他实例

你描述为:

只要数组中的第一个程序的ID不和当前执行程序的ID相同,就直接返回当前操作系统中的程序,结束该方法

这是错误的,原因:

  1. 不是只检查第一个 :代码遍历整个数组 ,不是只检查第一个。即使第一个ID不同,还需要通过路径比较 才会返回;如果路径不同,不会返回,而是继续检查下一个进程。

  2. 不是无条件返回 :ID不同只是进入条件2的门票,条件2(路径相同)必须同时满足才会返回。

  3. 返回值不是"当前操作系统中的程序" :返回的是另一个已经运行的同路径进程,而不是"当前操作系统中的程序"这种模糊概念。

📌 总结
  • if (process.Id != currentProcess.Id) 只是排除自身,不是决定返回的唯一条件。

  • 必须同时满足"不是自己" + "路径相同" 才会认为"已存在另一个实例",并返回该进程。

  • 遍历是全数组扫描,不是只检查第一个。

  • 如果所有进程都不满足条件,最终返回 null,表示"我是唯一实例,可以启动"。

你问的 process.MainModule是 C# 中 System.Diagnostics.Process 类的一个核心属性。结合你之前学习的单实例检测代码,理解这个属性对于为什么代码会崩溃 以及如何正确编写至关重要。

你问的 process.MainModule(你写的是 Maindule,应该是笔误)是 C# 中 System.Diagnostics.Process 类的一个核心属性。结合你之前学习的单实例检测代码,理解这个属性对于为什么代码会崩溃 以及如何正确编写至关重要。

1. Process.MainModule 是什么?

定义 :MainModule 是一个 ProcessModule 类型的属性,代表进程加载的主模块 。 对于绝大多数 .NET 应用程序或 Windows 可执行文件,主模块就是那个 .exe 文件本身。

最常用子属性 :.FileName ------ 获取该模块的完整路径 (如 C:\Windows\System32\notepad.exe)。 这也是你在单实例检测中想拿来比较"两个进程是否来自同一个 exe 文件"的那个值。

相关推荐
weixin_307779131 小时前
C++代码实现MATLAB中的dlarray函数功能
开发语言·c++·算法·matlab
一水鉴天4 小时前
映射、哈希表与哈斯图:计算机科学的三种基线 20261003(元宝)
开发语言·人工智能
Frank_refuel4 小时前
C++11之一场名为“搬家”的 C++ 之旅
开发语言·c++
Wang's Blog5 小时前
Java 项目实战: 外卖平台优化-Nginx配置文件结构与块层级
java·开发语言·nginx
2601_962071576 小时前
类变量和全局变量的查找路径有什么区别?
开发语言·python
\光辉岁月/7 小时前
5.java-数组
java·开发语言
Java后端的Ai之路7 小时前
Python进阶探索29_eval内置函数
开发语言·python·探索·eval·内置函数
谢亮_vipxieliang7 小时前
Spring 事务失效的常见场景
java·开发语言·数据库·spring boot
时间的拾荒人7 小时前
Qt 多线程详解:从 QThread 到实战
开发语言·qt·面试
郑州光合科技余经理7 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程