根據窗口句柄顯示窗體


1、服務類型的應用程序一般都是單例模式,常規是要持續運行的,平常最小化到托盤,需要查看時可以點擊托盤圖標最大化,但是往往操作人員會誤操作,程序已經打開又去重新運行程序,以前的做法是監測是否可以創建互斥進程,如果不能則說明程序已經運行,並提示客戶軟件已經運行,代碼如下:

          //單例模式

           bool bCreatedNew;

           System.Threading.Mutex mutex = new System.Threading.Mutex(false, Application.ProductName, out bCreatedNew);

            if (!bCreatedNew)

            {

                MessageBox.Show("打開失敗,已有消息中心服務正在運行!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);

                return;

            }

2、對這種方式進行改進,如果軟件已經打開但是最小化到了托盤,用戶再次運行軟件,將隱藏在托盤里面的軟件顯示出來,而不是提示軟件已經運行

方法一、使用FindWindow根據窗體類名或者標題獲取窗體句柄(因為類名不會變化,所以優先使用類名獲取),然后根據句柄顯示窗體ShowWindow,讓窗體獲取焦點SwitchToThisWindow,然后獲取窗體矩形框GetWindowRect,設置窗體顯示位置SetWindowPos。

 IntPtr hwnd = SingleProcess.FindWindow("WindowsForms10.Window.8.app.0.2bf8098_r11_ad1",null);                            

 SingleProcess.ShowWin(hwnd);

獲取窗體類名的方法:打開Spy++,找到“消息中心服務器”,右鍵屬性-類 可以看到類名

 

方法二、開始沒發現方法一,用的這種方法,獲取已經運行的軟件的進程,獲取進程的MainWindowHandle,如果MainWindowHandle不為0(說明沒有最小化)則直接根據窗口句柄顯示窗體,同方法一步驟2,如果MainWindowHandle為0(由於在窗體最小化時,獲取進程MainWindowHandle總是0),則枚舉窗體窗體,當窗體標題與進程號滿足設定值時,根據窗體句柄顯示窗體。

 SingleProcess.Singling("消息中心服務器");

3、完整代碼:

 public class SingleProcess
    {
        //根據主窗體句柄顯示窗體
        public static void ShowWin(IntPtr hwnd)
        {
            ShowWindow(hwnd, SW_RESTORE);
            SwitchToThisWindow(hwnd, true);

            Rect windowRec;
            GetWindowRect(hwnd, out windowRec);
            System.Drawing.Rectangle rect = System.Windows.Forms.SystemInformation.VirtualScreen;
            SetWindowPos(hwnd, HWND_TOP, (rect.Width - (windowRec.Right - windowRec.Left)) / 2,
                (rect.Height - (windowRec.Bottom - windowRec.Top)) / 2, 0, 0, SWP_NOSIZE);
        }

        private static string _formText;// = string.Empty;
        private static Process _process = null;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="str"></param>
        public static void Singling(string formtext)
        {
            _formText = formtext;
            Process instance = GetInstance();
            if (instance != null)   //首先確定有無進程
            {
                _process = instance;
                if (_process.MainWindowHandle.ToInt32() != 0) //是否托盤化
                {
                    //HandleRunningInstance(pro);
                    ShowWin(_process.MainWindowHandle);
                }
                else
                {
                    CallBack myCallBack = new CallBack(Report);
                    EnumWindows(myCallBack, 0);
                }
                //System.Environment.Exit(System.Environment.ExitCode);
            }

        }

        public static Process GetInstance()
        {
            Process current = Process.GetCurrentProcess();
            Process[] processes = Process.GetProcessesByName(Application.ProductName);//current.ProcessName);
            //遍歷正在有相同名字運行的例程    
            foreach (Process process in processes)
            {
                //忽略現有的例程      
                if (process.Id != current.Id)
                {
                    //if (process.MainModule.FileName == current.MainModule.FileName)
                    {
                        //返回另一個例程實例          
                        return process;
                    }
                }
            }
            //沒有其它的例程,返回Null    
            return null;
        }

        private static bool Report(IntPtr hwnd, int lParam)
        {
            //獲得窗體標題
            StringBuilder sb = new StringBuilder(100);
            GetWindowText(hwnd, sb, sb.Capacity);

            int calcID;
            //獲取進程ID   
            GetWindowThreadProcessId(hwnd, out calcID);
            if ((sb.ToString() == _formText) && (_process != null) && (calcID == _process.Id)) //標題欄、進程id符合
            //if (pro != null && calcID == pro.Id) //進程id符合
            {
                ShowWin(hwnd);
                return true;
            }
            return true;


        }


        #region  win32 API
        /// <summary>
        /// 獲取窗體句柄
        /// </summary>,兩個參數至少要知道一個
        /// <param name="lpClassName">窗體類名,可以通過Spy++獲取,為null表示忽略</param>
        /// <param name="lpWindowName">窗體標題,Text屬性,為null時表示忽略</param>
        /// <returns></returns>
        [DllImport("user32.dll", EntryPoint = "FindWindow")]
        public extern static IntPtr FindWindow(string lpClassName, string lpWindowName);

        /// <summary>
        /// 根據窗體句柄獲得窗體標題
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="lpText"></param>
        /// <param name="nCount"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpText, int nCount);

        /// <summary>
        /// 枚舉窗體
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        [DllImport("user32")]
        private static extern int EnumWindows(CallBack x, int y);
        private delegate bool CallBack(IntPtr hwnd, int lParam);

        /// <summary>
        /// 根據窗體句柄獲得其進程ID
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="ID"></param>
        /// <returns></returns>
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        private static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID);

        /// <summary>
        /// 修改位置、大小
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="hWndInsertAfter"></param>
        /// <param name="X"></param>
        /// <param name="Y"></param>
        /// <param name="cx"></param>
        /// <param name="cy"></param>
        /// <param name="uFlags"></param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        private static extern bool SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
        /// <summary>
        ///     Retains the current size (ignores the cx and cy parameters).
        /// </summary>
        static uint SWP_NOSIZE = 0x0001;
        static int HWND_TOP = 0;
        public struct Rect
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        [DllImport("user32.dll")]
        private static extern int GetWindowRect(IntPtr hwnd, out  Rect lpRect);

        /// <summary>
        /// 顯示窗體,同  ShowWindowAsync 差不多
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="nCmdShow"></param>
        /// <returns></returns>
        [DllImport("user32.dll", EntryPoint = "ShowWindow", CharSet = CharSet.Auto)]
        private static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
        private const int SW_RESTORE = 9;


        /// <summary> 
        /// 該函數設置由不同線程產生的窗口的顯示狀態。 (沒用)
        /// </summary> 
        /// <param name="hWnd">窗口句柄</param> 
        /// <param name="cmdShow">指定窗口如何顯示。查看允許值列表,請查閱ShowWlndow函數的說明部分。</param> 
        /// <returns>如果函數原來可見,返回值為非零;如果函數原來被隱藏,返回值為零。</returns> 
        [DllImport("User32.dll")]
        private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);

        /// <summary> 
        /// 該函數將創建指定窗口的線程設置到前台,並且激活該窗口。
        /// 鍵盤輸入轉向該窗口,並為用戶改各種可視的記號。系統給創建前台窗口的線程分配的權限稍高於其他線程。 
        /// (沒用)
        /// </summary> 
        /// <param name="hWnd">將被激活並被調入前台的窗口句柄。</param> 
        /// <returns>如果窗口設入了前台,返回值為非零;如果窗口未被設入前台,返回值為零。</returns> 
        [DllImport("User32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
        private const int WS_SHOWNORMAL = 1;

        /// <summary>
        /// 窗體焦點
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="fAltTab"></param>
        [DllImport("user32.dll ", SetLastError = true)]
        private static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);

        #endregion



    }
View Code

4、調用

 //單例模式
            bool bCreatedNew;
            System.Threading.Mutex mutex = new System.Threading.Mutex(false, Application.ProductName, out bCreatedNew);
            if (!bCreatedNew)
            {
                tempForm.Close();
                IntPtr hwnd = SingleProcess.FindWindow("WindowsForms10.Window.8.app.0.2bf8098_r11_ad1",null);                            
                SingleProcess.ShowWin(hwnd);
                //SingleProcess.Singling("消息中心服務器");
                //MessageBox.Show("打開失敗,已有消息中心服務正在運行!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }
View Code

 

.參考

http://www.cnblogs.com/qingse/archive/2013/02/16/2913430.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM