海南网站建设公司,宁波学校网站建设,网站备案新增域名,网络营销跟做网站有什么区别检测窗口是否最大化#xff08;窗口覆盖或独占全屏#xff09;兼容 Win10/11
问题描述
在 Win10/11 上有很多 UWP 进程#xff0c;检测窗口是否最大化将迎来新的挑战。这些窗口以其不能够使用 Win32 的 IsWindowVisible 获取窗口可见性为特征。此时#xff0c;必须使用 D…检测窗口是否最大化窗口覆盖或独占全屏兼容 Win10/11
问题描述
在 Win10/11 上有很多 UWP 进程检测窗口是否最大化将迎来新的挑战。这些窗口以其不能够使用 Win32 的 IsWindowVisible 获取窗口可见性为特征。此时必须使用 DWM API 来判断窗口的可见性状态。
代码实现
下面的代码实现了一个工具类检测当前桌面是否被覆盖以及覆盖窗口的信息。此代码参考 CustomDesktop 开源项目并做了预览桌面时的逃逸规则
“Singleton.h”
#pragma oncenamespace cd
{templateclass Tclass Singleton{protected:Singleton() default;virtual ~Singleton() default;public:static T GetInstance(){static T s_instance;return s_instance;}};#define DECL_SINGLETON(T) friend class SingletonT
#define DECL_SINGLETON_DEFAULT(T) \DECL_SINGLETON(T); \private: \T() default; \~T() default
}“CheckCovered.h”
#pragma once
#include Singleton.h
#include thread
#include memorynamespace cd
{// 检测桌面是否被遮挡了class CheckCovered final : public SingletonCheckCovered{DECL_SINGLETON(CheckCovered);public:bool IsReady() { return m_runThreadFlag; }bool Init();bool Uninit();private:CheckCovered();~CheckCovered();std::unique_ptrstd::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::functionvoid() function);CD_API void WINAPI ExecInMainThread(std::functionvoid() function){PostMessage(g_global.m_fileListWnd, WM_EXEC_FUNCTION, reinterpret_castWPARAM(new decltype(function)(std::move(function))), NULL);}*/ExecInMainThread([this]{ m_thread std::make_uniquestd::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, LLivePreview) 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, LLivePreview) NULL) { // 预览桌面窗口出现时恢复动画播放return true;}else {m_coveredByHwnd nullptr;return false;}}return false;}
}这里有两个点要说一下一是此代码需要完善多桌面的情况二是此代码考虑了预览桌面时候会产生一个窗口覆盖全屏的情况LivePreview为了避免检测失效应该排除此时的覆盖情况代码中也已经初步实现了。 本文发布于2024.06.10.