乐趣区

关于c#: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,能够参考的链接(对于该话题的探讨较老了):链接一、链接二。

退出移动版