前言
前兩天在部落格園看到《如何防止程式多次運行》,文章寫的很好,最後還留下一個問題給我們思考。關於Winform的防止多次運行,曾經也想研究過,但是後來工作上沒有需要,於是就放棄了研究,這兩天找資料,將其封裝了一下,最後實現的效果為:Winform程式運行後,再次點擊exe,會將Winform顯示出去,若該表單被其他表單遮擋,則將其前置,若該表單被最小化至托盤,將其顯示並前置。
原理
使用命名事件,進程在此啟動時,前一個進程會收到通知,並做出回應。
using System;using System.Collections.Generic;using System.Runtime.InteropServices;using System.Text;using System.Threading;using System.Windows.Forms;namespace Ulitiy{ /// <summary> /// 工作列簡單封裝 /// </summary> /// <remarks> /// 檢查程式是否再次運行:在main方法裡調用:TaskBarUtil.CheckCreated(); /// 主表單在load事件或者構造方法初始化組件後調用:new TaskBarUtil(this, notifyIcon1); /// </remarks> public class TaskBarUtil { private Form mainForm; private NotifyIcon notifyIcon1; public static EventWaitHandle ProgramStarted; public TaskBarUtil(Form main, NotifyIcon notifyIcon1) { this.mainForm = main; this.notifyIcon1 = notifyIcon1; Load(); } [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd); #region 右下角表徵圖控制 private void Load() { //註冊進程OnProgramStarted ThreadPool.RegisterWaitForSingleObject(ProgramStarted, (obj, timeout) => { ShowForm(); }, null, -1, false); #region 表單事件 mainForm.SizeChanged += new EventHandler((sender, e) => { if (mainForm.WindowState == FormWindowState.Minimized) { HideForm(); } }); mainForm.FormClosing += new FormClosingEventHandler((sender, e) => { //注意判斷關閉事件Reason來源於表單按鈕,否則用菜單退出時無法退出! if (e.CloseReason == CloseReason.UserClosing) { mainForm.WindowState = FormWindowState.Minimized; //使關閉時視窗向右下角縮小的效果 notifyIcon1.Visible = true; e.Cancel = true; } }); #endregion #region 工作列表徵圖上下文事件 ContextMenuStrip contextMenuStrip1 = new ContextMenuStrip(); //設定工作列表徵圖上下文事件 var tsmShow = new ToolStripMenuItem(); tsmShow.Name = "tsmShow"; tsmShow.Text = "顯示"; tsmShow.Click += new System.EventHandler((sender, e) => { if (mainForm.Visible) return; ShowForm(); }); var tsmExit = new ToolStripMenuItem(); tsmExit.Text = "退出"; tsmExit.Name = "tsmShow"; tsmExit.Click += new System.EventHandler((sender, e) => { Application.Exit(); }); contextMenuStrip1.Items.Add(tsmShow); contextMenuStrip1.Items.Add(tsmExit); #endregion #region 工作列表徵圖事件 notifyIcon1.ContextMenuStrip = contextMenuStrip1; notifyIcon1.BalloonTipIcon = ToolTipIcon.Info; //notifyIcon1.Click += new EventHandler((sender, e) => //{ // //ShowForm(); //}); notifyIcon1.MouseClick += new MouseEventHandler((sender, e) => { if (e.Button != MouseButtons.Right) { ShowForm(); } }); #endregion } private void ShowForm() { mainForm.Visible = true; //顯示表單 if (mainForm.WindowState == FormWindowState.Minimized) mainForm.WindowState = FormWindowState.Normal; //恢複表單預設大小 //該屬性在設定後,再次雙擊exe,會導致表單在彈出時假死,使用form的Actived事件替代 //mainForm.ShowInTaskbar = true; mainForm.Show(); //前置該表單 SetForegroundWindow(mainForm.Handle); } private void HideForm() { mainForm.Visible = false; //隱藏表單 //notifyIcon1.ShowBalloonTip(3000, "提示", "雙擊恢複視窗", ToolTipIcon.Info); //出顯汽泡提示,可以不用 //mainForm.ShowInTaskbar = false; //從狀態列中隱藏 mainForm.Hide(); } #endregion #region 檢查是否啟動過,如果啟動則通知前一個進程,並退出當前進程 /// <summary> /// 檢查是否啟動過,如果啟動則通知前一個進程,並退出當前進程 /// </summary> public static void CheckCreated() { // 嘗試建立一個命名事件 bool createNew; //ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, "MyStartEvent", out createNew); ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, Application.ProductName, out createNew); // 如果該命名事件已經存在(存在有前一個運行執行個體),則發事件通知並退出 if (!createNew) { TaskBarUtil.ProgramStarted.Set(); Environment.Exit(1); } } #endregion }}
其中遇到的問題有在顯示和隱藏對表單的操作中,如果改變form的ShowInTaskbar會出問題。經過不嚴格的測試,這種發生在,在Winform運行後,多次點擊exe,在此過程中單機表單關閉,偶爾會出現無法找到控制代碼的錯誤。所以在顯示和隱藏表單的操作中,就沒有對該屬性進行操作。
封裝類包含了如下功能:
1、Winform 進程只能運行一個執行個體。
2、Winform 工作列表徵圖含操作功能表,顯示和退出,並包含相應的事件。
3. Winform 工作列表徵圖含滑鼠點擊事件,點擊即顯示表單。
使用過程中注意: 拖入notifyicon控制項,並指定表徵圖。
如果不需要這其中的功能,可以將類任意修改,滿足你的需要。
樣本下載
參考文章
Winform單一實例運行
[C# 開發技巧]如何防止程式多次運行