知识大全 .Net平台应用程序唯一运行实例

Posted

篇首语:丈夫欲遂平生志,一载寒窗一举汤。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 .Net平台应用程序唯一运行实例相关的知识,希望对你有一定的参考价值。

.Net平台应用程序唯一运行实例  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

  摘要

  本文阐述了在基于 NET平台的应用程序开发中如何实现唯一应用程序运行实例 对几种实现方式进行分析测试比较 从而寻找一种合适的处理方式 单击此处才查看本文的示例代码 概述   在开发一些应用系统的时候 由于程序内在的一些特征 系统的某些组成子程序只允许运行一个应用程序实例 以保证业务和数据处理安全 本文将从实际应用角度来分析其实现原理 对三种实现方式进行测试比较 从而确定一种合适的实现方法 文章的例子使用C#语言进行描述 进程匹配    对于每一个应用程序运行实例都会包含该实例的一个或多个进程 而且在程序运行过程中可能会动态的创建或销毁进程 或者访问其他现有进程进行通信 不难发现 在程序最先初始化的那一刻只有一个进程运行 而且应用程序进程生命周期最大进程名称集合是不变的 因此 在应用程序初始化的时候 可以根据进程关键信息检查系统进程列表是否存在同当前初始化进程匹配的进程来确定是否已经运行进程实例 逻辑处理步骤如下 .初始化应用程序 启动程序初始化进程 .访问系统进程列表 根据初始化进程关键信息进行匹配查找 .没有找到匹配进程(这一步是不会发生的 因为当前初始化进程也在列表中 不过还要看获取进程列表的实现代码怎么写) 继续初始化进程 程序初始化完成运行 .找到第一个匹配进程 判断找到的进程ID是否同初始化进程ID相同 .如果第一个匹配进程ID同初始化进程ID相同 则为当前初始化进程 继续查找 .没有找到第二个匹配进程 表明当前运行的是首个实例 继续初始化进程 程序初始化完成运行 .找到第二个 表明已有一个实例在运行 停止当前程序初始化 提示已有应用程序运行 .如果找到第一个匹配进程ID不同 表明已有一个实例在运行 停止当前程序初始化 提示已有应用程序运行      可见上面的逻辑实现中用于进程匹配的信息是关键 选择不当功能就无法实现 在这个实例中笔者使用了应用程序完全文件名称作为关键信息 在代码中首先需要引用下面命名空间 以调用WinAPI函数 using System Runtime InteropServices; 把实现唯一运行实例功能的类名取为SingleInstance 在类前面加static关键字为C# 新增的语言特征 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( User dll )] private static extern bool ShowWindowAsync(IntPtr hWnd int cmdShow); [DllImport( User dll )] private static extern bool SetForegroundWindow(IntPtr hWnd); 定义类成员辅助变量 private const int WS_SHOWNORMAL = ; 以上的方法声明为私有 对其进一步包装 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());    可见 在上面的实现过程中 由于关键信息采用应用程序的完整文件名 因此在文件名称或路径名称修改后 以上实现就会失效 进程互斥   在这个实现方式中需要定义一个进程同步基元 可以理解为临界资源 该资源只允许一个进程使用 根据这一点实现应用程序唯一运行实例就比较简单了 实现步骤如下 .应用程序初始化访问该同步基元 .可以访问 说明该同步基元未被使用 也就是说没有应用程序实例运行 使用同步基元 可以继续初始化成为第一个运行实例 .不可以访问 说明该同步基元已被使用 也就是说已有应用程序实例运行 停止当前程序初始化 提示已有应用程序运行 .应用程序实例退出释放同步基元占用    在代码中笔者使用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 那不同的应用程序实例也会出现互斥现象

运行标志    使用应用程序运行标志简单来讲就是在程序初始化的时候设置一个标志表示程序已运行 在程序运行结束的时候删除该标志 基本步骤如下 .应用程序初始化检查运行标志是否已经设置 .发现已经设置 说明已有应用程序实例运行 停止当前程序初始化 提示已有应用程序运行 .发现没有设置 说明没有应用程序实例运行 继续当前程序初始化 .退出应用程序时删除该运行标志    对于标志存储载体可以使用注册表 数据库或外部文件等 这里的代码使用外部文件实现 对存放标志的文件目录选择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管理器进程列表中结束或使其产生异常 那设置的运行标志就不会销毁 应用程序就没法再次运行 功能测试  这一节对上面的三个功能进行测试 以分析之间的区别 功能测试类别包括下面五类 .本地系统同一应用程序目录 .本地系统同一应用程序修改运行文件名称使两次运行名称不同 .本地系统两次运行程序目录不同 不修改文件名称 .本地系统不同会话用户登录启动应用程序 .远程计算机程序访问启动应用程序(一个程序在远程另一个在本地)    根据代码实现细节不同 对测试的结果可能会有所不同 这里的测试结果以笔者上面几节中实现的代码为准 为了测试简单化 通过给应用程序传入测试参数 决定使用哪种方式 入口函数调用代码为 [STAThread] static void Main(string[] args)          if (args Length == ) //没有传送参数                          Process p = SingleInstance GetRunningInstance();                 if (p != null) //已经有应用程序副本执行                         SingleInstance HandleRunningInstance(p);                 else //启动第一个应用程序                         Application Run(new MainForm());                  else //有多个参数                          switch (args[ ] ToLower())                                          case api :                                 if (SingleInstance HandleRunningInstance() == false)                                         Application Run(new MainForm());                                 break;                         case mutex :                                 if (args Length >= ) //参数中传入互斥体名称                                                                          if ( SingleInstance CreateMutex(args[ ]) )                                                                                          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 >= ) //参数中传入运行标志文件名称                                         SingleInstance RunFlag = args[ ];                                 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命令行 第一种调用为 WindowsApplication exe –api 或 WindowsApplication exe 第二种调用为 WindowsApplication exe –mutex 或WindowsApplication exe –mutex F AE C f BD E 第三种调用为 WindowsApplication exe –flag 或WindowsApplication exe –flag c:\\ zhzuo 测试结果

匹配/互斥/标志 同一目录 修改名称 不同目录 不同用户 远程访问 同一目录 O/O/O         修改名称   X/O/O       不同目录     X/O/O     不同用户       #/X/O   远程访问         X/O/O备注 O 表示成功 X – 表示失败 # 程序第二个运行没有反应    针对远程访问的测试 需要在系统管理工具的 NET Framework Configuration中进行设置授权该局域网路径允许访问 否则会抛出System Security SecurityException异常 根据测试结果可见三种实现方式适用范围不同 理想的实现是结合他们的优点进行多点判断 更多资源    关于 NET平台应用的开发 更多的技术文章可以访问 对于本文的建议或意见可在网站上留言 cha138/Article/program/net/201311/11652

相关参考

知识大全 vb.net中应用ArrayList 实例

vb.net中应用ArrayList实例  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  Arra

知识大全 ASP.NET怎么操作DataTable实例应用

ASP.NET怎么操作DataTable实例应用  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!有机

知识大全 把.Net整合进其他平台

把.Net整合进其他平台  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!整合分布式应用程序经常是一件

知识大全 .Net整合其他平台的一些探讨

.Net整合其他平台的一些探讨  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  整合分布式应用程序

知识大全 VB.net2008精彩实例,窗体应用技巧

VB.net2008精彩实例,窗体应用技巧  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  本篇文

知识大全 RAD Studio 2009通过Mono提供.NET应用跨平台开发

RADStudio2009通过Mono提供.NET应用跨平台开发  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起

知识大全 VB.net2008精彩实例,窗体应用技巧[1]

VB.net2008精彩实例,窗体应用技巧[1]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  

知识大全 VB.net2008精彩实例,窗体应用技巧[2]

VB.net2008精彩实例,窗体应用技巧[2]  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  

知识大全 .NET 平台下的OutLook 开发技术

.NET平台下的OutLook开发技术  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在应用程序

知识大全 .Net编写类库直接操作MySql数据库应用实例

.Net编写类库直接操作MySql数据库应用实例  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!