DWM 相关实现代码 [自用]

· 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

· 2. 更多代码......

(...)


本文发布于:2024.07.08

相关推荐

  1. 【C语言】自己代码实现字符串相关的常用API

    2024-07-11 23:48:04       57 阅读
  2. js相关dom方法

    2024-07-11 23:48:04       40 阅读
  3. 编译相关内容(自用

    2024-07-11 23:48:04       40 阅读
  4. mysql相关查询语法(自用

    2024-07-11 23:48:04       62 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-11 23:48:04       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-11 23:48:04       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-11 23:48:04       58 阅读
  4. Python语言-面向对象

    2024-07-11 23:48:04       69 阅读

热门阅读

  1. 2024.7.7刷题记录

    2024-07-11 23:48:04       20 阅读
  2. Vue3 + Vite项目使用SVG图片

    2024-07-11 23:48:04       19 阅读
  3. 代码随想录-DAY⑤-哈希表——leetcode 242 | 349 | 202

    2024-07-11 23:48:04       22 阅读
  4. Python爬虫-requests模块

    2024-07-11 23:48:04       25 阅读
  5. AIGC各个应用场景下的模型选择

    2024-07-11 23:48:04       24 阅读
  6. 在Linux中使用Typora将Markdown文档导出为docx格式

    2024-07-11 23:48:04       19 阅读