如何實現僅啟動一個 WPF 進程實例,並在打開第二個時,自動喚起之前打開的進程。
1 代碼入口
在 App.xaml.cs
文件中,重寫 OnStartup
方法,並添加 Mutex 進程鎖。
/// <summary>
/// 只打開一個進程
/// </summary>
/// <param name="e"></param>
protected override void OnStartup(StartupEventArgs e)
{
string mutexName = "32283F61-EC4D-43B1-9C44-40280D5854DD";
ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, mutexName, out var createNew);
if (!createNew)
{
// 喚起已經啟動的進程
App.Current.Shutdown();
Environment.Exit(-1);
}
else
{
// 正常啟動
base.OnStartup(e);
}
}
2 喚起已經啟動的進程
try
{
var processes = Process.GetProcessesByName("進程名");
if (!processes.Any())
{
// 沒有找到進程名,可能是啟動文件被改名了,但還是啟動了的。
MessageBox.Show("已經啟動了XXX", "錯誤提示", MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
// 其實這里應該只有一個
foreach (Process process in processes)
{
ShowWindowAsync(process.MainWindowHandle, SW_SHOWNOMAL);
SetForegroundWindow(process.MainWindowHandle);
SwitchToThisWindow(process.MainWindowHandle, true);
}
}
}
catch (Exception exception)
{
// Logger.Error(exception, "喚起已啟動進程時出錯");
}
Win32 函數引入:
private const int SW_SHOWNOMAL = 1;
///<summary>
/// 該函數設置由不同線程產生的窗口的顯示狀態
/// </summary>
/// <param name="hWnd">窗口句柄</param>
/// <param name="cmdShow">指定窗口如何顯示。查看允許值列表,請查閱ShowWindow函數的說明部分</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", EntryPoint = "SetForegroundWindow")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
3 完整代碼
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace Test
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public EventWaitHandle ProgramStarted { get; set; }
private const int SW_SHOWNOMAL = 1;
///<summary>
/// 該函數設置由不同線程產生的窗口的顯示狀態
/// </summary>
/// <param name="hWnd">窗口句柄</param>
/// <param name="cmdShow">指定窗口如何顯示。查看允許值列表,請查閱ShowWindow函數的說明部分</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", EntryPoint = "SetForegroundWindow")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
/// <summary>
/// 只打開一個進程
/// </summary>
/// <param name="e"></param>
protected override void OnStartup(StartupEventArgs e)
{
string mutexName = "32283F61-EC4D-43B1-9C44-40280D5854DD";
ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, mutexName, out var createNew);
if (!createNew)
{
try
{
var processes = Process.GetProcessesByName("進程名");
if (!processes.Any())
{
MessageBox.Show("已經啟動了XXX", "錯誤提示", MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
foreach (Process process in processes)
{
ShowWindowAsync(process.MainWindowHandle, SW_SHOWNOMAL);
SetForegroundWindow(process.MainWindowHandle);
SwitchToThisWindow(process.MainWindowHandle, true);
}
}
}
catch (Exception exception)
{
// Logger.Error(exception, "喚起已啟動進程時出錯");
}
App.Current.Shutdown();
Environment.Exit(-1);
}
else
{
base.OnStartup(e);
}
}
}
}