.Net结合PInvoke超简单实现程序单一实例运行

程序的实例(Application Instance)我们可以简单将其理解为进程。假设有个没有任何关于实例判断代码的可执行文件Test.exe,我们一旦运行了它,那么也意味着 Windows启动了一个新进程,这时Test.exe就有了一个实例,如果我们再次双击Test.exe,Windows就有了两个同名的进程,并且 Test.exe也有了两个实例,在Windows任务管理器中我们就会看到两个Test.exe进程。这足以证明Windows是支持多进程和多线程的,哈哈,这还用说!

多实例肯定要消耗不必要的内存,而且假设某些庞大的管理系统的一个实例也许会锁定某些资源,譬如数据库连接、文件读写等等,当启动另外一个实例时那么肯定会报错,我们不可能指望用户来理解这种情况,因为用户的水平参差不齐,我们必须加以应对。这就要求我们:

1、程序只运行一个实例;
2、当不慎启动第二个实例时,无论前一个实例是何种状态(最小化或者未前台显示),程序应有能力还原前一实例的主窗体(Restore)并将其前台显示,同时终止第二个实例的运行,以始终保持单一实例。

我们结合.Net框架和平台调用来实现上述需求。Process 类在我们的示例中很关键,我们将用该类通过检索进程名来获取进程信息并检测其是否已经运行,除了使用Process我们还可以使用互斥对象(Mutex)以及内存映射文件来达到同样的结果,但是后者同前者比起来要复杂很多,而且内存映射文件不被.Net原生支持,后者虽然复杂但进程检测绝对准确,而通过检索进程名来获取进程信息则有一定风险,因为可能存在两个相同进程名却完全不同的两个程序,虽然这种可能性极小。本文先以检索进程名为例,后续博文将会探索互斥对象和内存映射文件结合使用来绝对保证进程的唯一性。

若已存在同名实例,则利用PInvoke平台调用,通过调用Win32API还原前一实例的主窗体并前台显示。

我们创建一个默认的Windows Form. Application,双击Program.cs,更新代码如下:
    using System;
    using System.Collections.Generic;
    using System.Windows.Forms;
    using System.Diagnostics;
    using System.Runtime.InteropServices;

......

    static class Program
    {
        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
        [DllImport("user32.dll")]
        private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
        [DllImport("user32.dll")]
        private static extern bool IsIconic(IntPtr hWnd);

        private const int SW_RESTORE = 9;

        ///
        /// 应用程序的主入口点。
        ///
        [STAThread]
        static void Main()
        {
            string proc = Process.GetCurrentProcess().ProcessName;
            Process[] processes = Process.GetProcessesByName(proc);
            if (processes.Length > 1)
            {
                Process p = Process.GetCurrentProcess();
                int n = 0;
                if (processes[0].Id == p.Id)
                {
                    n = 1;
                }
                IntPtr hWnd = processes[n].MainWindowHandle;
                if (IsIconic(hWnd))
                {
                    ShowWindowAsync(hWnd, SW_RESTORE);
                }
                SetForegroundWindow(hWnd);
                return;
            }
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new frmMain());
        }
    }

只要在任何项目的Program.cs中按照上述代码更新,都能轻松实现程序的单一实例运行。也许有网友会疑问:我们怎么没看到终止进程的代码呢?请注意上述代码中的 return 语句!第二个实例的代码将会一直运行到在return语句之前,当我们获取了已经运行的实例的主窗体句柄并将其前台实现之后,实际上现在在Windows 上运行了两个同名实例!前一实例的主窗体显示后,main函数就返回了。c/c++程序员会清楚main函数是程序的入口点,一个程序有且仅有一个 main函数,除非程序退出,main函数是决不会返回的!所以,return也就等于终止了程序,第二个实例就此运行结束。

上述代码在Windows XP SP3 + Visual Studio 2008 SP1下编译调试通过。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14325734/viewspace-536377/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/14325734/viewspace-536377/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值