Windows平台Tauri应用捕获前台窗口截图Buffer全0问题排查
问题
在Windows平台的Tauri应用中尝试捕获前台窗口截图并保存为PNG文件(仅针对Windows系统),但得到的buffer全为0,生成的图片是纯黑色。怀疑问题出在bitmap_info_header的配置上,但对图像相关操作比较陌生,无法确定具体错误点。
原代码:
use image::{ImageBuffer}; use std::mem; use std::path::Path; use std::ptr::null_mut; use winapi::shared::minwindef::DWORD; use winapi::shared::windef::RECT; use winapi::um::wingdi::{ BitBlt, GetDIBits, BITMAPINFO, BITMAPINFOHEADER, BI_RGB, DIB_RGB_COLORS, RGBQUAD, SRCCOPY, }; use winapi::um::winuser::{GetDC, GetForegroundWindow, GetWindowRect, ReleaseDC}; pub fn get_screenshot_buffer() -> SCBufferDetails { let hwnd = unsafe { GetForegroundWindow() }; unsafe { let mut rect: RECT = std::mem::zeroed(); GetWindowRect(hwnd, &mut rect); let width = rect.right - rect.left; let height = rect.bottom - rect.top; // returns handle to the device context let hdc = GetDC(hwnd); let mut buf: Vec<u32> = vec![0; (width * height) as usize]; // perform a bit-block transfer from the window's DC to itself (src -> src) // effectively captures the screenshot of the window BitBlt(hdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY); // Prepare the bitmap info header let mut bitmap_info_header: BITMAPINFOHEADER = mem::zeroed(); bitmap_info_header.biSize = mem::size_of::<BITMAPINFOHEADER>() as DWORD; bitmap_info_header.biWidth = width; bitmap_info_header.biHeight = -height; // Negative height to ensure top-down image bitmap_info_header.biPlanes = 1; bitmap_info_header.biBitCount = 32; bitmap_info_header.biCompression = BI_RGB; // Prepare the bitmap info structure let mut bitmap_info: BITMAPINFO = mem::zeroed(); bitmap_info.bmiHeader = bitmap_info_header; // retrieve the bitmap information from the window's DC and stores it in the buf buffer. GetDIBits( hdc, null_mut(), 0, height as u32, buf.as_mut_ptr() as *mut _, &mut bitmap_info, DIB_RGB_COLORS, ); // release the device context ReleaseDC(hwnd, hdc); return SCBufferDetails { buffer: buf, height: height as u32, width: width as u32, }; } }
问题分析与修复方案
你的代码存在几个核心错误,导致无法正确捕获图像:
- 无效的BitBlt操作:直接将窗口DC自身作为源和目标,没有实际捕获图像数据
- GetDIBits传入空位图句柄:无法从空句柄读取有效像素数据
- 缺少图像缓冲区:没有创建用于存储截图的内存位图和兼容DC
修复后的完整代码:
use std::mem; use std::ptr::null_mut; use winapi::shared::minwindef::{DWORD, HBITMAP, HDC}; use winapi::shared::windef::RECT; use winapi::um::wingdi::{ BitBlt, CreateCompatibleBitmap, CreateCompatibleDC, DeleteDC, DeleteObject, GetDIBits, SelectObject, BITMAPINFO, BITMAPINFOHEADER, BI_RGB, DIB_RGB_COLORS, SRCCOPY, }; use winapi::um::winuser::{GetDC, GetForegroundWindow, GetWindowRect, ReleaseDC}; // 补充定义SCBufferDetails结构 #[derive(Debug)] pub struct SCBufferDetails { pub buffer: Vec<u32>, pub width: u32, pub height: u32, } pub fn get_screenshot_buffer() -> SCBufferDetails { let hwnd = unsafe { GetForegroundWindow() }; unsafe { let mut rect: RECT = mem::zeroed(); GetWindowRect(hwnd, &mut rect); let width = rect.right - rect.left; let height = rect.bottom - rect.top; let window_dc: HDC = GetDC(hwnd); // 创建兼容内存DC作为图像缓冲区 let mem_dc: HDC = CreateCompatibleDC(window_dc); // 创建与窗口DC兼容的位图 let hbitmap: HBITMAP = CreateCompatibleBitmap(window_dc, width, height); // 将位图选入内存DC,保存原DC中的位图用于后续恢复 let old_bitmap = SelectObject(mem_dc, hbitmap); // 将窗口DC的内容复制到内存DC的位图中 BitBlt(mem_dc, 0, 0, width, height, window_dc, 0, 0, SRCCOPY); // 配置位图信息头 let mut bitmap_info_header: BITMAPINFOHEADER = mem::zeroed(); bitmap_info_header.biSize = mem::size_of::<BITMAPINFOHEADER>() as DWORD; bitmap_info_header.biWidth = width; bitmap_info_header.biHeight = -height; // 负高度表示图像从上到下排列 bitmap_info_header.biPlanes = 1; bitmap_info_header.biBitCount = 32; bitmap_info_header.biCompression = BI_RGB; bitmap_info_header.biSizeImage = (width as u32 * height as u32 * 4) as DWORD; // 32位色每个像素占4字节 let mut bitmap_info: BITMAPINFO = mem::zeroed(); bitmap_info.bmiHeader = bitmap_info_header; let mut buf: Vec<u32> = vec![0; (width * height) as usize]; // 从内存DC的位图中读取像素数据到缓冲区 GetDIBits( mem_dc, hbitmap, 0, height as u32, buf.as_mut_ptr() as *mut _, &mut bitmap_info, DIB_RGB_COLORS, ); // 清理GDI资源,避免内存泄漏 SelectObject(mem_dc, old_bitmap); DeleteObject(hbitmap as _); DeleteDC(mem_dc); ReleaseDC(hwnd, window_dc); SCBufferDetails { buffer: buf, width: width as u32, height: height as u32, } } }
关键修复说明
- 新增内存DC与位图:通过
CreateCompatibleDC和CreateCompatibleBitmap创建专门的图像缓冲区,这是Windows平台捕获屏幕内容的标准流程 - 修正BitBlt目标:将窗口内容复制到内存DC的位图中,而非原代码的无效自复制
- 传入有效位图句柄:使用创建的兼容位图句柄给
GetDIBits,确保能读取到真实像素数据 - 补充biSizeImage字段:明确指定图像数据的大小,避免系统自动计算出错
- 完善资源清理:所有创建的GDI对象(DC、位图)都进行了正确释放,防止内存泄漏
内容的提问来源于stack exchange,提问作者WICKED
相关产品推荐
相关产品推荐

