扩展显示器下获取其他程序窗口位置及全屏检测问题
解决程序扩展显示器全屏检测中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
相关产品推荐
相关产品推荐

