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

如何在特定全屏应用上层以悬浮窗形式打开Form?

如何在特定全屏应用上层显示悬浮Form

全屏应用通常处于更高的显示层级,普通TopMost属性无法覆盖,结合Win32 API调整窗口关联与层级是可行方案,以下是具体实现步骤:

1. 配置悬浮Form的基础属性

确保Form不抢占焦点,避免干扰全屏应用,并设置合适的窗口样式:

public class DeckOverlayForm : Form
{
    // 禁止窗体激活,保证全屏应用始终获得焦点
    protected override bool ShowWithoutActivation => true;

    public DeckOverlayForm()
    {
        InitializeComponent();
        // 无边框样式,适配悬浮需求
        FormBorderStyle = FormBorderStyle.None;
        // 可选:设置透明背景,实现异形悬浮窗
        BackColor = Color.Magenta;
        TransparencyKey = Color.Magenta;
        // 铺满主屏幕,可根据需求调整区域
        Bounds = Screen.PrimaryScreen.Bounds;
    }
}

2. 用Win32 API绑定目标应用并设置层级

通过API将悬浮窗与目标应用窗口关联,确保悬浮窗始终显示在其上层,同时避免直接绑定父窗口导致的崩溃:

首先定义Win32 API封装类:

public static class NativeMethods
{
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

    [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);

    [DllImport("user32.dll")]
    public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll")]
    public static extern int SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

    public const int GWL_HWNDPARENT = -8;
    public const uint SWP_NOSIZE = 0x0001;
    public const uint SWP_NOMOVE = 0x0002;
    public const uint SWP_SHOWWINDOW = 0x0040;
    public static readonly IntPtr HWND_TOP = new IntPtr(0);
}

然后实现显示悬浮窗的逻辑:

private void TrackerManagerOnGameStarted(object? sender, EventArgs e)
{
    // 获取目标进程的主窗口句柄
    var targetProcess = Process.GetProcessesByName(PROCESS_NAME).FirstOrDefault();
    if (targetProcess == null || targetProcess.MainWindowHandle == IntPtr.Zero)
        return;

    _frm = new DeckOverlayForm();
    
    // 将悬浮窗与目标应用窗口建立关联,而非直接设为子窗口,避免生命周期绑定崩溃
    NativeMethods.SetWindowLongPtr(_frm.Handle, NativeMethods.GWL_HWNDPARENT, targetProcess.MainWindowHandle);
    
    // 强制将悬浮窗置于目标窗口上层
    NativeMethods.SetWindowPos(_frm.Handle, NativeMethods.HWND_TOP, 0, 0, 0, 0, 
        NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE | NativeMethods.SWP_SHOWWINDOW);
    
    _frm.Show();
}

3. 适配全屏应用的模式切换

部分全屏应用会在窗口/全屏模式间切换,可通过监听窗口激活事件,在目标应用重新激活时重新设置悬浮窗层级,确保显示正常:

// 示例:监听系统窗口激活事件
[DllImport("user32.dll")]
private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);

private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);

private void SetupWindowHook()
{
    SetWinEventHook(0x0008 /* EVENT_OBJECT_SHOW */, 0x0008, IntPtr.Zero, (hook, eventType, hwnd, idObj, idChild, thread, time) =>
    {
        // 判断激活的窗口是否为目标应用
        if (hwnd == targetProcess.MainWindowHandle && _frm != null)
        {
            NativeMethods.SetWindowPos(_frm.Handle, NativeMethods.HWND_TOP, 0, 0, 0, 0, 
                NativeMethods.SWP_NOSIZE | NativeMethods.SWP_NOMOVE);
        }
    }, 0, 0, 0x0005 /* WINEVENT_OUTOFCONTEXT */);
}

关键注意事项

  • 禁止使用NativeWindow.AssignHandle绑定父窗口,该方法会将窗体生命周期与目标应用强绑定,极易引发崩溃。
  • 必须保持ShowWithoutActivation = true,防止悬浮窗抢占全屏应用焦点,影响用户操作。
  • 针对DirectX/OpenGL类全屏应用,可额外设置窗体为分层窗口(添加WS_EX_LAYERED扩展样式),配合SetLayeredWindowAttributes实现更稳定的顶层显示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 13:05:54