个人管理功能

在CSDN Blog撰写技术文章,即有机会入选CSDN技术中心,现在就去免费注册!已注册用户,点击登录

热门标签

专题历史

有人说SOA是一种IT策略,有人说SOA是一种架构理念,还有人说SOA是一种服务。SOA到底是什么?它将带来什么?软件产业的变革亦或是新的机遇下的挑战?业界权威专家带领我们一起去深究,去探索。BEA三位重量级专家与您共同探讨SOA

随着WPF/E更名为正式名称Silverlight,以及Silverlight 1.1 Alpha 版本的发布,答案变得清晰,而且令人兴奋! - 一个跨操作系统,跨浏览器的Web应用平台出现了。Silverlight 这样一个4.5MB的浏览器插件(1.1 Alpha文件)是如何做到的这些的?周岳: SilverLight-Web应用的一道强光

中国移动用户数量在大踏步地发展与增长,根据产业部的数据,仅三月就新增了670万户。预估计6月份之后,中国很快将迎来第五亿手机用户(平均不到3人拥有一台手机)而J2ME做为最重要的手机跨平台技术,凭借Java平台以其良好的开放性和支持能力,得到了众多手机厂商的支持。对众多开发者来说,J2ME程序易于移植,轻松实现“一次编写,到处运行”。J2ME系列开发专题,将带你从最基本的工具安装,环境配置开始,进入移动应用开发的世界。
 
CSDN移动开发系列之-“J2ME开发实训”

7月31日-8月1日,即将在上海召开甲骨文全球大会•亚太地区会议同期举行的甲骨文开发者大会,这是一项付费参加的面向开发人员的活动。在甲骨文开发者大会期间,您将听到世界一流的专家讲述如何使用Java、.NET、XML和PL/SQL以及Ajax、PHP、Spring、Groovy on Rails等流行技术来简化开发过程。在为期两天的甲骨文开发者大会中,您将能够提高自身的开发技能,扩充知识,参加几十场由专家主持的深入细致的技术讲座并在专家的辅导下进行上机操作、了解高级技能和获得详细指导。在甲骨文开发者大会期间,您有机会直接向业界一流的技术专家和开发人员请教。欢迎参加甲骨文全球大会·2007·亚太地区开发者大会

2007年6月29日,自由软件基金会宣布,其创始人Richard Stallman将在GNU的网站上,在本周太平洋时间星期五上午9点通过视频发布GPLv3。本来,GPL并不是所有开源组织所认可的协议。其从出现以来一直存在争议,GPL被认为是一种“病毒式”的协议,BSD的fans和老牌Unix黑客们认为,他们编写Unix的年头都比GPL声明要长得多,他们更愿意采用比GPL更加的自由的BSD协议。今天,开源社区中有70%左右的项目采用了GPL。很多在开源社区的老牌黑客们认为,Richard Stallman所鼓吹开源软件的言行与当年卡尔·马克思号召产业无产阶级反抗工作的努力如出一辙。在GPLv3的第三版修订案发布时,开源软件团体中的许多成员都反对这种协议。尤其是Linux的核心开发小组,其中29个高级架构师有28个反对这个协议。Linus Torvalds称这个协议有“宗教性质”,并公开反对。而整个软件行业特别是开源社区对GPLv3的争论也愈演愈烈。GPLv3:大教堂和集市的新一轮对抗

2007年7月14日由CSDN与ThoughtWorks联合主办的第二届“敏捷中国”技术大会在北京丽亭华苑酒店召开,多位开源社区和ThoughtWorks公司的技术领袖即将带来精彩的演讲。本次“敏捷中国”技术大会集中展现塑造敏捷企业所需的方方面面:业界领先的敏捷项目管理工具;极大提升软件开发效率的新语言和新框架;数据库领域的敏捷实践;全方位的敏捷项目管理指导;还有身临其境的亲身体验。来自开源社区和ThoughtWorks公司的技术领导者们将带领听众全面感受敏捷企业。“敏捷中国大会”现场直击

从2004年起,在每年的夏季,CSDN都会举办面向中国程序员的大型网上调查活动。这是中国样本最丰富的开发者社区调查,持续、全面和深入地反映了中国开发者社群自身状况、各项技术、工具、产品的使用状况和发展趋势,是完整、准确地了解中国开发者市场的重要参考资料。本次调查覆盖基础信息、.NET、Java、C/C++、Web开发、数据库应用开发、软件工程及项目管理、移动及嵌入式开发、开放源代码、企业信息化等10个领域。还有惊喜大奖等着你哦,赶快进入吧! 2007中国软件开发者大调查正式启动

推荐作者
  • 大宝大宝

    时间如流水,知惜方成功。

  • SkymanSkyman

    江苏人氏,梅兰芳之老乡。现游学渝州之最...

  • ralph623ralph623

新进作者
  • 冲 s冲 s

  • 小鱼小鱼

  • 棱角棱角

    多年J2EE构架设计与开发经验,专注于企业信息系统建设,精通Java设计模式,并能熟练的运用到企业开发中。 精通Struts与Spring框架。数据库方面精通Oracle数据库,从事过数据库方面的开发以及oracle优化方面的工作。

最新技术图书推荐
基于.Net平台应用程序唯一运行实例实现

发表日期:2006-7-31
更新日期:2006-7-31
作者文章阅读次数:3280

源自:秋枫 (个人网站) 标签:

您认为本文应该得        共有4人参与打分打印|收藏|讨论|投诉

本文阐述了在基于.NET平台的应用程序开发中如何实现唯一应用程序运行实例,对几种实现方式进行分析测试比较,从而寻找一种合适的处理方式。

 基于.Net平台应用程序唯一运行实例实现

发布日期: 2006-06-30 | 更新日期: 2006-07-04
作者:郑佐
适用于: Windows 操作系统
.NET Framework 1.x,2.0运行时环境
.NET Windows开发

摘要:本文阐述了在基于.NET平台的应用程序开发中如何实现唯一应用程序运行实例,对几种实现方式进行分析测试比较,从而寻找一种合适的处理方式。单击此处才查看本文的示例代码。

内容索引
概述
进程匹配
进程互斥
运行标志
功能测试
更多资源

概述
在开发一些应用系统的时候,由于程序内在的一些特征,系统的某些组成子程序只允许运行一个应用程序实例,以保证业务和数据处理安全。本文将从实际应用角度来分析其实现原理,对三种实现方式进行测试比较,从而确定一种合适的实现方法。文章的例子使用C#语言进行描述。

进程匹配
对于每一个应用程序运行实例都会包含该实例的一个或多个进程,而且在程序运行过程中可能会动态的创建或销毁进程,或者访问其他现有进程进行通信。不难发现,在程序最先初始化的那一刻只有一个进程运行,而且应用程序进程生命周期最大进程名称集合是不变的。因此,在应用程序初始化的时候,可以根据进程关键信息检查系统进程列表是否存在同当前初始化进程匹配的进程来确定是否已经运行进程实例。
逻辑处理步骤如下,
1.初始化应用程序,启动程序初始化进程;
2.访问系统进程列表,根据初始化进程关键信息进行匹配查找;
3.没有找到匹配进程(这一步是不会发生的,因为当前初始化进程也在列表中,不过还要看获取进程列表的实现代码怎么写),继续初始化进程,程序初始化完成运行。
4.找到第一个匹配进程,判断找到的进程ID是否同初始化进程ID相同;
5.如果第一个匹配进程ID同初始化进程ID相同,则为当前初始化进程,继续查找;
6.没有找到第二个匹配进程,表明当前运行的是首个实例,继续初始化进程,程序初始化完成运行。
7.找到第二个,表明已有一个实例在运行,停止当前程序初始化,提示已有应用程序运行。
8.如果找到第一个匹配进程ID不同,表明已有一个实例在运行,停止当前程序初始化,提示已有应用程序运行。

可见上面的逻辑实现中用于进程匹配的信息是关键,选择不当功能就无法实现。在这个实例中笔者使用了应用程序完全文件名称作为关键信息。
在代码中首先需要引用下面命名空间,以调用WinAPI函数。
using System.Runtime.InteropServices;
把实现唯一运行实例功能的类名取为SingleInstance,在类前面加static关键字为C# 2.0新增的语言特征。
public static class SingleInstance {}
使用GetRunningInstance静态方法获取应用程序进程实例,如果没有匹配进程,返回Null值,
public static Process GetRunningInstance()
{
        Process currentProcess = Process.GetCurrentProcess(); //获取当前进程
        //获取当前运行程序完全限定名 
        string currentFileName = currentProcess.MainModule.FileName; 
        //获取进程名为ProcessName的Process数组。 
        Process[] processes = Process.GetProcessesByName(currentProcess.ProcessName); 
        //遍历有相同进程名称正在运行的进程 
        foreach (Process process in processes) 
       
                if (process.MainModule.FileName == currentFileName) 
               
                        if (process.Id != currentProcess.Id) //根据进程ID排除当前进程 
                                return process;//返回已运行的进程实例 
               
       
        return null;
}

接下来调用两个WinAPI,其功能将在包装方法中描述,
[DllImport("User32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);

定义类成员辅助变量,
private const int WS_SHOWNORMAL = 1;

以上的方法声明为私有,对其进一步包装,HandleRunningInstance静态方法为获取应用程序句柄,设置应用程序为前台运行,并返回bool值。
public static bool HandleRunningInstance(Process instance)

        //确保窗口没有被最小化或最大化 
        ShowWindowAsync(instance.MainWindowHandle, WS_SHOWNORMAL); 
        //设置为foreground window 
        return SetForegroundWindow(instance.MainWindowHandle);
}

对上面的方法创建一个重载版本,使调用代码更加简洁,
public static bool HandleRunningInstance()

        Process p = GetRunningInstance(); 
        if (p != null
       
                HandleRunningInstance(p); 
                return true
       
        return false;
}

上面的方法实现获取已经运行的进程实例的句柄,并获取其焦点显示到前台,这个很有用,在其他实现方式中也可以用到。

在Main函数中调用下面代码实现单一应用程序实例,
Process p = SingleInstance.GetRunningInstance();
if (p != null) //已经有应用程序副本执行

        SingleInstance.HandleRunningInstance(p);
}
else //启动第一个应用程序

        Application.Run(new MainForm());
}

简洁的调用为,
if (SingleInstance.HandleRunningInstance()== false)

        Application.Run(new MainForm());
}

可见,在上面的实现过程中,由于关键信息采用应用程序的完整文件名,因此在文件名称或路径名称修改后,以上实现就会失效。

进程互斥
在这个实现方式中需要定义一个进程同步基元,可以理解为临界资源,该资源只允许一个进程使用。根据这一点实现应用程序唯一运行实例就比较简单了。
实现步骤如下,
1.应用程序初始化访问该同步基元;
2.可以访问,说明该同步基元未被使用,也就是说没有应用程序实例运行,使用同步基元,可以继续初始化成为第一个运行实例。
3.不可以访问,说明该同步基元已被使用,也就是说已有应用程序实例运行,停止当前程序初始化,提示已有应用程序运行。
4.应用程序实例退出释放同步基元占用。

在代码中笔者使用System.Threading.Mutex类实现同步基元,实现应用程序实例之间互斥功能。Mutex默认名字取Assembly.GetEntryAssembly().FullName。
在类成员中声明同步基元,
private static Mutex mutex = null;

CreateMutex静态方法创建应用程序进程Mutex,返回创建结果为true表示创建成功,false失败。
public static bool CreateMutex()

        return CreateMutex(Assembly.GetEntryAssembly().FullName);
}

实现其重载方法,让用户可以自定义Mutex名字,
public static bool CreateMutex(string name)

        bool result = false
        mutex = new Mutex(true, name, out result); 
        return result;
}

对应的释放Mutex资源方法为,
public static void ReleaseMutex()

        if (mutex != null
       
                mutex.Close(); 
        }
}

在Main函数中调用下面代码实现单一应用程序实例,
if (SingleInstance.CreateMutex())

        Application.Run(new MainForm()); 
        SingleInstance.ReleaseMutex();
}
else

        MessageBox.Show("程序已经运行!");
}

可见,在上面的实现过程中,Mutex名字是同步基元的唯一标识,如果刚好有不同的应用程序使用了相同名称的Mutex,那不同的应用程序实例也会出现互斥现象。

运行标志
使用应用程序运行标志简单来讲就是在程序初始化的时候设置一个标志表示程序已运行,在程序运行结束的时候删除该标志。
基本步骤如下,
1.应用程序初始化检查运行标志是否已经设置;
2.发现已经设置,说明已有应用程序实例运行,停止当前程序初始化,提示已有应用程序运行。 3.发现没有设置,说明没有应用程序实例运行,继续当前程序初始化。
4.退出应用程序时删除该运行标志。

对于标志存储载体可以使用注册表、数据库或外部文件等,这里的代码使用外部文件实现。对存放标志的文件目录选择C:\Documents and Settings\All Users\Application Data,也可以是C:\Program Files\Common Files。
声明类成员标志文件名称变量,
private static string runFlagFullname = null;

初始化程序运行标志,如果设置成功,返回true,已经设置返回false,设置失败将抛出异常,
public static bool InitRunFlag()

        if (File.Exists(RunFlag)) 
       
                return false
       
        using (FileStream fs = new FileStream(RunFlag, FileMode.Create)) 
       
        }
        return true;
}

释放初始化程序运行标志,如果释放失败将抛出异常,
public static void DisposeRunFlag()

        if (File.Exists(RunFlag)) 
       
                File.Delete(RunFlag); 
        }
}

获取或设置程序运行标志,必须符合Windows文件命名规范,
public static string RunFlag

        get 
       
                if(runFlagFullname == null
               
                        string assemblyFullName = Assembly.GetEntryAssembly().FullName; 
                        string path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); 
                        runFlagFullname = Path.Combine(path, assemblyFullName); 
               
                return runFlagFullname; 
       
        set 
       
                runFlagFullname = value
        }
}

在Main函数中调用下面代码实现单一应用程序实例,
if (SingleInstance.InitRunFlag())

        Application.Run(new MainForm()); 
        SingleInstance.DisposeRunFlag();
}
else

        MessageBox.Show("程序已经运行!");
}

可见,在上面的实现过程中,需要访问文件IO,因此有可能会出现异常,对异常需要进行具体处理。如果不同应用程序使用了相同的运行标志,也会出现进程互斥实现中存在的问题。由于运行标志存在外部载体中,如果笔者把启动的应用程序进程实例直接在Windows管理器进程列表中结束或使其产生异常,那设置的运行标志就不会销毁,应用程序就没法再次运行。

功能测试
这一节对上面的三个功能进行测试,以分析之间的区别。功能测试类别包括下面五类,
1.本地系统同一应用程序目录;
2.本地系统同一应用程序修改运行文件名称使两次运行名称不同;
3.本地系统两次运行程序目录不同,不修改文件名称;
4.本地系统不同会话用户登录启动应用程序;
5.远程计算机程序访问启动应用程序(一个程序在远程另一个在本地)。

根据代码实现细节不同,对测试的结果可能会有所不同,这里的测试结果以笔者上面几节中实现的代码为准。为了测试简单化,通过给应用程序传入测试参数,决定使用哪种方式,入口函数调用代码为,
[STAThread]
static void Main(string[] args)

        if (args.Length == 0) //没有传送参数 
       
                Process p = SingleInstance.GetRunningInstance(); 
                if (p != null) //已经有应用程序副本执行 
                        SingleInstance.HandleRunningInstance(p); 
                else //启动第一个应用程序 
                        Application.Run(new MainForm()); 
       
        else //有多个参数 
       
                switch (args[0].ToLower()) 
               
                        case "-api"
                                if (SingleInstance.HandleRunningInstance() == false
                                        Application.Run(new MainForm()); 
                                break
                        case "-mutex"
                                if (args.Length >= 2) //参数中传入互斥体名称 
                               
                                        if ( SingleInstance.CreateMutex(args[1]) ) 
                                       
                                                Application.Run(new MainForm()); 
                                                SingleInstance.ReleaseMutex(); 
                                       
                                        else 
                                                //调用SingleInstance.HandleRunningInstance()方法显示到前台。 
                                                MessageBox.Show("程序已经运行!"); 
                               
                                else 
                               
                                        if (SingleInstance.CreateMutex()) 
                                       
                                                Application.Run(new MainForm()); 
                                                SingleInstance.ReleaseMutex(); 
                                       
                                        else 
                                                //调用SingleInstance.HandleRunningInstance()方法显示到前台。 
                                                MessageBox.Show("程序已经运行!"); 
                               
                                break
                        case "-flag":
//使用该方式需要在程序退出时调用 
                                if (args.Length >= 2)
//参数中传入运行标志文件名称 
                                        SingleInstance.RunFlag = args[1]; 
                                try 
                                {
                                        if (SingleInstance.InitRunFlag()) 
                                       
                                                Application.Run(new MainForm()); 
                                                SingleInstance.DisposeRunFlag(); 
                                       
                                        else 
                                                //调用SingleInstance.HandleRunningInstance()方法显示到前台。 
                                                MessageBox.Show("程序已经运行!"); 
                               
                                catch (Exception ex) 
                               
                                        MessageBox.Show(ex.ToString()); 
                               
                                break
                        default
                                MessageBox.Show("应用程序参数设置失败。"); 
                                break
               
        }
}

运行CMD命令行,
第一种调用方式:
 WindowsApplication1.exe –api 或 WindowsApplication1.exe
第二种调用方式:
 WindowsApplication1.exe –mutex 或WindowsApplication1.exe –mutex {F140AE26-626C-42f8-BD49-45025742205E}
第三种调用方式:
 WindowsApplication1.exe –flag 或WindowsApplication1.exe –flag c:\blog.csdn.net.zhzuo

测试结果:

匹配/互斥/标志 1同一目录 2修改名称 3不同目录 4不同用户 5远程访问
1同一目录 O/O/O        
2修改名称   X/O/O      
3不同目录     X/O/O    
4不同用户       #/X/O  
5远程访问         X/O/O

备注:O - 表示成功,X – 表示失败,# - 程序第二个运行没有反应

针对远程访问的测试,需要在系统管理工具的.NET Framework 2.0 Configuration中进行设置授权该局域网路径允许访问,否则会抛出System.Security.SecurityException异常。根据测试结果可见三种实现方式适用范围不同,理想的实现是结合他们的优点进行多点判断。

更多资源
关于.NET平台应用的开发,更多的技术文章可以访问http://blog.csdn.net/zhzuo ,对于本文的建议或意见可在网站上留言。

您认为本文应该得        共有4人参与打分打印|收藏|讨论|投诉

暂无图片

秋枫思语(http://blog.csdn.net/zhzuo/)

评论

CSDN技术中心团队官方Blog:http://blog.csdn.net/techcenter/,反馈邮箱:techcenter at csdn.net (注意:请把 at 换成@)


网站简介广告服务网站地图帮助联系方式诚聘英才English问题报告

北京创新乐知广告有限公司 版权所有, 京 ICP 证 070598 号

世纪乐知(北京)网络技术有限公司 提供技术支持

Copyright © 2000-2008, CSDN.NET, All Rights Reserved