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,可以参考的链接(关于该话题的讨论较老了):、。