我们可能需要三个函数
根据title获取窗口句柄
首先可使用以下Powershell命令获取win窗口信息
Get-Process |where {$_.mainWindowTItle} |format-table id,name,mainwindowtitle –AutoSize
pub fn find_window_by_title(title: &str) -> Option<winapi::shared::windef::HWND> {
let title_wide: Vec<u16> = OsStr::new(title)
.encode_wide()
.chain(Some(0).into_iter())
.collect();
let hwnd = unsafe { FindWindowW(null_mut(), title_wide.as_ptr()) };
if hwnd.is_null() {
None
} else {
Some(hwnd)
}
}
根据句柄获取窗口大小位置
注意此处要考虑到屏幕缩放,win默认100%缩放下dpi是96,缩放比例可通过此基准计算到。
pub fn get_window_rect(hwnd: HWND) -> Option<RECT> {
let mut rect: RECT = RECT { left: (0), top: (0), right: (0), bottom: (0) };
let success: i32 = unsafe { GetWindowRect(hwnd, &mut rect) };
if success != 0 {
let dpi = unsafe { GetDpiForWindow(hwnd) };
let scale = dpi as i32 / 96;
println!("屏幕的缩放系数为:{}%", scale * 100);
rect.left *= scale; rect.right *= scale; rect.top *= scale; rect.bottom *= scale;
Some(rect)
} else {
None
}
}
根据句柄和位置获取窗口截图image
#[measure_time]
fn capture_window(hwnd: HWND, rect: RECT) -> Result<ImageBuffer<Rgba<u8>, Vec<u8>>, String> {
unsafe {
let hdc_window: HDC = GetDC(hwnd);
if hdc_window.is_null() {
return Err(format!("Failed to get window DC, Error:"));
}
let hdc_mem: HDC = CreateCompatibleDC(hdc_window);
if hdc_mem.is_null() {
ReleaseDC(hwnd, hdc_window);
return Err(format!("Failed to create memory DC, Error:"));
}
let width = rect.right - rect.left;
let height = rect.bottom - rect.top;
let hbitmap: HBITMAP = CreateCompatibleBitmap(hdc_window, width, height);
if hbitmap.is_null() {
DeleteDC(hdc_mem);
ReleaseDC(hwnd, hdc_window);
return Err(format!("Failed to create compatible bitmap, Error:"));
}
if SelectObject(hdc_mem, hbitmap as *mut c_void).is_null() {
DeleteObject(hbitmap as *mut c_void);
DeleteDC(hdc_mem);
ReleaseDC(hwnd, hdc_window);
return Err(format!("Failed to select object into memory DC, Error:"));
}
if BitBlt(hdc_mem, 0, 0, width, height, hdc_window, 0 /* rect.left */, 0 /* rect.top */, SRCCOPY) == 0 {
DeleteObject(hbitmap as *mut c_void);
DeleteDC(hdc_mem);
ReleaseDC(hwnd, hdc_window);
return Err(format!("Failed to perform BitBlt, Error:"));
}
let mut bitmap_info = BITMAPINFO {
bmiHeader: winapi::um::wingdi::BITMAPINFOHEADER {
biSize: std::mem::size_of::<winapi::um::wingdi::BITMAPINFOHEADER>() as u32,
biWidth: width,
biHeight: height,
biPlanes: 1,
biBitCount: 32,
biCompression: BI_RGB,
biSizeImage: 0,
biXPelsPerMeter: 0,
biYPelsPerMeter: 0,
biClrUsed: 0,
biClrImportant: 0,
},
bmiColors: [winapi::um::wingdi::RGBQUAD { rgbBlue: 0, rgbGreen: 0, rgbRed: 0, rgbReserved: 0 }; 1],
};
let mut pixel_data: Vec<u8> = vec![0; (width * height * 4) as usize];
if GetDIBits(hdc_mem, hbitmap, 0, height as u32, pixel_data.as_mut_ptr() as *mut c_void, &mut bitmap_info, DIB_RGB_COLORS) == 0 {
DeleteObject(hbitmap as *mut c_void);
DeleteDC(hdc_mem);
ReleaseDC(hwnd, hdc_window);
return Err(format!("Failed to get bitmap data, Error:"));
}
DeleteObject(hbitmap as *mut c_void);
DeleteDC(hdc_mem);
ReleaseDC(hwnd, hdc_window);
for i in (0..pixel_data.len()).step_by(4) {
pixel_data.swap(i, i + 2); // 交换 B 和 R
}
let image = ImageBuffer::<Rgba<u8>, _>::from_raw(width as u32, height as u32, pixel_data)
.ok_or("Failed to create ImageBuffer")?;
Ok(image)
}
}
测试代码效果
fn main() -> Result<(), Box<dyn std::error::Error>> {
let window_title: &str = "title";
let hwnd_option = find_window_by_title(window_title);
let hwnd = match hwnd_option {
None => {
println!("窗口未找到");
return Ok(());
}
Some(value) => {
println!("窗口句柄: {:?}", value);
value
}
};
let rect: winapi::shared::windef::RECT = {
let rect_: Option<winapi::shared::windef::RECT> = get_window_rect(hwnd);
match rect_ {
None => {
println!("无法获取窗口的RECT");
return Ok(());
}
Some(r) => {
println!("窗口的RECT:{},{},{},{}[{}x{}]", r.left, r.top, r.right, r.bottom, r.right - r.left, r.bottom - r.top);
r
}
}
};
let image = capture_window(hwnd, rect)?;
image::imageops::flip_vertical(&image).save("screenshot.png")?;
Ok(())
}