Winform 單實例運行


前言

  前兩天在博客園看到《如何防止程序多次運行》,文章寫的很好,最后還留下一個問題給我們思考。關於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# 開發技巧]如何防止程序多次運行


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM