· 1. DWM 缩略图和模糊隐藏实现半透明
#include <windows.h>
#include <dwmapi.h>
#include <string>
#pragma comment(lib, "dwmapi.lib")
// 检查 UWP 窗口是否可见
bool IsUWPWindowVisible(HWND hwnd) {
DWORD cloaked = 0;
DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &cloaked, sizeof(cloaked));
return (cloaked == 0);
}
bool IsBadWindow(HWND hwnd) {
wchar_t title[256];
wchar_t className[256];
GetWindowTextW(hwnd, title, 256);
GetClassNameW(hwnd, className, 256);
LPCWSTR badTitleList[] = { L"NVIDIA", L"Xaml" };
for (LPCWSTR badTitle : badTitleList) {
if (wcsstr(title, badTitle) != nullptr) {
return true;
}
}
return false;
}
void UpdateInfoWindow(HWND infoHwnd, HWND mainHwnd) {
if (infoHwnd) {
RECT mdrc;
GetWindowRect(mainHwnd, &mdrc);
// 获取屏幕工作区和任务栏的位置信息
RECT rcWorkArea = { 0 };
SystemParametersInfoW(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
HWND hTrayWnd = FindWindowW(L"Shell_TrayWnd", nullptr);
// 计算任务栏是否遮挡信息窗口
bool taskbarVisible = false;
if (IsWindow(hTrayWnd)
&& IsWindowVisible(hTrayWnd)) {
RECT tbrc;
GetWindowRect(hTrayWnd, &tbrc);
if (tbrc.top >= rcWorkArea.bottom && mdrc.bottom + 50 > tbrc.top) {
// 任务栏在底部且信息窗口底部超出工作区域底部
taskbarVisible = true;
}
}
if (taskbarVisible) {
// 将信息窗口移动到窗口上方
SetWindowPos(infoHwnd, nullptr, mdrc.left + 10, mdrc.top - 110,
mdrc.right - mdrc.left - 25,
100, SWP_NOZORDER | SWP_SHOWWINDOW);
}
else {
// 将信息窗口移动到窗口底部
SetWindowPos(infoHwnd, nullptr, mdrc.left + 10, mdrc.bottom,
mdrc.right - mdrc.left - 25,
100, SWP_NOZORDER | SWP_SHOWWINDOW);
}
}
}
LRESULT CALLBACK InfoWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc);
// 获取目标窗口信息
HWND targetHwnd = (HWND)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
if (targetHwnd) {
wchar_t title[256];
wchar_t className[256];
GetWindowTextW(targetHwnd, title, 256);
GetClassNameW(targetHwnd, className, 256);
wchar_t info[512];
swprintf(info, 512, L"Title: %s\nClass: %s\nHandle: 0x%llX",
title, className, (UINT_PTR)targetHwnd);
// 绘制信息文本
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, RGB(0, 0, 0));
DrawTextW(hdc, info, -1, &rc, DT_LEFT | DT_TOP | DT_NOPREFIX);
}
EndPaint(hwnd, &ps);
return 0;
}
default:
break;
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static HTHUMBNAIL thumbnail = NULL;
static HWND infoHwnd = NULL;
static HWND oldBehindWnd = NULL;
// 最小窗口尺寸
const int MIN_WIDTH = 100;
const int MIN_HEIGHT = 100;
switch (uMsg) {
case WM_CREATE: {
// 创建子窗口用于显示信息
HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtrW(hwnd, GWLP_HINSTANCE);
const wchar_t INFO_CLASS_NAME[] = L"InfoWindowClass";
WNDCLASS wc = {};
wc.lpfnWndProc = InfoWindowProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 白色背景
wc.lpszClassName = INFO_CLASS_NAME;
RegisterClassW(&wc);
infoHwnd = CreateWindowExW(
WS_EX_TRANSPARENT | WS_EX_NOACTIVATE, // 扩展窗口样式
INFO_CLASS_NAME, // 窗口类名
nullptr, // 窗口标题
WS_POPUP, // 窗口样式
0, 0, 800, 100, // 窗口尺寸
hwnd, nullptr, hInstance, nullptr
);
if (infoHwnd == nullptr) {
return 0;
}
SetLayeredWindowAttributes(infoHwnd, RGB(0,0,0), 0, LWA_COLORKEY);
// 初始化信息窗口的位置
RECT rc;
GetWindowRect(hwnd, &rc);
SetWindowPos(infoHwnd, nullptr, rc.left + 10, rc.bottom, 0, 0,
SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
}
case WM_ACTIVATE: {
// 查找第一个可见且尺寸不小于最小尺寸的后台窗口
HWND hwndBehind = GetWindow(hwnd, GW_HWNDNEXT);
while (hwndBehind) {
if (IsWindowVisible(hwndBehind)
&& IsUWPWindowVisible(hwndBehind)
&& !IsBadWindow(hwndBehind)) {
RECT rcBehind;
GetWindowRect(hwndBehind, &rcBehind);
int width = rcBehind.right - rcBehind.left;
int height = rcBehind.bottom - rcBehind.top;
if (width >= MIN_WIDTH && height >= MIN_HEIGHT) {
break;
}
}
hwndBehind = GetWindow(hwndBehind, GW_HWNDNEXT);
}
if (oldBehindWnd != hwndBehind) {
// 注销当前缩略图
if (thumbnail) {
DwmUnregisterThumbnail(thumbnail);
thumbnail = NULL;
}
if (hwndBehind) {
oldBehindWnd = hwndBehind;
// 注册后台窗口的缩略图
DwmRegisterThumbnail(hwnd, hwndBehind, &thumbnail);
RECT rcBehindWnd;
GetWindowRect(hwndBehind, &rcBehindWnd);
SetWindowPos(hwnd, nullptr,
0, 0,
rcBehindWnd.right - rcBehindWnd.left,
rcBehindWnd.bottom - rcBehindWnd.top + 50,
SWP_NOZORDER | SWP_NOMOVE | SWP_SHOWWINDOW);
// 更新缩略图属性
RECT rcClient;
GetClientRect(hwnd, &rcClient);
DWM_THUMBNAIL_PROPERTIES dpt;
dpt.dwFlags = DWM_TNP_RECTDESTINATION | DWM_TNP_OPACITY;
dpt.rcDestination = rcClient;
dpt.opacity = (BYTE)(255 * 0.7); // 设置透明度为70%
DwmUpdateThumbnailProperties(thumbnail, &dpt);
// 设置子窗口的用户数据为目标窗口句柄
SetWindowLongPtrW(infoHwnd, GWLP_USERDATA, (LONG_PTR)hwndBehind);
InvalidateRect(infoHwnd, NULL, TRUE); // 重新绘制子窗口
}
else {
// 设置子窗口的用户数据为目标窗口句柄
SetWindowLongPtrW(infoHwnd, GWLP_USERDATA, (LONG_PTR)NULL);
MessageBoxW(NULL,
L"没有找到合适窗口", L"提示",
MB_OK | MB_ICONINFORMATION |
MB_APPLMODAL | MB_TOPMOST);
}
}
return 0;
}
case WM_DESTROY:
if (thumbnail) {
DwmUnregisterThumbnail(thumbnail);
}
PostQuitMessage(0);
return 0;
case WM_SIZE: {
if (thumbnail) {
RECT rcClient;
GetClientRect(hwnd, &rcClient);
// 窗口大小改变时重新扩展DWM框架到客户区
MARGINS margins = { 0, 0, 0, rcClient.bottom - rcClient.top };
DwmExtendFrameIntoClientArea(hwnd, &margins);
// 更新缩略图属性
DWM_THUMBNAIL_PROPERTIES dpt;
dpt.dwFlags = DWM_TNP_RECTDESTINATION | DWM_TNP_OPACITY;
dpt.rcDestination = rcClient;
dpt.opacity = (BYTE)(255 * 0.7); // 设置透明度为70%
DwmUpdateThumbnailProperties(thumbnail, &dpt);
}
// 更新信息窗口的位置
UpdateInfoWindow(infoHwnd, hwnd);
return 0;
}
case WM_MOVE: {
// 更新信息窗口的位置
UpdateInfoWindow(infoHwnd, hwnd);
return 0;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc;
GetClientRect(hwnd, &rc);
// 绘制半透明背景
HBRUSH hBrush = CreateSolidBrush(RGB(255, 255, 255));
SetBkMode(hdc, TRANSPARENT);
FillRect(hdc, &rc, hBrush);
DeleteObject(hBrush);
EndPaint(hwnd, &ps);
return 0;
}
default:
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow) {
// 注册窗口类
const wchar_t CLASS_NAME[] = L"TransparentWindowClass";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); // 防止背景重绘
wc.lpszClassName = CLASS_NAME;
RegisterClassW(&wc);
// 创建窗口
HWND hwnd = CreateWindowExW(
WS_EX_NOREDIRECTIONBITMAP, // 扩展窗口样式
CLASS_NAME, // 窗口类名
L"Transparent Window", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式
CW_USEDEFAULT, CW_USEDEFAULT, 1000, 600,
nullptr, nullptr, hInstance, nullptr
);
if (hwnd == nullptr) {
return 0;
}
// 使用 DWM API 设置毛玻璃效果
DWM_BLURBEHIND bb = { 0 };
bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
bb.fEnable = TRUE;
bb.hRgnBlur = NULL;
DwmEnableBlurBehindWindow(hwnd, &bb);
// 设置标题栏颜色为深色(示例为灰色)
COLORREF grey = RGB(30, 30, 30);
DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, &grey, sizeof(grey));
// 显示窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// 消息循环
MSG msg = {};
while (GetMessageW(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
效果图:
窗口切换和大小跟随:
· 2. 更多代码......
(...)
本文发布于:2024.07.08