最近項目要求windows桌面客戶端最小化到托盤狀態下,再次重復啟動客戶端時,不啟動,將托盤程序再次顯示到桌面。我在網上找到的辦法,基本上都只能最小化時(任務欄存在),才能將程序顯示到桌面,最小化到托盤后不起作用。仔細研究發現原來最小化到后,也就是窗體的ShowInTaskbar = false或者this.Hide()后,就獲取不到窗口句柄了,MainWindowHandle為0后來我又搜了一下關於MainWindowHandle為0的解決辦法,再綜合一下,總算找到解決辦法了。
下面是放在Program.cs中的代碼
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows.Forms; namespace Cal_Earth { static class Program { //防止程序運行多個實例的方法有多種,如:通過使用互斥量和進程名等.而我想要實現的是:在程序運行多個實例時激活的是第一個實例,使其獲得焦點,並在前端顯示. //主要用到兩個API 函數: //ShowWindowAsync 該函數設置由不同線程產生的窗口的顯示狀態。 //SetForegroundWindow 該函數將創建指定窗口的線程設置到前台,並且激活該窗口。鍵盤輸入轉向該窗口,並為用戶改各種可視的記號。系統給創建前台窗口的線程分配的權限稍高於其他線程。 /// <summary> /// 該函數設置由不同線程產生的窗口的顯示狀態。 /// </summary> /// <param name="hWnd">窗口句柄</param> /// <param name="cmdShow">指定窗口如何顯示。查看允許值列表,請查閱ShowWlndow函數的說明部分。</param> /// <returns>如果函數原來可見,返回值為非零;如果函數原來被隱藏,返回值為零。</returns> [DllImport("user32.dll")] public static extern IntPtr FindWindow(string className, string frmText); [DllImport("User32.dll")] private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow); [DllImport("user32.dll", EntryPoint = "ShowWindow", CharSet = CharSet.Auto)] public static extern int ShowWindow(IntPtr hwnd, int showWay); [DllImport("user32.dll ", SetLastError = true)] static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab); /// <summary> /// 該函數將創建指定窗口的線程設置到前台,並且激活該窗口。鍵盤輸入轉向該窗口,並為用戶改各種可視的記號。系統給創建前台窗口的線程分配的權限稍高於其他線程。 /// </summary> /// <param name="hWnd">將被激活並被調入前台的窗口句柄。</param> /// <returns>如果窗口設入了前台,返回值為非零;如果窗口未被設入前台,返回值為零。</returns> [DllImport("User32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd); /// <summary> /// 根據句柄查找進程ID /// </summary> /// <param name="hwnd"></param> /// <param name="ID"></param> /// <returns></returns> [System.Runtime.InteropServices.DllImport("User32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int ID); private const int WS_SHOWNORMAL = 1; /// <summary> /// 應用程序的主入口點。 /// </summary> [STAThread] static void Main() { Process instance = RunningInstance(); if (RunningInstance() != null) { HandleRunningInstance(instance); return; } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); ToolFactory.GetMessage.SaveLog("用戶手動打開客戶端"); Application.Run(new Wallet()); } /// <summary> /// 返回相同運行路徑的 Process /// </summary> /// <returns></returns> private static Process RunningInstance() { Process current = Process.GetCurrentProcess(); Process[] processes = Process.GetProcessesByName(current.ProcessName); //遍歷與當前進程名稱相同的進程列表 foreach (Process process in processes) { //如果實例已經存在則忽略當前進程 if (process.Id != current.Id) { //保證要打開的進程同已經存在的進程來自同一文件路徑 if (Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == current.MainModule.FileName) { //返回已經存在的進程 return process; } } } return null; } /// <summary> /// 顯示已運行的程序。 /// </summary> public static void HandleRunningInstance(Process instance) { //MessageBox.Show("ID:"+instance.Id .ToString()+"--句柄"+instance.MainWindowHandle.ToString() + "--正常窗口" + WS_SHOWNORMAL + "--" + ShowWindowAsync(instance.MainWindowHandle, WS_SHOWNORMAL) + "--" + SetForegroundWindow(instance.MainWindowHandle)); //ShowWindowAsync(instance.MainWindowHandle, WS_SHOWNORMAL); //顯示,可以注釋掉 //放到前端 string s = instance.MainWindowHandle.ToString(); string ss = instance.Handle.ToString(); if (s == "0") { System.Diagnostics.Process proc = System.Diagnostics.Process.GetCurrentProcess(); Process[] Proes = System.Diagnostics.Process.GetProcessesByName(proc.ProcessName); foreach (System.Diagnostics.Process otherProc in Proes) { if (proc.Id != otherProc.Id) { IntPtr hWnd = otherProc.MainWindowHandle;//注意這里hWnd代表的是窗口句柄,不是進程句柄 if (hWnd.ToInt32() == 0) { hWnd = FindWindow(null, "主窗體名稱");//我理解為主窗體的Text(不知道對不對哈),這里注意一定不是進程名哦 int id = -1; GetWindowThreadProcessId(hWnd, out id); if (id == otherProc.Id) { ShowWindow(hWnd, 5);//5代表窗體執行show(); ShowWindow(hWnd, 1);//1代表窗體執行this.WindowState = FormWindowState.Normal; break; } } //此處獲取的hWnd即為之前運行程序的主窗口句柄,再使用其他函數打開窗體 break; } } } else { ShowWindow(instance.MainWindowHandle, 1);//1代表窗體執行this.WindowState = FormWindowState.Normal;也可以用WS_SHOWNORMAL,這個是自己定義的常量,看上面定義,照顧小白我才沒有用哦 } } } }
不僅僅要注意用到上面的代碼,當你最小化窗體的時候,你用
WindowState = FormWindowState.Minimized;
當你最小化到托盤時代碼為:
private void Wallet_FormClosing(object sender, FormClosingEventArgs e) { // 取消關閉窗體 e.Cancel = true; // 將窗體變為最小化 this.WindowState = FormWindowState.Minimized; this.Hide();//這樣不需要設置ShowInTaskbar = false;了 }
點擊托盤圖標時執行的代碼
private void notifyIcon1_MouseClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { if (this.WindowState == FormWindowState.Minimized) { this.Show(); this.WindowState = FormWindowState.Normal;//先將窗體show出來再設置大小,可以很好的防止窗體閃爍哦 } } }
我貼出程序中的窗體顯隱執行順序就是想讓你們更好的理解為什么要這樣執行。最小化到托盤時,先最小化,再Hide();從托盤顯示時先Show(),再Normal,外部調用的時候
順序和這個時一樣的,先Show(),再Normal;不懂的小伙伴仔細看下下面這兩塊的對比:
從外部調用時:
ShowWindow(hWnd, 5);//5代表窗體執行show(); ShowWindow(hWnd, 1);//1代表窗體執行this.WindowState = FormWindowState.Normal;
程序里面:
if (this.WindowState == FormWindowState.Minimized)
{
this.Show();
this.WindowState = FormWindowState.Normal;//先將窗體show出來再設置大小,可以很好的防止窗體閃爍哦
}
希望看我博客的麻油能明白吧,不明白可以留言哈