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

C++ WinApi窗口截图重复问题:如何每次获取最新画面?

问题描述

通过C++ WinApi获取指定窗口截图时,首次截图正常,但后续多次截图均得到相同画面;重启系统后首次截图可获取新画面,但后续再次截图仍重复。已尝试调用GdiFlush()无效,怀疑存在缓存机制但不知如何清除。

原代码:

#include <cstdio>
#include <windows.h>
#include <tlhelp32.h>
#include <iostream>
#include <chrono>
#include <thread>
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <opencv2/highgui/highgui.hpp>

cv::Mat hwnd2mat(HWND handle_window);

int main( int, char *[] )
{
    using namespace std::chrono_literals;
    
    PROCESSENTRY32 entry;
    entry.dwSize = sizeof(PROCESSENTRY32);

    HWND handle;
    handle=FindWindowA(NULL, "test.exe");

    cv::Mat pict = hwnd2mat(handle);
    cv::imwrite("1.jpg", pict);

    return 0;
}


cv::Mat hwnd2mat(HWND handle_window){

    GdiFlush();
    HDC hwindowDC=GetDC(handle_window);
    HDC hwindowCompatibleDC=CreateCompatibleDC(hwindowDC);

    
    RECT window_size;  
    GetClientRect(handle_window, &window_size);

    int src_height = window_size.bottom;
    int src_width = window_size.right;

    HBITMAP bitmap = CreateCompatibleBitmap( hwindowDC, src_width,src_height);

    BITMAPINFOHEADER  bi;
    bi.biSize = sizeof(BITMAPINFOHEADER);    
    bi.biWidth = src_width;    
    bi.biHeight = -src_height;
    bi.biPlanes = 1;    
    bi.biBitCount = 32;    
    bi.biCompression = BI_RGB;    
    bi.biSizeImage = 0;  
    bi.biXPelsPerMeter = 0;    
    bi.biYPelsPerMeter = 0;    
    bi.biClrUsed = 0;    
    bi.biClrImportant = 0;
    

    SelectObject(hwindowCompatibleDC, bitmap);
    StretchBlt( hwindowCompatibleDC, 0,0, src_width, src_height, hwindowDC, 0, 0,src_width,src_height, SRCCOPY); 

    cv::Mat src;
    src.create(src_height, src_width,CV_8UC4);
    GetDIBits(hwindowCompatibleDC,bitmap,0,src_height,src.data,(BITMAPINFO *)&bi,DIB_RGB_COLORS); 

    DeleteObject (bitmap); 
    DeleteDC(hwindowCompatibleDC); 
    ReleaseDC(handle_window, hwindowDC);

    return src;
}
解决方案

问题根源在于系统对窗口DC的缓存机制,默认StretchBlt/BitBlt可能读取缓存画面而非实时内容,解决核心是强制窗口将最新内容主动渲染到目标DC:

  • 发送WM_PRINTCLIENT/WM_PRINT消息:迫使窗口主动将客户区(或整个窗口)的最新内容绘制到我们创建的兼容DC,绕过系统缓存。
  • 替换拷贝式截图为消息驱动绘制:相比直接拷贝DC,消息驱动方式能确保获取实时画面。

修正后的代码

#include <cstdio>
#include <windows.h>
#include <tlhelp32.h>
#include <iostream>
#include <chrono>
#include <thread>
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <opencv2/highgui/highgui.hpp>

cv::Mat hwnd2mat(HWND handle_window);

int main( int, char *[] )
{
    using namespace std::chrono_literals;
    
    HWND handle = FindWindowA(NULL, "test.exe");
    if (!handle) {
        std::cerr << "Failed to find window" << std::endl;
        return 1;
    }

    // 测试多次截图,验证实时性
    for (int i = 0; i < 3; ++i) {
        cv::Mat pict = hwnd2mat(handle);
        std::string filename = std::to_string(i+1) + ".jpg";
        cv::imwrite(filename, pict);
        std::this_thread::sleep_for(2s); // 等待窗口内容变化
    }

    return 0;
}


cv::Mat hwnd2mat(HWND handle_window){
    HDC hwindowDC = GetDC(handle_window);
    HDC hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);

    RECT window_size;  
    GetClientRect(handle_window, &window_size);

    // 正确计算窗口尺寸(兼容窗口位置非原点的场景)
    int src_height = window_size.bottom - window_size.top;
    int src_width = window_size.right - window_size.left;

    HBITMAP bitmap = CreateCompatibleBitmap(hwindowDC, src_width, src_height);
    SelectObject(hwindowCompatibleDC, bitmap);

    // 强制窗口绘制最新客户区内容到兼容DC
    // PRF_CLIENT: 绘制客户区; PRF_CHILDREN: 绘制子窗口; PRF_ERASEBKGND: 先擦除背景
    SendMessage(handle_window, WM_PRINTCLIENT, (WPARAM)hwindowCompatibleDC, PRF_CLIENT | PRF_CHILDREN | PRF_ERASEBKGND);

    // 如果需要包含标题栏等非客户区,替换为WM_PRINT消息:
    // SendMessage(handle_window, WM_PRINT, (WPARAM)hwindowCompatibleDC, PRF_CLIENT | PRF_NONCLIENT | PRF_CHILDREN | PRF_ERASEBKGND);

    BITMAPINFOHEADER bi;
    bi.biSize = sizeof(BITMAPINFOHEADER);    
    bi.biWidth = src_width;    
    bi.biHeight = -src_height;
    bi.biPlanes = 1;    
    bi.biBitCount = 32;    
    bi.biCompression = BI_RGB;    
    bi.biSizeImage = 0;  
    bi.biXPelsPerMeter = 0;    
    bi.biYPelsPerMeter = 0;    
    bi.biClrUsed = 0;    
    bi.biClrImportant = 0;

    cv::Mat src;
    src.create(src_height, src_width, CV_8UC4);
    GetDIBits(hwindowCompatibleDC, bitmap, 0, src_height, src.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS); 

    // 清理资源
    DeleteObject(bitmap); 
    DeleteDC(hwindowCompatibleDC); 
    ReleaseDC(handle_window, hwindowDC);

    return src;
}

关键修改说明

  1. 窗口有效性检查:避免FindWindowA失败后后续操作出错。
  2. 强制实时重绘:通过WM_PRINTCLIENT消息让窗口主动渲染最新内容到目标DC,彻底绕过系统缓存。
  3. 准确尺寸计算:使用bottom-top和right-left确保尺寸计算准确,兼容窗口位置非原点的场景。
  4. 多截图测试:主函数添加循环测试,验证每次截图都能获取最新画面。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 15:29:49