You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在其他进程的窗口句柄(HWND)上绘制BITMAP

问题分析与解决方案

你的代码存在几个核心问题,导致绘制的内容无法持久显示在计算器窗口上:

  1. 临时绘制被窗口重绘覆盖:直接通过GetDC获取窗口DC绘制的内容是临时的,计算器窗口会在收到WM_PAINT消息时自动重绘自身界面,完全覆盖你绘制的截图。
  2. 未适配UWP计算器的窗口结构:现代Windows计算器是UWP应用,ApplicationFrameWindow只是外层容器,实际的内容显示窗口是它的子窗口,直接绘制到外层容器无法覆盖内部的计算器控件。
  3. 无持续更新机制:代码只执行一次绘制,无法实现"持续显示最新截图"的需求。

解决步骤

1. 定位计算器的实际内容窗口

遍历ApplicationFrameWindow的子窗口,找到负责显示内容的核心窗口(类名通常为Windows.UI.Core.CoreWindow):

HWND FindCalculatorContentWnd(HWND hFrameWnd) {
    return FindWindowEx(hFrameWnd, NULL, L"Windows.UI.Core.CoreWindow", NULL);
}

2. 子类化计算器内容窗口,拦截重绘消息

通过SetWindowSubclass子类化窗口,在WM_PAINT触发时强制绘制桌面截图,覆盖原界面:

#include <windows.h>
#include <commctrl.h>
#pragma comment(lib, "comctl32.lib")

HBITMAP g_hDesktopBmp = NULL; // 存储最新桌面截图
int g_ScreenWidth, g_ScreenHeight;

LRESULT CALLBACK CalculatorSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
    switch (uMsg) {
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            if (g_hDesktopBmp) {
                RECT rc;
                GetClientRect(hWnd, &rc);
                int clientWidth = rc.right - rc.left;
                int clientHeight = rc.bottom - rc.top;

                // 创建兼容DC并绘制截图
                HDC hMemDC = CreateCompatibleDC(hdc);
                HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, g_hDesktopBmp);
                StretchBlt(hdc, 0, 0, clientWidth, clientHeight, hMemDC, 0, 0, g_ScreenWidth, g_ScreenHeight, SRCCOPY);
                
                SelectObject(hMemDC, hOldBmp);
                DeleteDC(hMemDC);
            }
            EndPaint(hWnd, &ps);
            return 0; // 跳过原WM_PAINT处理,不绘制计算器默认界面
        }
        case WM_ERASEBKGND:
            return 1; // 禁止擦除背景,避免闪烁
        case WM_DESTROY:
            RemoveWindowSubclass(hWnd, CalculatorSubclassProc, uIdSubclass);
            if (g_hDesktopBmp) DeleteObject(g_hDesktopBmp);
            break;
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

3. 定时更新截图并触发重绘

用SetTimer创建定时器,每隔一段时间更新桌面截图,并调用InvalidateRect触发计算器窗口重绘:

VOID UpdateDesktopScreenshot() {
    HDC hScreenDC = GetDC(NULL);
    if (!hScreenDC) return;

    // 释放旧位图
    if (g_hDesktopBmp) {
        DeleteObject(g_hDesktopBmp);
        g_hDesktopBmp = NULL;
    }

    // 捕获桌面截图
    g_ScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
    g_ScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
    g_hDesktopBmp = CreateCompatibleBitmap(hScreenDC, g_ScreenWidth, g_ScreenHeight);
    
    HDC hMemDC = CreateCompatibleDC(hScreenDC);
    HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, g_hDesktopBmp);
    BitBlt(hMemDC, 0, 0, g_ScreenWidth, g_ScreenHeight, hScreenDC, 0, 0, SRCCOPY);
    
    SelectObject(hMemDC, hOldBmp);
    DeleteDC(hMemDC);
    ReleaseDC(NULL, hScreenDC);
}

VOID CALLBACK TimerProc(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) {
    UpdateDesktopScreenshot();
    InvalidateRect(hWnd, NULL, FALSE); // 触发窗口重绘
}

4. 主流程整合

int main() {
    // 查找计算器外层窗口
    HWND hCalcFrame = FindWindowA("ApplicationFrameWindow", "Calculator");
    if (!hCalcFrame) return 1;

    // 查找实际内容窗口
    HWND hCalcContent = FindCalculatorContentWnd(hCalcFrame);
    if (!hCalcContent) return 1;

    // 子类化内容窗口
    if (!SetWindowSubclass(hCalcContent, CalculatorSubclassProc, 0, 0)) return 1;

    // 初始化截图+设置定时器(每100ms更新一次)
    UpdateDesktopScreenshot();
    SetTimer(hCalcContent, 1, 100, TimerProc);

    // 消息循环维持程序运行
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    KillTimer(hCalcContent, 1);
    return 0;
}

关键说明

  • 子类化窗口:通过拦截WM_PAINT完全接管绘制逻辑,确保每次重绘都显示最新截图。
  • UWP窗口适配:必须定位到内部CoreWindow才能覆盖计算器的实际界面,直接绘制外层容器无效。
  • 资源管理:注意及时释放GDI资源(位图、DC等),避免内存泄漏。
  • 定时器更新:定时捕获桌面并触发重绘,实现"持续显示最新截图"的需求。

内容的提问来源于stack exchange,提问作者curiousgeorge8954

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 08:05:38