本文介绍几种使应用一直置于顶层的方法。

问题描述

一般情况下,想要将应用置于顶层,设置其TopMost属性为true即可。对于多个设置了TopMost属性的应用,后激活的在上面。

但有的应用,比如全局的快捷操作工具条,它需要在所有应用之上,即使是设置了TopMost的应用。

解决思路

注意:使某个应用永远不会被其它应用覆盖,这本身是个伪命题。因为假如有两个程序(A和B)这样做,拖动两个窗口使它们重叠,这两个窗口中的一个必须在另一个之上,这在逻辑上是互相矛盾的。

所以应该尽量避免这种情况,如果非要这样做,本文提供如下几种办法实现(不要将两个这样的应用重叠,否则会不停将置顶)。

首先,该应用程序需要设置其TopMost属性为true,这样普通窗口本身就会在它下面。本文主要讨论该窗口如何置于设置了TopMost属性的窗口之上。

方案一:捕获WM_WINDOWPOSCHANGING消息

我们知道,使用Win32的SetWindowPos接口可以改变窗口的Z Order,可以猜测,当另外一个应用置顶时,我们的应用会改变其Z Order,因此,我们可以尝试捕获WM_WINDOWPOSCHANGING消息。

当窗口的大小、位置、Z序改变时,窗口会接收到WM_WINDOWPOSCHANGING消息,我们可以使用WndProc处理窗口消息。当捕获到该消息时,我们可以尝试将应用再次置顶。关键代码如下,测试可行,但不确定是否有副作用:

/// <summary>
/// 方案一:捕获WM_WINDOWPOSCHANGING消息,若无SWP_NOZORDER标志,则置顶
/// </summary>
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case Win32Api.WM_WINDOWPOSCHANGING:
Win32Api.WINDOWPOS wp = (Win32Api.WINDOWPOS)Marshal.PtrToStructure(
lParam, typeof(Win32Api.WINDOWPOS));
if ((wp.flags & Win32Api.SWP_NOZORDER) == 0)
_ = SetTopMostLater(); // 不使用弃元编译器会发出警告
break;
} return IntPtr.Zero;
} private async Task SetTopMostLater()
{
await Task.Delay(300);
var interopHelper = new WindowInteropHelper(this);
Win32Api.SetWindowPos(interopHelper.Handle, Win32Api.HWND_TOPMOST, 0, 0, 0, 0, Win32Api.TOPMOST_FLAGS);
}

方案二:循环置顶

这个是比较容易想到的一个方案,每隔一定的时间给应用设置下TopMost,该方案也是可行的:

/// <summary>
/// 方案二:循环置顶
/// </summary>
/// <returns></returns>
private async Task SetTopMostLoop()
{
while (true)
{
await Task.Delay(2000);
var interopHelper = new WindowInteropHelper(this);
Win32Api.SetWindowPos(interopHelper.Handle, Win32Api.HWND_TOPMOST, 0, 0, 0, 0, Win32Api.TOPMOST_FLAGS);
}
}

方案三:使用钩子

思考一下,其实大部分情况下,使用鼠标或键盘等其它输入设备才会导致窗口的置顶被抢,因此可以使用全局钩子捕获输入事件,然后进行处理。

该方案是存在瑕疵的,因为存在不使用输入设备打开某个应用的情况,这种情况下置顶效果就会被新打开的置顶应用抢占。

// 方案三:当鼠标按下时置顶(仅考虑了鼠标)
private void MouseHook_OnMouseActivity(object sender, System.Windows.Forms.MouseEventArgs e)
{
Console.WriteLine("mouse down......");
_ = SetTopMostLater();
} private MouseHook _mouseHook;

最后,本文是我对该问题想到的一些解决方案,Windows系统的任务管理器可以运行在所有应用的最上层,也许微软正是考虑到上文提到的伪命题,因此没有开放该接口吧,了解原理的小伙伴欢迎讨论。

本文三种方案的完整demo见GitHub,可以参考的链接(关于该话题的讨论较老了):链接一链接二

最新文章

  1. mave之:java的web项目必须要的三个jar的pom形式
  2. iOS实用的小技巧
  3. 关于Spring的Controller及Struts的Action的多线程的注意
  4. angularjs kindEditor 中content获得不到值
  5. uva 10034 Problem A: Freckles
  6. [转]在ITunes播放中前进、后退五秒的快捷键
  7. python学习日记(isinstance和issubclass)
  8. C++\CLI语法 在项目中的使用
  9. Python简单实现KNN算法
  10. Python_tuple部分功能介绍
  11. SQL SERVER查询的临时文件路径
  12. linux的必知必会规则
  13. Eclipse+Spring boot开发教程
  14. elasticsearch搜索引擎环境的搭建
  15. Linux常用命令-vim
  16. 20155313 2016-2017-2 《Java程序设计》第五周学习总结
  17. RHEL/CentOS 一些不错的第三方软件包仓库
  18. day29akka
  19. 【Atcoder】ARC102 题解
  20. 微软BI 之SSRS 系列 - 报表邮件订阅中 SMTP 服务器匿名访问与 Windows验证, 以及如何成功订阅报表的实例

热门文章

  1. PTA 第三章 栈与队列
  2. 如何利用CRM系统打通营销全渠道?
  3. CRM系统实现自动化的“三部曲”
  4. Git 系列教程(12)- 分支的新建与合并
  5. 【建议收藏】缺少 Vue3 和 Spring Boot 的实战项目经验?我这儿有啊!
  6. Netperf测试技巧
  7. Linux_搭建NFS服务(基础)
  8. 项目实践之工作流引擎基本文档!Activiti工作流框架中流程引擎API和服务详解
  9. 【无人机航空摄影测量精品教程】目录:摄影测量、Pix4d、EPS、CC、PhotoScan项目化作业流程及注意事项汇总
  10. Ubuntu中的MySQL修改root密码的多种方法