开启左侧

让C#程序只运行一个实例,显示已经运行的界面

[复制链接]
Princess 发表于 2020-11-3 15:47:06 | 显示全部楼层 |阅读模式
本帖最后由 Princess 于 2020-11-3 15:49 编辑

前提:一般操作硬件类型的软件,都需要独占硬件,所以这个时候对软件有一个要求就是只能运行一个实例,那么用户如果没有注意到程序已经运行再次去点击呢?如果没有做过防护那么很容易出现各种初始化异常问题,所以这个时候我们会考虑通过加入互斥体来解决该问题:
  1. bool bFlag;
  2. Mutex mutex = new Mutex(true, "_Demo1_", out bFlag);
  3. if (!bFlag)
  4. {
  5.       //检测到程序已经运行,一般情况下是弹窗提示不能重复运行
  6.       return;
  7. }
  8. mutex.ReleaseMutex();;
复制代码


这个是最早的雏形,当然这个问题也解决了,但是却不是很友好!
那么有没有更好的一种方式呢?有的,我们可以迭代进程看看能否找到对应名称的进程,如果找到再将它打开并且显示,是不是就友好多了。

那么我们需要在Progress.cs添加以下内容:
命名空间引用:
  1. using System.Diagnostics;
  2. using System.Reflection;
  3. using System.Runtime.InteropServices;
复制代码

Main函数内部改造:
  1. Process instance = RunningInstance();
  2. if (null != instance)                  
  3. {
  4.         HandleRunningInstance(instance);
  5. }
  6. else
  7. {
  8.         Application.EnableVisualStyles();
  9.         Application.SetCompatibleTextRenderingDefault(false);
  10.         Application.Run(new Form1());
  11. }
复制代码

这句代码的意思就是如果找到了进程就直接打开运行中的实例,否则正常开启。

下面是上面实现的核心函数,添加在Main函数下面即可。
  1. static Process RunningInstance()
  2.         {
  3.             Process current = Process.GetCurrentProcess();
  4.             Process[] processes = Process.GetProcessesByName(current.ProcessName);
  5.             foreach (Process process in processes)
  6.             {
  7.                 if (process.Id != current.Id)
  8.                 {
  9.                     if (Assembly.GetExecutingAssembly().Location.Replace("/ ", "\\ ") == current.MainModule.FileName)
  10.                     {
  11.                         return process;
  12.                     }
  13.                 }
  14.             }
  15.             return null;//第一次运行,返回null
  16.         }

  17.         public static void HandleRunningInstance(Process instance)
  18.         {
  19.             ShowWindowAsync(instance.MainWindowHandle, WS_SHOWNORMAL); //置窗口为正常状态
  20.             SetForegroundWindow(instance.MainWindowHandle);
  21.         }

  22.         #region 调用系统api

  23.         [DllImport("User32.dll ")]

  24.         private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);

  25.         [DllImport("User32.dll ")]

  26.         private static extern bool SetForegroundWindow(IntPtr hWnd);

  27.         private const int WS_SHOWNORMAL = 1;

  28.         #endregion
复制代码


这里要注意,不要调试测试,因为current.ProcessName在调试下的进程是带了虚拟机标识的,比如Demo1.vshost这样的名称,实际运行是没有的。
所以直接编译生成文件,到生成的目录下运行exe两次就可以看到效果。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表