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

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

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

Winform 单实例运行的更多相关文章

  1. WinForm 登录窗体 + 单实例运行

    关于怎么在winform里增加登录窗体或者如何让winform程序单实例运行网上都很多例子. 然而两者结合起来呢? //Program.cs static class Program { public ...

  2. DevExpress Winform使用单例运行程序方法和非DevExpress使用Mutex实现程序单实例运行且运行则激活窗体的方法

    原文:DevExpress Winform使用单例运行程序方法和非DevExpress使用Mutex实现程序单实例运行且运行则激活窗体的方法 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA ...

  3. Qt实现应用程序单实例运行--LocalServer方式

    使Qt应用程序能够单实例运行的典型实现方法是使用共享内存实现.该方法实现简单,代码简洁. 但有一个致命缺陷:共享内存(QSharedMemory)实现的单程序运行,当运行环境是UNIX时,并且程序不幸 ...

  4. wpf单实例运行

    默认情况下我们可以打开一个应用程序多个实例,例如你双击一个exe多次.当然有些时候这么做会带来很多好处,但是有时我们又不希望这么做,要避免这个问题其实很简单,同WinForm中单实例运行一个应用是一样 ...

  5. C++实现程序单实例运行的两种方式

    简介 在我们编写程序的时候,经常会注意到的一个问题就是如何能够让程序只运行一个实例,确保不会让同一个程序多次运行,从而产生诸多相同进程,给我们的带来不便呢?那么常用的有以下四种方法,第一种方法是通过扫 ...

  6. Windows进程单实例运行

    场景         Windows进程单实例运行,如果有进程没有退出,继续等待,直到进程完全退出,才会进入下一个实例 HANDLE pHandle = NULL; do  {  pHandle = ...

  7. c#设计应用程序单实例运行

    利用WindowsFormsApplicationBase的IsSingleInstance来控制应用程序只能单实例运行. [DllImport("user32.dll", Ent ...

  8. c# 单实例运行

    /// <summary> /// 单实例运行程序 /// </summary> static void SingleInstanceRun() { bool isAppRun ...

  9. C#实现单实例运行

    C#实现单实例运行的方法,也有多种,比如利用 Process 查找进程的方式,利用 API findwindow 查找窗体的方式,还有就是 利用 Mutex 原子操作,上面几种方法中, 综合考虑利用 ...

随机推荐

  1. 使用Jenkins来构建Docker容器

    使用Jenkins来构建Docker容器(Ubuntu 14.04) 当开发更新了代码,提交到Gitlab上,然后由测试人员触发Jenkins,于是一个应用的新版本就被构建了.听起来貌似很简单,dua ...

  2. asp.net动态加载ascx用户控件

    原文:asp.net动态加载ascx用户控件 在主aspx/ascx文件中,将目标ascx1,ascx2控件拖拉到其页面中,然后删除,目的是要生成:Register 代码,然后在主文件中定义DIV或T ...

  3. BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 文件夹

    BEGINNING SHAREPOINT® 2013 DEVELOPMENT 文件夹 第一部分--開始使用SharePoint 2013 第1章节--SharePoint 2013 介绍 逐渐了解Sh ...

  4. Asp.Net MVC5入门学习系列⑦

    原文:Asp.Net MVC5入门学习系列⑦ 接着上篇结尾所说,如果开发中刚才遇到Model需要添加或者减少字段/属性的话,但是刚好你也利用EF的Code frist通过Model生存的数据库,这时改 ...

  5. PHP 以POST方式提交XML、获取XML,最后解析XML

    以POST方式提交XML // Do a POST $data="<?xml version='1.0' encoding='UTF-8'?> <TypeRsp> & ...

  6. gtest框架

    解析gtest框架运行机制   1.前言 Google test是一款开源的白盒单元测试框架,据说目前在Google内部已在几千个项目中应用了基于该框架的白盒测试. 最近的工作是在搞一个基于gtest ...

  7. 大约ActionContext.getContext()使用体验

    这是我在另一个人的博客看了,原来博客的时间长一点.我把它简化了一下,运营商,以方便它看起来. 为了避免与Servlet API耦合在一起,方便Action类做单元測试,Struts 2对HttpSer ...

  8. go orcale

    golang连接orcale   使用glang有一段时间了,最开始其实并不太喜欢他的语法,但是后来熟悉之后发现用起来还挺爽的.之前数据库一直使用mysql,连接起来没有什么问题,github上有很多 ...

  9. 如何使用MacVim

    如何使用MacVim 如何使用MacVim 如何使用MacVim呢? 命令模式 按下ESC 高效率移动 1.在插入模式之外 基本上来说,你应该尽可能少的呆在插入模式里面,因为在插入模式里面 VIM 就 ...

  10. Hive中抽取连续多天登录用户

    昨天群上有人发个阿里的面试题,题目描述大概如下: 数据源:用户登录表,只有俩个字段,uid和dt 试用HQL抽取出连续登录了K天的用户uid 第一个想法就是直接用一个UDF解决,按uid分组,把dt收 ...