Win32Api -- 使应用Always on top的几种方法
本文介绍几种使应用一直置于顶层的方法。
问题描述
一般情况下,想要将应用置于顶层,设置其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,可以参考的链接(关于该话题的讨论较老了):链接一、链接二。
Win32Api -- 使应用Always on top的几种方法的更多相关文章
- Duilib改进窗口拖动,使整个窗口都能拖动两种方法(转载)
转载:http://www.cnblogs.com/XiHua/articles/3490490.html 转载:http://blog.csdn.net/lostspeed/article/deta ...
- java使double保留两位小数的多方法 java保留两位小数
这篇文章主要介绍了java使double类型保留两位小数的方法,大家参考使用吧 复制代码 代码如下: mport java.text.DecimalFormat; DecimalFormat d ...
- F - Goldbach`s Conjecture 对一个大于2的偶数n,找有多少种方法使两个素数的和为n;保证素数a<=b; a+b==n; a,b都为素数。
/** 题目:F - Goldbach`s Conjecture 链接:https://vjudge.net/contest/154246#problem/F 题意:对一个大于2的偶数n,找有多少种方 ...
- 在vc6.0下编的对话框界面如果没做过其他处理,往往显的很生硬,怎么样才能使他有Windows XP的风格呢,其实也很简单,我们来看看下面两种方法。
在vc6.0下编的对话框界面如果没做过其他处理,往往显的很生硬,怎么样才能使他有Windows XP的风格呢,其实也很简单,我们来看看下面两种方法. 方法一: 1.首先确认你在Windows ...
- java使double保留两位小数的多方法
java使double保留两位小数的多方法 java保留两位小数 mport java.text.DecimalFormat; DecimalFormat df = new DecimalFormat ...
- 使WordPress改域名后网站正常运行的方法
使WordPress改域名后网站正常运行的方法 wp-content/wp-config.php $path = '/blog'; $scheme = (isset($_SERVER['HTTPS'] ...
- Select Top在七种数据库中的使用方法(包含mysql)
1. Oracle数据库 SELECT * FROM TABLE1 WHERE ROWNUM<=N 2. Infomix数据库 SELECT FIRST N * FROM TABLE1 3. D ...
- 分页查询的两种方法(双top 双order 和 row_number() over ())
--跳过10条取2条 也叫分页select top 2 * from studentwhere studentno not in (select top 2 studentno from studen ...
- access数据库 top语句失效解决方法
使用查询语句 select top 1 * from News order by [PublicTime] desc 就不一定管用了,如果News表里面的PublicTime字段 ...
随机推荐
- 测试中常用的链接URL----方便自己查找
1.TesterHome:https://testerhome.com/ 2.selenium的操作手册:https://selenium-python.readthedocs.io/ 3.
- 17.继承 and18.接口和多态 内部类 匿名内部类,Lambda表达式
1. 继承 1.1 继承的实现(掌握) 继承的概念 继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法 实现继承的格式 继承通过extends实现 ...
- stm32开发笔记(三):stm32系列的GPIO基本功能之输出驱动LED灯、输入按键KEY以及Demo
前言 stm32系列是最常用的单片机之一,不同的版本对应除了引脚.外设.频率.容量等'不同之外,其开发的方法是一样的. 本章讲解使用GPIO引脚功能驱动LED灯和接收Key按钮输入. STM ...
- 密码学系列之:memory-hard函数
密码学系列之:memory-hard函数 目录 简介 为什么需要MHF Memory hard的评估方法 MHF的种类 MHF的密码学意义 memory-hard在MHF中的应用 简介 Memory ...
- grub救援模式
http://www.jinbuguo.com/linux/grub.cfg.html
- gparted 当分区空间大于1T 用gparted分区
lsblkfdisk -lparted -s /dev/sdb mklabel msdos parted -s /dev/sdb mkpart primary 0 100%lsblk dfparted ...
- Ubuntu 18.04 进入单用户模式修改密码
Ubuntu 18.04 使用单用户模式修改密码 操作步骤 启动Ubuntu 18.04 ,长按 Shift 键(有的可能按 Esc 键:绝大多数按 Shift 键)进入单用户视图,选中 Ubuntu ...
- 使用find命令查找大文件
使用find命令查找大文件 find命令是Linux系统管理员工具库中最强大的工具之一.它允许您根据不同的标准(包括文件大小)搜索文件和目录. 例如,如果在当前工作目录中要搜索大小超过100MB的文件 ...
- 013.Ansible Playbook include
一 include 当项目越大,tasks越多的时候.如果将多有的task写入一个playbook中,可读性很差,就需要重新组织playbook 可以把一个playbook分成若干份晓得palyboo ...
- 云计算OpenStack---创建实例(11)
创建实例时,需要先创建网络及实例类型等其它配置: 一.创建虚拟网络(Self-service network) 网络概述图: 网络连接图: 1.创建self-service网络 (1)加载系统变量: ...