1.问题
禁止程序多实例运行时,防止多实例简单,这里不做介绍。这里着重关注怎样激活运行中的实例的窗口,常规方法都不那么完美,如当目标窗口被其它窗口遮蔽时,想把它置顶,如下方法都没用
- this.Show();
- this.Activate();
- this.BringToFront();
- ShowWindow(hWnd, SW_RESTORE);
- SetForegroundWindow(hWnd);
2.两个半吊子方法鉴赏
下面两个方法,能实现激活&置顶,但是有瑕疵,大家不要踩坑。它们具体是
2.1.用this.TopMost=true
实现了对窗口的激活&置顶,但改变了窗口的特性——窗口永久置顶,且总是置于其它窗口之上,这个效果很多余,很可能我们并不希望如此
2.2.先最小化再恢复显示
if (this.Visible)
{
this.WindowState = FormWindowState.Minimized;
}
this.Show();
this.WindowState = lastWindowState; //Form最后WindowState,但不包括最小化
实现了对窗口的激活&置顶,且是临时置顶。但是,当窗口本身就在顶层可见时,跑上面的代码窗口会肉眼可见夸张的闪烁。
3.推荐方法
摸索出如下方法,不一定是最好的,但保证体验丝滑
用自定义的windows消息通知已存在的实例:请恢复你的窗口显示&临时将窗口置顶
Program.css
实现:
- 禁止本程序的多实例运行
- 如果已有实例在运行,则用windows消息通知对方恢复显示
static class Program
{
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
static void Main()
{
// 尝试创建Mutex
bool createdNew;
using (Mutex mutex = new Mutex(false, "APP_MUTEX_ID__MYAPP", out createdNew)) // 常量为自定义值,作为本程序的唯一Id
{
if (!createdNew) //创建失败说明已存在。则激活已存在的窗口
{
// 根据Form的Caption查找窗口句柄
IntPtr hWnd = FindWindow(null, "我的app的Title");
if (hWnd != IntPtr.Zero)
{
// 发送自定义消息,要求激活本APP的窗口
PostMessage(hWnd, CustomMessages.WM_CUSTOM_MESSAGE_ACTIVEWINDOWS, IntPtr.Zero, IntPtr.Zero);
}
else
{
MessageBox.Show("检测到有一个实例正在运行!", "提示");
}
// 退出当前实例
return;
}
// 打开frmMain
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmMain());
}
}
}
frmMain.css
实现:
监听windows消息,在需要时临时置顶本Form
public partial class frmMain : Form
{
private FormWindowState lastWindowState; // Form最后WindowState,但不包括最小化
public frmMain()
{
InitializeComponent();
this.Text = "我的app的Title";
}
private void frmMain_Resize(object sender, EventArgs e)
{
if (this.WindowState != FormWindowState.Minimized)
{
lastWindowState = this.WindowState;
}
}
// 重写WndProc方法
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case CustomMessages.WM_CUSTOM_MESSAGE_ACTIVEWINDOWS: // 自定义消息-激活窗口,实现被外部程序激活本Form
ShowMe();
break;
default:
base.WndProc(ref m);
break;
}
}
private void ShowMe()
{
if (this.Visible == false || this.WindowState == FormWindowState.Minimized)
{
this.Show();
this.WindowState = lastWindowState;
}
this.TopMost = true; // 全局置顶
this.TopMost = false; // 取消全局置顶,但上句已完成此次置顶
}
}