You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现类似Firefox画中画的始终置顶窗口?解决Win+D失效问题

解决窗口置顶失效问题的方案

针对Win+D导致窗口隐藏、Brave浏览器覆盖置顶窗口的问题,可以通过拦截系统命令+定期维持置顶状态的方式解决,以下是修改后的完整代码:

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace fixedWindow
{
    public partial class Form1 : Form
    {
        private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        private const uint SWP_NOSIZE = 0x0001;
        private const uint SWP_NOMOVE = 0x0002;
        private const uint TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;
        private const uint SWP_NOZORDER = 0x0004;

        private Timer _topmostMaintenanceTimer;

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        // 用于解析WM_WINDOWPOSCHANGED消息的结构体
        [StructLayout(LayoutKind.Sequential)]
        private struct WINDOWPOS
        {
            public IntPtr hwnd;
            public IntPtr hwndInsertAfter;
            public int x;
            public int y;
            public int cx;
            public int cy;
            public uint flags;
        }

        public Form1()
        {
            InitializeComponent();
            InitializeTopmostMaintenance();
        }

        private void InitializeTopmostMaintenance()
        {
            // 初始化定时器,定期维持置顶状态
            _topmostMaintenanceTimer = new Timer
            {
                Interval = 500 // 每500毫秒检查一次
            };
            _topmostMaintenanceTimer.Tick += (sender, e) =>
            {
                // 强制重新设置置顶,覆盖其他程序的Z序修改
                SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
            };
            _topmostMaintenanceTimer.Start();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // 初始设置置顶
            SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
        }

        protected override void WndProc(ref Message m)
        {
            const int WM_SYSCOMMAND = 0x0112;
            const int SC_MINIMIZE = 0xF020;
            const int SC_DESKTOP = 0xF140;
            const int WM_WINDOWPOSCHANGED = 0x0047;

            switch (m.Msg)
            {
                case WM_SYSCOMMAND:
                    // 拦截最小化和显示桌面(Win+D)的命令,防止窗口被隐藏
                    int sysCmd = m.WParam.ToInt32() & 0xFFF0;
                    if (sysCmd == SC_MINIMIZE || sysCmd == SC_DESKTOP)
                    {
                        return; // 不执行默认处理,窗口保持显示
                    }
                    break;
                case WM_WINDOWPOSCHANGED:
                    // 监听窗口位置/Z序变化,若置顶状态被修改则立即恢复
                    WINDOWPOS windowPos = Marshal.PtrToStructure<WINDOWPOS>(m.LParam);
                    if ((windowPos.flags & SWP_NOZORDER) == 0 && windowPos.hwndInsertAfter != HWND_TOPMOST)
                    {
                        SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
                    }
                    break;
            }

            base.WndProc(ref m);
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            // 清理定时器资源
            _topmostMaintenanceTimer?.Stop();
            _topmostMaintenanceTimer?.Dispose();
            base.OnFormClosing(e);
        }
    }
}

关键部分说明

  1. 拦截Win+D命令:
    通过重写WndProc处理WM_SYSCOMMAND消息,拦截SC_MINIMIZE(窗口最小化)和SC_DESKTOP(Win+D显示桌面)命令,不让窗口执行隐藏/最小化操作,保持显示状态。

  2. 监听窗口Z序变化:
    处理WM_WINDOWPOSCHANGED消息,当窗口的Z序被其他程序修改(比如Brave浏览器启动时调整自身置顶),立即重新设置窗口为置顶状态,避免被覆盖。

  3. 定期维持置顶:
    用定时器每隔500毫秒强制调用SetWindowPos设置置顶,作为兜底方案,确保即使有未被拦截的Z序修改操作,窗口也能快速恢复置顶状态。

补充说明

  • 定时器的间隔可以根据需求调整,500毫秒既能保证响应速度,也不会造成过多性能开销。
  • 如果遇到其他程序强制抢占置顶的情况,可以适当缩短定时器间隔,或者监听WM_ACTIVATE消息,在窗口激活时重新设置置顶。

内容的提问来源于stack exchange,提问作者Coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 17:03:09