zoukankan      html  css  js  c++  java
  • Winform 单实例运行

    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# 开发技巧]如何防止程序多次运行

     



       如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的推荐】按钮。

       感谢阅读,希望这篇文章能给你带来帮助!

  • 相关阅读:
    【C语言】23typedef
    C#蓝牙开发之查找设备以及配对
    GridView获取隐藏列的值
    PDA(Windows Mobile)调用远程WebService
    VS2008使用宏记录来实现自动增加注释信息
    CS 系统框架二[部分内容更新]
    GridView里面嵌套RadioButton
    .Net 以报表的形式加载SAP里面的数据
    取GridView的PagerTemplate里面的控件ID
    《深入Ajax架构和最佳实践》读书笔记
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3170972.html
Copyright © 2011-2022 走看看