Win32Api -- 使应用Always on top的几种方法

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

问题描述

免费优惠券 m.cps3.cn

一般情况下,想要将应用置于顶层,设置其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,可以参考的链接(关于该话题的讨论较老了):链接一、链接二。

The SetForegroundWindow Win32-API function is used to bring a window to the foreground and give it focus. However, it may not always work on Windows 7 due to certain security enhancements introduced in the operating system. One possible reason why SetForegroundWindow may fail on Windows 7 is because of the User Interface Privilege Isolation (UIPI) feature. UIPI restricts applications from sending messages to higher integrity level processes, which can prevent a lower integrity level process from bringing a higher integrity level window to the foreground. Another reason could be related to the focus-stealing prevention feature in Windows 7. This feature prevents applications from stealing focus from the user, which can be particularly useful in preventing malware from taking control of a user's system. To ensure that SetForegroundWindow works correctly on Windows 7, you can try the following: 1. Ensure that the window you are trying to bring to the foreground is not running with higher privileges than your application. 2. Use the AttachThreadInput API to attach the input queues of your application and the target window's thread before calling SetForegroundWindow. 3. Use the AllowSetForegroundWindow API to grant your application permission to bring the target window to the foreground. 4. Consider using the SetWindowPos API instead of SetForegroundWindow to adjust the position of the target window. If you continue to experience issues with SetForegroundWindow on Windows 7, you may need to consider alternative methods for achieving the same functionality.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值