当前位置: 首页 > news >正文

检测窗口是否最大化兼容 Win10/11

检测窗口是否最大化(窗口覆盖或独占全屏)兼容 Win10/11

问题描述

在 Win10/11 上有很多 UWP 进程,检测窗口是否最大化将迎来新的挑战。这些窗口以其不能够使用 Win32 的 IsWindowVisible 获取窗口可见性为特征。此时,必须使用 DWM API 来判断窗口的可见性状态。

代码实现

下面的代码实现了一个工具类检测当前桌面是否被覆盖,以及覆盖窗口的信息。(此代码参考 CustomDesktop 开源项目,并做了预览桌面时的逃逸规则)

“Singleton.h” :

#pragma oncenamespace cd
{template<class T>class Singleton{protected:Singleton() = default;virtual ~Singleton() = default;public:static T& GetInstance(){static T s_instance;return s_instance;}};#define DECL_SINGLETON(T) friend class Singleton<T>
#define DECL_SINGLETON_DEFAULT(T) \DECL_SINGLETON(T); \private: \T() = default; \~T() = default
}

“CheckCovered.h”:

#pragma once
#include "Singleton.h"
#include <thread>
#include <memory>namespace cd
{// 检测桌面是否被遮挡了class CheckCovered final : public Singleton<CheckCovered>{DECL_SINGLETON(CheckCovered);public:bool IsReady() { return m_runThreadFlag; }bool Init();bool Uninit();private:CheckCovered();~CheckCovered();std::unique_ptr<std::thread> m_thread;bool m_runThreadFlag = true;bool m_isCovered = false;HWND m_coveredByHwnd = NULL;void CheckCoveredThread();bool IsDesktopCovered();};
}

“CheckCovered.cpp” :

#include "CheckCovered.h"#ifdef _WIN64
#include <Dwmapi.h>
#endifnamespace cd
{CheckCovered::CheckCovered(){Init();}CheckCovered::~CheckCovered(){Uninit();}bool CheckCovered::Init(){m_runThreadFlag = true;/* 在主线程中执行函数,可以用来做 dllmain 中不能完成的初始化,通过自定义消息实现// MYDLL_API void WINAPI ExecInMainThread(std::function<void()> function);CD_API void WINAPI ExecInMainThread(std::function<void()> function){PostMessage(g_global.m_fileListWnd, WM_EXEC_FUNCTION, reinterpret_cast<WPARAM>(new decltype(function)(std::move(function))), NULL);}*/ExecInMainThread([this]{ m_thread = std::make_unique<std::thread>(&CheckCovered::CheckCoveredThread, this); });return true;}bool CheckCovered::Uninit(){m_runThreadFlag = false;if (m_thread != nullptr && m_thread->joinable())m_thread->join();m_thread = nullptr;return true;}void CheckCovered::CheckCoveredThread(){while (m_runThreadFlag){if (IsDesktopCovered()){if (!m_isCovered){m_isCovered = true;//ExecInMainThread([]{ g_desktopCoveredEvent(); });#ifdef _DEBUGWCHAR windowName[100], className[100];GetWindowTextW(m_coveredByHwnd, windowName, _countof(windowName));GetClassNameW(m_coveredByHwnd, className, _countof(className));_RPTFW2(_CRT_WARN, L"桌面被 %s (%s) 遮挡\n", windowName, className);
#endif}}else{if (m_isCovered){m_isCovered = false;//ExecInMainThread([]{ g_desktopUncoveredEvent(); });_RPTF0(_CRT_WARN, "桌面从被遮挡恢复\n");}}for (int i = 0; i < 10; i++){if (!m_runThreadFlag)break;Sleep(100);}}}bool CheckCovered::IsDesktopCovered(){m_coveredByHwnd = NULL;// 对于 D3D 独占全屏的程序,不能用 IsZoomed 判断全屏// TODO:兼容多屏幕int screenWidth = GetSystemMetrics(SM_CXSCREEN);int screenHeight = GetSystemMetrics(SM_CYSCREEN);HWND hwnd = GetForegroundWindow();if (hwnd != GLOBAL_YOUR_WINDOW) // GLOBAL_YOUR_WINDOW 是你要检测是否被全屏幕覆盖的窗口{RECT rect;GetWindowRect(hwnd, &rect);if (rect.left == 0 && rect.top == 0&& rect.right == screenWidth && rect.bottom == screenHeight){WCHAR wsClassName[MAX_PATH] = { 0 };GetClassNameW(hwnd, wsClassName, MAX_PATH);if (wcsstr(wsClassName, L"LivePreview") == NULL) {  // 预览桌面窗口出现时,恢复动画播放m_coveredByHwnd = hwnd;return true;}else {m_coveredByHwnd = nullptr;return false;}}}EnumWindows([](HWND hwnd, LPARAM pCoveredByHwnd)->BOOL {
#ifdef _WIN64// 对于 win10 app,不能用 IsWindowVisible 判断是否可见DWORD cloaked = 0;DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &cloaked, sizeof(cloaked));if (cloaked != 0)return TRUE;
#endif// 有最大化的窗口而且可见则被遮挡(最小化也是不可见)if (IsZoomed(hwnd) && IsWindowVisible(hwnd)){*(HWND*)pCoveredByHwnd = hwnd;return FALSE;}return TRUE;}, (LPARAM)&m_coveredByHwnd);if (m_coveredByHwnd != nullptr) {WCHAR wsClassName[MAX_PATH] = { 0 };GetClassNameW(m_coveredByHwnd, wsClassName, MAX_PATH);if (wcsstr(wsClassName, L"LivePreview") == NULL) {  // 预览桌面窗口出现时,恢复动画播放return true;}else {m_coveredByHwnd = nullptr;return false;}}return false;}
}

这里有两个点要说一下,一是此代码需要完善多桌面的情况,二是此代码考虑了预览桌面时候会产生一个窗口覆盖全屏的情况(LivePreview),为了避免检测失效,应该排除此时的覆盖情况(代码中也已经初步实现了)。


本文发布于:2024.06.10.

http://www.lryc.cn/news/369028.html

相关文章:

  • 【qsort函数】
  • python类元编程示例-使用类型注解来检查转换属性值的类框架
  • Python3 笔记:字符串的 zfill() 和 rjust()
  • SpringBoot项目启动提示端口号占用
  • 音视频开发23 FFmpeg 音频重采样
  • windows系统下安装fnm
  • 【Linux网络】传输层协议 - UDP
  • debugger(四):源代码
  • 基于运动控制卡的圆柱坐标机械臂设计
  • MongoDBTemplate-基本文档查询
  • 23种设计模式——创建型模式
  • idm究竟有哪些优势
  • 如何学习Golang语言!
  • Redis系列之淘汰策略介绍
  • sql 调优
  • 【UML用户指南】-13-对高级结构建模-包
  • 前端面试题日常练-day63 【面试题】
  • GAN的入门理解
  • 43【PS 作图】颜色速途
  • 定个小目标之刷LeetCode热题(13)
  • 【AI大模型】Prompt Engineering
  • centos安装vscode的教程
  • 面试题------>MySQL!!!
  • 英伟达:史上最牛一笔天使投资
  • PDF分页处理:技术与实践
  • 数据可视化——pyecharts库绘图
  • Python的return和yield,哪个是你的菜?
  • 持续总结中!2024年面试必问 20 道分布式、微服务面试题(七)
  • AJAX 跨域
  • 3 数据类型、运算符与表达式-3.1 C语言的数据类型和3.2 常量与变量