记录一下工作中遇到的场景。单例运行程序同时还要将程序隐藏到系统托盘,再次启动程序时如果已存在需要调出隐藏的程序
Mutex 互斥锁
程序启动入口添加互斥锁判断:
public System.Threading.Mutex mutex;
public static bool AppSingletonMark = false;
protected override void OnStartup(StartupEventArgs e)
{
bool isNew;
mutex = new Mutex(true, "Singleton Instance Sample", out isNew);
if (!isNew)//isNew为false代表已经存在,所以需要关闭正在打开的程序
{
//这里可以添加唤起已启动的程序
... ...
AppSingletonMark = true;//标识是否需要隐藏程序
Shutdown();
}
}
NotifyIcon 系统托盘
在主窗体加载的同时创建系统托盘:
private System.Windows.Forms.NotifyIcon notifyIcon;
private void InitialTray()
{
this.notifyIcon = new System.Windows.Forms.NotifyIcon();
this.notifyIcon.BalloonTipText = "提示文本";
this.notifyIcon.ShowBalloonTip(8000);
this.notifyIcon.Text = "Wits监听中...";
//this.notifyIcon.Icon = new System.Drawing.Icon("../../activity_monitor.ico");
//以上一句替换成下面内容,意思就是读取程序图标,来作为托盘图标
this.notifyIcon.Icon = System.Drawing.Icon.ExtractAssociatedIcon(System.Windows.Forms.Application.ExecutablePath);
this.notifyIcon.Visible = true;
//打开菜单项
System.Windows.Forms.MenuItem show = new System.Windows.Forms.MenuItem("Show");
show.Click += new EventHandler(Show);
//退出菜单项
System.Windows.Forms.MenuItem exit = new System.Windows.Forms.MenuItem("Exit");
exit.Click += new EventHandler(Close);
//将菜单关联到托盘控件
System.Windows.Forms.MenuItem[] childen = new System.Windows.Forms.MenuItem[] { show, exit };
this.notifyIcon.ContextMenu = new System.Windows.Forms.ContextMenu(childen);
//单机系统托盘图标,显示程序
this.notifyIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler((o, e) =>
{
if (e.Button == MouseButtons.Left) this.Show(o, e);
});
}
/// <summary>
/// 显示主窗体
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Show(object sender, EventArgs e)
{
this.Visibility = System.Windows.Visibility.Visible;
this.ShowInTaskbar = true;
this.Activate();
//this.notifyIcon.Visible = false;
}
private void Close(object sender, EventArgs e)
{
Environment.Exit(0);
System.Windows.Application.Current.Shutdown();
}
Win32 唤起隐藏程序
需要注意的是对于隐藏的程序还要使用
SendMessage
否则程序将会黑屏
/// <summary>
/// 正常显示窗口值
/// </summary>
private const int WS_SHOWNORMAL = 1;
/// <summary>
/// 当隐藏或显示窗口是发送此消息给这个窗口
/// </summary>
private const int WM_SHOWWINDOW =0x18;
/// <summary>
/// 设置由不同线程产生的窗口的显示状态
/// </summary>
/// <param name="hWnd">窗口句柄</param>
/// <param name="cmdShow">窗口如何显示</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);
/// <summary>
/// 查找窗口句柄
/// </summary>
/// <param name="lpClassName">窗口类名</param>
/// <param name="lpWindowName">窗口文本</param>
/// <returns></returns>
[DllImport("User32.dll", EntryPoint = "FindWindow")]
private extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("User32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
public void HandleRunningInstance(string windowsTitle)
{
//通过窗体的title查找句柄
var windowHandle = FindWindow(null, windowsTitle);
//发送显示消息到指定窗体
SendMessage(windowHandle, WM_SHOWWINDOW, (IntPtr)1,(IntPtr)3);
//确保窗口没有被最小化或最大化
ShowWindowAsync(windowHandle, WS_SHOWNORMAL);
//设置为foreground window
SetForegroundWindow(windowHandle);
}
许可协议:
CC BY 4.0