逻辑处理步骤如下,
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);
}