技术思绪摘录旅行笔记
很多时候我们需要我们的程序只运行一个,比如微信这种,再次双击图标,不会开新的实例,而是把已经运行的显示出来,这篇文章记录我们如何实现以及我们实现过程中遇到的问题。

1、封装一个句柄操作类IntPtrHelper.cs

    public static class IntPtrHelper
    {
        public const int WS_SHOWMAXIMIZE = 3;  //最大化显示窗口
        public const int WS_SHOW = 5;//以原来大小和位置显示

        /// <summary>
        /// 获取所有窗体
        /// </summary>
        /// <returns></returns>
        public static List<WindowInfo> GetAllDesktopWindows()
        {
            List<WindowInfo> wndList = new List<WindowInfo>();

            //enum all desktop windows
            EnumWindows(delegate (IntPtr hWnd, int lParam)
            {
                WindowInfo wnd = new WindowInfo();
                StringBuilder sb = new StringBuilder(256);
                //get hwnd
                wnd.HWnd = hWnd;
                //get window name
                GetWindowTextW(hWnd, sb, sb.Capacity);
                wnd.WindowName = sb.ToString();
                //get window class
                GetClassNameW(hWnd, sb, sb.Capacity);
                wnd.ClassName = sb.ToString();
                //add it into list
                wndList.Add(wnd);
                return true;
            }, 0);

            return wndList;
        }

        /// <summary>
        /// 定义委托
        /// </summary>
        /// <param name="hWnd">The h WND.</param>
        /// <param name="lParam">The l parameter.</param>
        /// <returns></returns>
        private delegate bool Wndenumproc(IntPtr hWnd, int lParam);

        /// <summary>
        ///枚举所有的窗体
        /// </summary>
        /// <param name="lpEnumFunc">The lp enum function.</param>
        /// <param name="lParam">The l parameter.</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        private static extern bool EnumWindows(Wndenumproc lpEnumFunc, int lParam);

        /// <summary>
        ///查找窗体tit
        /// </summary>
        /// <param name="hWnd">The h WND.</param>
        /// <param name="lpString">The lp string.</param>
        /// <param name="nMaxCount">The n maximum count.</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        private static extern int GetWindowTextW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpString, int nMaxCount);

        /// <summary>
        ///查找窗体类名
        /// </summary>
        /// <param name="hWnd">The h WND.</param>
        /// <param name="lpString">The lp string.</param>
        /// <param name="nMaxCount">The n maximum count.</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        private static extern int GetClassNameW(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpString, int nMaxCount);

        /// <summary>
        ///窗体置顶
        /// </summary>
        /// <param name="hWnd">The h WND.</param>
        /// <param name="hWndInsertAfter">The h WND insert after.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="Width">The width.</param>
        /// <param name="Height">The height.</param>
        /// <param name="flags">The flags.</param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int Width, int Height, int flags);

        /// <summary>
        /// 该函数设置由不同线程产生的窗口的显示状态。
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="cmdShow">指定窗口如何显示。查看允许值列表,请查阅ShowWlndow函数的说明部分。</param>
        /// <returns>如果函数原来可见,返回值为非零;如果函数原来被隐藏,返回值为零。</returns>
        [DllImport("User32.dll")]
        public static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
        
        /// <summary>
        /// 该函数将创建指定窗口的线程设置到前台,并且激活该窗口。键盘输入转向该窗口,并为用户改各种可视的记号。系统给创建前台窗口的线程分配的权限稍高于其他线程。
        /// </summary>
        /// <param name="hWnd">将被激活并被调入前台的窗口句柄。</param>
        /// <returns>如果窗口设入了前台,返回值为非零;如果窗口未被设入前台,返回值为零。</returns>
        [DllImport("User32.dll")]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        /// <summary>
        /// 根据窗口标题查找窗体
        /// </summary>
        /// <param name="lpClassName"></param>
        /// <param name="lpWindowName"></param>
        /// <returns></returns>
        [DllImport("user32.dll", EntryPoint = "FindWindow")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);


    }
    /// <summary>
    /// 窗体信息
    /// </summary>
    public struct WindowInfo
    {
        /// <summary>
        /// The h WND
        /// </summary>
        public IntPtr HWnd;

        /// <summary>
        /// The window name
        /// </summary>
        public string WindowName;

        /// <summary>
        /// The class name
        /// </summary>
        public string ClassName;
    }


1、改造Program.cs的Main方法

        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Process instance = RunningInstance();

            if (instance == null)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Frm_Main());
            }
            else
            {
                HandleRunningInstance(instance);
            }
        }

这里面涉及到两个方法,一个是RunningInstance(),获取正在运行的程序进程;一个是HandleRunningInstance(),激活正在运行的程序。

看看怎么实现的:

        /// <summary>
        /// 获取正在运行的实例,没有运行的实例返回null;
        /// </summary>
        public static Process RunningInstance()
        {
            try
            {
                Process current = Process.GetCurrentProcess();
                var processes = Process.GetProcessesByName(current.ProcessName).Where(x => x.Id != current.Id).ToList();
                foreach (Process process in processes)
                {
                    if (process?.MainModule?.FileName == current.MainModule.FileName)
                    {
                        return process;
                    }
                }

                return null;
            }
            catch
            {
                return null;
            }
        }
        /// <summary>
        /// 显示已运行的程序。
        /// </summary>
        public static void HandleRunningInstance(Process instance)
        {
            IntPtr hwnd = instance.MainWindowHandle;
            if (hwnd == IntPtr.Zero)
            {
                var wins = IntPtrHelper.GetAllDesktopWindows();
                string winname = "窗体名称";
                if (wins.Exists(x => x.WindowName.Contains(winname)))
                {
                    var cla = wins.Where(x => x.WindowName.Contains(winname)).ToList();
                    cla.ForEach(x =>
                    {
                        IntPtrHelper.ShowWindowAsync(x.HWnd, IntPtrHelper.WS_SHOWMAXIMIZE);
                        IntPtrHelper.SetForegroundWindow(x.HWnd);
                    });
                }
            }
            else
            {
                IntPtrHelper.ShowWindowAsync(hwnd, IntPtrHelper.WS_SHOWMAXIMIZE);
                IntPtrHelper.SetForegroundWindow(hwnd);
            }
        }

激活正在运行的程序这里,我们遇到点问题,当程序缩小到托盘的时候,获取不到句柄信息,导致激活不了窗体

所以我们当获取到的句柄为IntPtr.Zero的时候,通过窗体名称遍历一下所有的进程,找到指定的进程,再激活

CarsonIT 微信扫码关注公众号 策略、创意、技术

留下您的脚步

 

最近评论

查看更多>>

站点统计

总文章数:275 总分类数:18 总评论数:88 总浏览数:153.36万

精选推荐

阅读排行

友情打赏

请打开您的微信,扫一扫