C#中进程单例的实现

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WinAppSingleton
{
    static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            Process instance = GetRunningInstance();
            if (instance == null)
            {
                Application.Run(new Form1());
            }
            else
            {
                try
                {
                    if (!instance.HasExited &&
                        instance.MainWindowHandle != IntPtr.Zero)

                    {
                        TODO:这里表示当前进程正在运行(HasExited为false)且有与之关联的主窗口(MainWindowHandle!=IntPtr.Zero)
                        这里不会再运行一个新的进程,而会返回之前已经开启的并且正在运行的进程实例
                        这里的分支条件,对有重启功能的程序尤为重要,否则重启不起来,特别是对使用了Application.Restart()来实现程序重启的程序

                        SendMessage(instance.MainWindowHandle, WM_SYSCOMMAND, new IntPtr(SC_MAXIMIZE), IntPtr.Zero);    // 最大化
                        SwitchToThisWindow(instance.MainWindowHandle, true);
                    }
                    else
                    {
                        TODO:这里表示要么当前进程已经退出,要么表示当前进程没有退出但是没有与之关联的主窗口
                        无论哪种状况,都需要new一个主窗体,以使得进程得以再次运行
                        Application.Run(new Form1());
                    }
                }
                catch (Exception allE)
                {
                    MessageBox.Show(allE.ToString());
                }
            }
        }

        /// <summary>
        /// 查找正在运行的实例
        /// </summary>
        /// <returns></returns>
        public static Process GetRunningInstance()
        {
            Process current = Process.GetCurrentProcess();
            Process[] processes = Process.GetProcesses();
            foreach (Process process in processes)
            {
                if (process.ProcessName == current.ProcessName &&
                    process.Id != current.Id)
                    return process;
            }
            return null;
        }

        private const uint WM_SYSCOMMAND = 0x0112;
        private const uint SC_MAXIMIZE = 0xF030;

        /// <summary>
        /// 向窗口发送消息(如:控制窗口大小等)
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="Msg"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        /// <summary>
        /// 激活
        /// </summary>
        /// <param name="hWnd">需要激活的句柄</param>
        /// <param name="fAltTab">用ALT+Tab键来实现此功能</param>
        [DllImport("user32.dll")]
        public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
    }

 

==========================================

相关知识:

Process.Id 属性

获取关联进程的唯一标识符。

类型:System.Int32
Process实例引用的、由系统生成的进程的唯一标识符。

 

异常

条件

InvalidOperationException

尚未设置进程的 Id 属性。

- 或 -

没有与此 Process  对象关联的进程。

PlatformNotSupportedException

该平台为 Windows 98 或 Windows Millennium Edition  (Windows Me);如果将 ProcessStartInfo.UseShellExecute  属性设置为 false,则可以在 Windows 98 和  Windows Me 上访问此属性。

 

 

如果关联的进程没有运行,则进程 Id 无效。因此,在尝试检索Id 属性之前,应确保该进程正在运行。在进程终止前,进程标识符在整个系统中唯一地标识进程。

可以通过向 GetProcessById方法传递进程标识符,将运行在本地或远程计算机上的进程与新的 Process实例相连。GetProcessById 是创建新组件并自动为新的Process实例设置 Id 属性的 static 方法。

系统可重用进程标识符。Id属性值仅当关联进程在运行中时才是唯一的。进程终止后,系统可对不相关的进程重用Id属性值。

由于标识符在系统中是唯一的,所以,作为传递 Process实例的替代方式,还可以将标识符传递给其他线程。此操作既可节省系统资源,又可保证正确标识进程。

平台说明:如果在启动进程时 ProcessStartInfo.UseShellExecute 设置为 true,则此属性在此平台上不可用。

Process.MainWindowHandle属性

属性值

类型:System.IntPtr
系统生成的、关联进程主窗口的窗口句柄。

 

 

异常

条件

InvalidOperationException

未定义 MainWindowHandle,因为进程已退出。

NotSupportedException

您尝试访问在远程计算机上运行的进程的  MainWindowHandle 属性。该属性仅对本地计算机上运行的进程可用。

PlatformNotSupportedException

该平台为 Windows 98 或 Windows Millennium Edition  (Windows Me);如果将 ProcessStartInfo.UseShellExecute  设置为 false,则可以在 Windows 98 和 Windows Me 上访问此属性。

主窗口是在启动进程时创建的窗口。初始化后,可能会打开其他窗口,包括ModalTopLevel窗口,但是将与进程关联的第一个窗口保持为主窗口。

只能获取本地计算机上运行的进程的 MainWindowHandle 属性。MainWindowHandle 属性是一个唯一标识与进程关联的窗口的值。

仅当进程有图形界面时,该进程才具有与其关联的主窗口。如果关联进程没有主窗口,则MainWindowHandle 值为零。对于已隐藏的进程,也就是任务栏里不可见的进程,该值也为零。这可以是任务栏最右端通知区域中显示为图标的进程的事例。(针对这里的描述,上面的代码其实还是有风险的,如果要完善,要将隐藏的判断逻辑加上)

如果刚启动了一个进程,并且想使用其主窗口句柄,则请考虑使用 WaitForInputIdle方法让该进程完成启动,从而确保创建了主窗口句柄。否则,将引发异常。

平台说明:如果在启动进程时 ProcessStartInfo.UseShellExecute 设置为 true,则此属性在此平台上不可用。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值