主頁 > .NET開發 > C#如何防止程式多次運行的技巧(精典)

C#如何防止程式多次運行的技巧(精典)

2021-02-23 06:08:48 .NET開發

一、引言
最近發現很多人在論壇中問到如何防止程式被多次運行的問題的,所以這里就記錄下來,希望給遇到同樣問題的朋友有所參考的,同時也是對自己的一個積累,在介紹具體實作代碼之前,我們必須明確解決這個問題的思路是什么的?下面只要分享我的一個思考的這個問題的方式:

1、當我們點擊一個exe檔案時,此時該exe程式將會運行,我們可以看到該程式的界面,對于計算機而言,就是會在系統上開啟一個該程式的進行,這個我們可以通過任務管理器來查看的(當我們點擊exe之后,程式運行,系統會創建一個與與程式同名的行程)

2、既然我們要防止程式運行多次,也就是說程式只能運行一次,從作業系統的角度來講就是該程式的行程只能是唯一的,分析到這里我們自然就想到了,要保證該程式行程只有一個,我們就要判斷下該程式行程是否在自己的作業系統上運行了,如果已經運行了一個行程,當我們下次運行exe的時候,此時不是再開啟該程式行程,而是退出,彈出一個提示框告訴用戶該程式已經運行,如果作業系統沒有運行該程式行程,則運行這個程式,

3、從而這個問題就轉換為判斷該程式行程的數量問題了,此時我們就想.NET 有沒有提供一個類可以獲得該行程名的數量,如果數量大于1則說明該程式已經運行了,小于就表明程式沒有運行,如果熟悉.NET類別庫的人肯定知道.NET類別庫中有一個Process類,該類的意思就是一個行程的抽象,(有些人就會說,我一開始不知道有這個類那怎么辦呢?那就是考驗你英文了,因為行程的英文就是Process,然而所有編程語言的命名都很通俗易懂,此時就可以用Process在MSDN上搜索,這樣你也就發現這個類了)

4、除了第三點中提出找行程數量的思路外,還有另外一種實作思路就是——我們能不能讓運行一個行程的時候,讓該行程具有一個變數,該變數是唯一標識該行程,當點擊exe檔案預創建一個改程式行程時,我們去判斷這個變數是否存在,如果存在就說明這個行程已經運行,從而退出本次的程式,并且提示給用戶說該程式已經運行,
從上面的分析程序中可以看出,我們解決這個問題的思路就是從行程入手,第三點的思路就是直接從行程數量入手,而第四點思路也是從行程入手,只是做了一個變換罷了,讓一個變數來唯一標識一個行程,當變數存在時說明該程式行程也運行了,

二、使用互斥量Mutex
弄懂了主要的實作思路之后,下面看代碼實作就完全不是問題了,使用互斥量的實作就是第四點的思路的體現,我們用為該程式行程創建一個互斥量Mutex物件變數,當運行該程式時,該程式行程就具有了這個互斥的Mutex變數,如果再次運行該程式時,通過檢查該互斥變數是否存在(來替換檢測這個行程是否存在),如果存在則說明程式已運行,否則就沒運行,這里需要注意的是:從我的多執行緒同步的文章大家可以知道,Mutex類也可以對執行緒進行同步,那是不是其他對執行緒同步的類也可以解決本專題中的問題呢?答案是否定,之所以Mutex類可以解決這個問題,是因為Mutex類除了可以對執行緒同步,也可以對行程同步,下面就具體看看實作代碼吧:

using System;
using System.Threading;
using System.Windows.Forms;

namespace OnlyInstanceRunning
{
  static class Program
  {
    /// <summary>
    /// 應用程式的主入口點,
    /// </summary>
    [STAThread]
    static void Main()
    {
      #region 方法一:使用互斥量
      bool createNew;

      // createdNew:
      // 在此方法回傳時,如果創建了區域互斥體(即,如果 name 為 null 或空字串)或指定的命名系統互斥體,則包含布林值 true;
      // 如果指定的命名系統互斥體已存在,則為false
      using (Mutex mutex = new Mutex(true, Application.ProductName, out createNew))
      {
        if (createNew)
        {
          Application.EnableVisualStyles();
          Application.SetCompatibleTextRenderingDefault(false);
          Application.Run(new Form1());
        }
        // 程式已經運行的情況,則彈出訊息提示并終止此次運行
        else
        {
          MessageBox.Show("應用程式已經在運行中...");
          System.Threading.Thread.Sleep(1000);

          // 終止此行程并為基礎作業系統提供指定的退出代碼,
          System.Environment.Exit(1);
        }
      }

      #endregion
    }
  }
}

  三、直接判斷行程是否存在的方式來解決這個問題
3.1 判斷該程式行程數量的方式
有了上面的思路分析之后,相信大家看下面代碼會覺得一目了然,這里就不多解釋了,直接看代碼:

#region 方法二:使用行程名
      Process[] processcollection = Process.GetProcessesByName(Application.CompanyName);
      // 如果該程式行程數量大于,則說明該程式已經運行,則彈出提示資訊并提出本次操作,否則就創建該程式
      if (processcollection.Length >= 1)
      {
        MessageBox.Show("應用程式已經在運行中,,");
        Thread.Sleep(1000);
        System.Environment.Exit(1);
      }
      else
      {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        // 運行該應用程式
        Application.Run(new Form1());
      }
      #endregion 

  3.2 直接判斷程式行程是否存在的方式

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Way3
{
  static class Program
  {
    #region 方法三:使用的Win32函式的宣告
    /// <summary>
    /// 設定視窗的顯示狀態
    /// Win32 函式定義為:http://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
    /// </summary>
    /// <param name="hWnd">視窗句柄</param>
    /// <param name="cmdShow">指示視窗如何被顯示</param>
    /// <returns>如果表單之前是可見,回傳值為非零;如果表單之前被隱藏,回傳值為零</returns>
    [DllImport("User32.dll")]
    private static extern bool ShowWindow(IntPtr hWnd, int cmdShow);

    /// <summary>
    /// 創建指定視窗的執行緒設定到前臺,并且激活該視窗,鍵盤輸入轉向該視窗,并為用戶改變各種可視的記號,
    /// 系統給創建前臺視窗的執行緒分配的權限稍高于其他執行緒,
    /// </summary>
    /// <param name="hWnd">將被激活并被調入前臺的視窗句柄</param>
    /// <returns>如果視窗設入了前臺,回傳值為非零;如果視窗未被設入前臺,回傳值為零</returns>
    [DllImport("User32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);

    // 指示視窗為普通顯示
    private const int WS_SHOWNORMAL = 1;
    #endregion

    /// <summary>
    /// 應用程式的主入口點,
    /// </summary>
    [STAThread]
    static void Main()
    {
      #region 方法三:呼叫Win32 API,并激活運行程式的視窗顯示在最前端
      // 這種方式在VS呼叫的情況不成立的,因為在VS中按F5運行的行程為OnlyInstanceRunning.vshost,從這個行程的命名就可以看出,該行程為OnlyInstanceRunning行程的宿主行程
      // 關于這個行程的更多內容可以查看:http://msdn.microsoft.com/zh-cn/library/ms185331(v=vs.100).aspx
      // 而直接點OnlyInstanceRunning.exe運行的程式行程為OnlyInstanceRunning,
      // 但是我們可以一些小的修改,即currentProcess.ProcessName.Replace(".vshose","")此時無論如何都為 OnlyInstanceRunning

      // 獲得正在運行的程式,如果沒有相同的程式,則運行該程式
      Process process = RunningInstance();
      if (process == null)
      {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
      }
      else
      {
        // 已經運行該程式,顯示資訊并使程式顯示在前端
        MessageBox.Show("應用程式已經在運行中......");
        HandleRunningInstance(process);
      }
      #endregion 
    }

    #region 方法三定義的方法
    /// <summary>
    /// 獲取正在運行的程式,沒有運行的程式則回傳null
    /// </summary>
    /// <returns></returns>
    private static Process RunningInstance()
    {
      // 獲取當前活動的行程
      Process currentProcess = Process.GetCurrentProcess();

      // 根據當前行程的行程名獲得行程集合
      // 如果該程式運行,行程的數量大于1
      Process[] processcollection = Process.GetProcessesByName(currentProcess.ProcessName.Replace(".vshost", ""));
      foreach (Process process in processcollection)
      {
        // 如果行程ID不等于當前運行行程的ID以及運行行程的檔案路徑等于當前行程的檔案路徑
        // 則說明同一個該程式已經運行了,此時將回傳已經運行的行程
        if (process.Id != currentProcess.Id)
        {
          if (Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == process.MainModule.FileName)
          {
            return process;
          }
        }
      }

      return null;
    }

    /// <summary>
    /// 顯示已運行的程式
    /// </summary>
    /// <param name="instance"></param>
    private static void HandleRunningInstance(Process instance)
    {
      // 顯示視窗
      ShowWindow(instance.MainWindowHandle, WS_SHOWNORMAL);

      // 把表單放在前端
      SetForegroundWindow(instance.MainWindowHandle);
    }

    #endregion
  }
}

  3.3 解決3.2實作方式中存在的問題——只能是最小化的表單顯示出來,如果隱藏到托盤中則不能把運行的程式顯示出來

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Way4
{
  static class Program
  {

    #region 方法四:使用的Win32函式的宣告

    /// <summary>
    /// 找到某個視窗與給出的類別名和視窗名相同視窗
    /// 非托管定義為:http://msdn.microsoft.com/en-us/library/windows/desktop/ms633499(v=vs.85).aspx
    /// </summary>
    /// <param name="lpClassName">類別名</param>
    /// <param name="lpWindowName">視窗名</param>
    /// <returns>成功找到回傳視窗句柄,否則回傳null</returns>
    [DllImport("user32.dll")]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    /// <summary>
    /// 切換到視窗并把視窗設入前臺,類似 SetForegroundWindow方法的功能
    /// </summary>
    /// <param name="hWnd">視窗句柄</param>
    /// <param name="fAltTab">True代表視窗正在通過Alt/Ctrl +Tab被切換</param>
    [DllImport("user32.dll ", SetLastError = true)]
    static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);

    ///// <summary>
    ///// 設定視窗的顯示狀態
    ///// Win32 函式定義為:http://msdn.microsoft.com/en-us/library/windows/desktop/ms633548(v=vs.85).aspx
    ///// </summary>
    ///// <param name="hWnd">視窗句柄</param>
    ///// <param name="cmdShow">指示視窗如何被顯示</param>
    ///// <returns>如果表單之前是可見,回傳值為非零;如果表單之前被隱藏,回傳值為零</returns>
    [DllImport("user32.dll", EntryPoint = "ShowWindow", CharSet = CharSet.Auto)]
    public static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
    public const int SW_RESTORE = 9;
    public static IntPtr formhwnd;
    #endregion

    /// <summary>
    /// 應用程式的主入口點,
    /// </summary>
    [STAThread]
    static void Main()
    {
      #region 方法四: 可以是托盤中的隱藏程式顯示出來
      // 方法四相對于方法三而言應該可以說是一個改進,
      // 因為方法三只能是最小化的表單顯示出來,如果隱藏到托盤中則不能把運行的程式顯示出來
      // 具體問題可以看這個帖子:http://social.msdn.microsoft.com/Forums/zh-CN/6398fb10-ecc2-4c03-ab25-d03544f5fcc9
      Process currentproc = Process.GetCurrentProcess();
      Process[] processcollection = Process.GetProcessesByName(currentproc.ProcessName.Replace(".vshost", string.Empty));
      // 該程式已經運行,
      if (processcollection.Length >= 1)
      {
        foreach (Process process in processcollection)
        {
          if (process.Id != currentproc.Id)
          {
            // 如果行程的句柄為0,即代表沒有找到該表單,即該表單隱藏的情況時
            if (process.MainWindowHandle.ToInt32() == 0)
            {
              // 獲得表單句柄
              formhwnd = FindWindow(null, "Form1");
              // 重新顯示該表單并切換到帶入到前臺
              ShowWindow(formhwnd, SW_RESTORE);
              SwitchToThisWindow(formhwnd, true);
            }
            else
            {
              // 如果表單沒有隱藏,就直接切換到該表單并帶入到前臺
              // 因為表單除了隱藏到托盤,還可以最小化
              SwitchToThisWindow(process.MainWindowHandle, true);
            }
          }
        }
      }
      else
      {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
      }
      #endregion
    }
  }
}

  四、程式實作效果
四種實作方式的運行效果都是差不多的,這里就以實作方式一作為演示的,具體實作效果如下圖:

五、總結
寫這個專題主要是看到原因是看到有些朋友問了這樣的問題,所以就總結下具體的實作代碼來幫助遇到同樣問題的朋友做一個參考,同時也是對自己一個學習的積累和復習,



轉載請註明出處,本文鏈接:https://www.uj5u.com/net/262334.html

標籤:WinForm

上一篇:dotnet core TargetFramework 決議順序探索

下一篇:WPF之屬性

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more