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

扩展显示器下获取其他程序窗口位置及全屏检测问题

解决程序扩展显示器全屏检测中GetWindowRect结果异常的问题

核心问题分析

你的代码存在几个关键缺陷,直接导致GetWindowRect返回异常结果:

1. 窗口枚举逻辑错误

一个进程可能包含多个窗口(比如主窗口、隐藏辅助窗口、托盘弹窗等),当前EnumWindowsProcMy仅匹配进程ID就立刻返回第一个窗口,大概率拿到的不是目标程序的可见主窗口,自然无法获取正确的全屏窗口坐标。

2. 显示器枚举逻辑缺陷

  • 静态变量first会导致多次调用EnumDisplayMonitors时状态混乱,比如第二次枚举时first仍为TRUE,会错误跳过主显示器的存储。
  • 固定数组rect[2]只能存储一个扩展显示器位置,若系统存在多个扩展显示器,后续显示器坐标会覆盖之前的,无法正确匹配。

3. 缺少有效性检查

未判断g_HWND是否有效(比如进程存在但无可见窗口),也未检查GetWindowRect的返回值,调用失败时会得到无效的矩形数据。


修正后的实现方案

1. 修正窗口枚举逻辑,获取进程主窗口

只枚举可见的顶层主窗口(排除隐藏窗口、子窗口):

HWND g_HWND = NULL;
BOOL CALLBACK EnumWindowsProcMy(HWND hwnd, LPARAM lParam)
{
    DWORD lpdwProcessId;
    GetWindowThreadProcessId(hwnd, &lpdwProcessId);
    if (lpdwProcessId == lParam)
    {
        // 筛选可见的顶层主窗口
        if (IsWindowVisible(hwnd) && GetParent(hwnd) == NULL)
        {
            g_HWND = hwnd;
            return FALSE; // 找到目标主窗口后停止枚举
        }
    }
    return TRUE;
}

2. 修正显示器枚举逻辑,收集所有显示器信息

用动态数组存储所有显示器的位置和主副属性,避免多个扩展显示器的覆盖问题:

#include <vector>
using namespace std;

// 存储显示器信息的结构体
struct MonitorData {
    CRect rcMonitor;
    BOOL isPrimary;
};

vector<MonitorData> g_Monitors;

BOOL CALLBACK Monitorenumproc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
    MONITORINFO monitorinfo;
    monitorinfo.cbSize = sizeof(MONITORINFO);
    if (GetMonitorInfo(hMonitor, &monitorinfo))
    {
        MonitorData data;
        data.rcMonitor = monitorinfo.rcMonitor;
        data.isPrimary = (monitorinfo.dwFlags == MONITORINFOF_PRIMARY);
        g_Monitors.push_back(data);
    }
    return TRUE;
}

3. 完整检测流程(含有效性检查)

// 1. 枚举所有显示器
g_Monitors.clear();
EnumDisplayMonitors(NULL, NULL, Monitorenumproc, 0);

// 2. 获取目标进程ID
DWORD targetPid = GetProcessidFromName(_T("xxx.exe"));
if (targetPid == 0)
{
    // 未找到目标进程,处理错误逻辑
    return;
}

// 3. 枚举窗口获取主窗口
g_HWND = NULL;
EnumWindows(EnumWindowsProcMy, targetPid);
if (g_HWND == NULL)
{
    // 未找到目标进程的可见主窗口,处理错误逻辑
    return;
}

// 4. 获取窗口矩形并检查有效性
RECT windowRect;
if (!::GetWindowRect(g_HWND, &windowRect))
{
    // GetWindowRect调用失败,处理错误逻辑
    return;
}

// 5. 判断是否在扩展显示器全屏
BOOL isFullScreenOnExtended = FALSE;
for (const auto& monitor : g_Monitors)
{
    if (!monitor.isPrimary) // 仅检查扩展显示器
    {
        // 两种判断方式可选:
        // 方式1:窗口完全覆盖显示器(适用于自定义全屏的程序)
        bool isRectMatch = (windowRect.left == monitor.rcMonitor.left &&
                           windowRect.top == monitor.rcMonitor.top &&
                           windowRect.right == monitor.rcMonitor.right &&
                           windowRect.bottom == monitor.rcMonitor.bottom);
        // 方式2:结合最大化状态(适用于系统最大化的全屏)
        bool isMaximized = IsZoomed(g_HWND);
        
        if (isRectMatch && isMaximized)
        {
            isFullScreenOnExtended = TRUE;
            break;
        }
    }
}

// 根据isFullScreenOnExtended处理后续逻辑
if (isFullScreenOnExtended)
{
    // 程序在扩展显示器处于全屏状态
}
else
{
    // 程序未在扩展显示器全屏
}

额外注意事项

  • 高DPI适配:若系统开启高DPI缩放,需确保你的程序是DPI感知的,否则GetWindowRect返回的坐标会被系统缩放,导致和显示器坐标不匹配。可通过程序清单添加dpiAware属性,或调用SetProcessDpiAwarenessContext设置DPI感知。
  • 特殊全屏程序:部分游戏或视频软件会使用独占全屏模式,此时需要通过GetMonitorInfo结合MonitorFromWindow判断窗口所属显示器,再检查窗口是否覆盖该显示器。

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

相关产品推荐
方舟 Agent Plan

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

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