【网通】点击此处下载源代码 【电信、网通】点击此处下载源代码
【电信】点击此处下载演示程序 【电信、网通】点击此处下载演示程序
【下载说明】
1、单击上面这个地址,打开下载页面。
2、点普通下载--等待30秒--点“下载”按钮--保存
介绍
一个单实例应用程序就是仅让用户打开一个实例的程序。严格的说,第二个实例也已经启动了,但是当它探测到已经有一个实例存在时,就自动关闭了。
在Visual Studio 2005中,做一个单实例的应用程序是很容易的。在VB中,仅需要选中工程设置对话框中的一个Check Box选项,然后在合适的事件中处理就可以了。这些代码会在Microsoft.VisualBasic命名空间中完成,但是也能在一个C#应用程序中这么做。这篇文章将会告诉你怎么去做,同时也会告诉你怎么讲命令行的参数从第二个实例中传递到第一个实例中。
背景
即使在.NET出现之前,单实例应用程序就已经出现了,而且我们有很多种方法去实现它。关键的问题就是多个实例之间的通信。在.NET中,通常是通过远程操作来完成的。在.net 2.0以前,关于单实例应用程序最好的一个文章就是由Detlef Grohs所写的。如果你还在使用.net 1.1,或者对详细的过程感兴趣的话,不妨看看他所写的文章。
代码
这篇文章中的源代码主要有两个文件组成:一个主窗口叫MainForm,aMain.cs里面包含Main函数和App类。
如果你从一个空的C#窗口工程中开始的话,那么第一步你需要做的就是添加一个名为Microsoft.VisualBasic的引用。在解决方案浏览器中,在引用上面点击右键,选择“添加引用”,之后选择.net页,然后选择"Microsoft.VisualBasic“就行了。
在Main函数中,创建了一个App类的实例,并且运行了它,并传递命令行参数,如下代码:
static void Main(string[] args)
{
App myApp = new App();
myApp.Run(args);
}
App类是从Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase类派生而来的,这个基类包含检测一个应用程序已经存在的实例的所有代码。这个仅需要在构造函数中开启即可:
public App()
{
// Make this a single-instance application
this.IsSingleInstance = true;
this.EnableVisualStyles = true;
// There are some other things available in
// the VB application model, for
// instance the shutdown style:
this.ShutdownStyle =
Microsoft.VisualBasic.ApplicationServices.ShutdownMode.AfterMainFormCloses;
// Add StartupNextInstance handler
this.StartupNextInstance +=
new StartupNextInstanceEventHandler(this.SIApp_StartupNextInstance);
}
在构造函数的最后一行中,我们为StartupNextInstance事件添加了一个处理函数。当已经检测到一个已存在的实例时,会激活这个处理函数。在这里使用它,是为了传递命令行参数,但是如果不需要传递命令行参数的话,那么这个事件是需要去处理的。
下一步要做的就是覆盖OnCreateMainForm方法。只有当第一个实例运行的时候,才会调用这个方法。需要创建一个MainForm类的实例,然后将它保存到WindowsFormsApplicationBase的属性中。
通过复制ReadonlyCollection到一个字符串数组,并保存到窗口的一个成员变量中,从而实现将命令行参数传递到窗口。
protected override void OnCreateMainForm()
{
// Create an instance of the main form
// and set it in the application;
// but don't try to run() it.
this.MainForm = new MainForm();
// We want to pass along the command-line arguments to
// this first instance
// Allocate room in our string array
((MainForm)this.MainForm).Args =
new string[this.CommandLineArgs.Count];
// And copy the arguments over to our form
this.CommandLineArgs.CopyTo(((MainForm)this.MainForm).Args, 0);
}
应用程序中的其他的实例可能也想向第一个实例传递命令参数;比如,当第二个文件打开的是偶,一个文件查看器可能需要打开一个新的文档窗口,而不是需要应用程序的一个新的实例。那么可以像下面这么做:
protected void SIApp_StartupNextInstance(object sender,
StartupNextInstanceEventArgs eventArgs)
{
// Copy the arguments to a string array
string[] args = new string[eventArgs.CommandLine.Count];
eventArgs.CommandLine.CopyTo(args, 0);
// Create an argument array for the Invoke method
object[] parameters = new object[2];
parameters[0] = this.MainForm;
parameters[1] = args;
// Need to use invoke to b/c this is being called
// from another thread.
this.MainForm.Invoke(new MainForm.ProcessParametersDelegate(
((MainForm)this.MainForm).ProcessParameters), parameters );
}
新的实例在另一个线程上,因此需要使用delegates和Invoke函数。MainForm.ProcessParameters方法仅仅是将参数添加到窗口的一个文本框上面。
感兴趣的地方
可能最需要单实例程序的地方是在一个资源浏览器的弹出菜单中打开一组文件。这样基本上会同时打开应用程序的多个实例。远程通信并加载其他实例需要很长的事件;在我的系统上,演示程序基本上可以每秒打开5个文件。
当打开50个文件时,发生了一个异常。原因是远程连接到第一个实例时超时了。如果你知道一个更好的方法来实现一个单实例应用程序,请告诉我们。
【更多阅读】