Skip to content

Commit 46918cd

Browse files
authored
fix: 优化 windows 窗口截图 (#180)
* fix: 优化 windows 窗口截图 * chore: 更新版本号 * fix: fmt --------- Co-authored-by: nashaofu <[email protected]>
1 parent 5457eb2 commit 46918cd

File tree

6 files changed

+68
-43
lines changed

6 files changed

+68
-43
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "xcap"
3-
version = "0.1.1"
3+
version = "0.2.0"
44
edition = "2021"
55
description = "XCap is a cross-platform screen capture library written in Rust. It supports Linux (X11, Wayland), MacOS, and Windows. XCap supports screenshot and video recording (WIP)."
66
license = "Apache-2.0"

examples/monitor_capture.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ use std::time::Instant;
33
use xcap::Monitor;
44

55
fn normalized(filename: &str) -> String {
6-
filename
7-
.replace('|', "")
8-
.replace('\\', "")
9-
.replace(':', "")
10-
.replace('/', "")
6+
filename.replace(['|', '\\', ':', '/'], "")
117
}
128

139
fn main() {

examples/window_capture.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ use std::time::Instant;
33
use xcap::Window;
44

55
fn normalized(filename: &str) -> String {
6-
filename
7-
.replace('|', "")
8-
.replace('\\', "")
9-
.replace(':', "")
10-
.replace('/', "")
6+
filename.replace(['|', '\\', ':', '/'], "")
117
}
128

139
fn main() {

src/windows/impl_monitor.rs

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ use image::RgbaImage;
44
use windows::{
55
core::{s, w, HRESULT, PCWSTR},
66
Win32::{
7-
Foundation::{BOOL, HANDLE, LPARAM, POINT, RECT, TRUE},
7+
Foundation::{BOOL, LPARAM, POINT, RECT, TRUE},
88
Graphics::Gdi::{
99
EnumDisplayMonitors, EnumDisplaySettingsW, GetDeviceCaps, GetMonitorInfoW,
1010
MonitorFromPoint, DESKTOPHORZRES, DEVMODEW, DMDO_180, DMDO_270, DMDO_90, DMDO_DEFAULT,
1111
ENUM_CURRENT_SETTINGS, HDC, HMONITOR, HORZRES, MONITORINFO, MONITORINFOEXW,
1212
MONITOR_DEFAULTTONULL,
1313
},
14-
System::LibraryLoader::GetProcAddress,
14+
System::{LibraryLoader::GetProcAddress, Threading::GetCurrentProcess},
1515
UI::WindowsAndMessaging::MONITORINFOF_PRIMARY,
1616
},
1717
};
@@ -22,7 +22,7 @@ use super::{
2222
boxed::{BoxHDC, BoxHModule},
2323
capture::capture_monitor,
2424
impl_video_recorder::ImplVideoRecorder,
25-
utils::wide_string_to_string,
25+
utils::{get_process_is_dpi_awareness, wide_string_to_string},
2626
};
2727

2828
// A 函数与 W 函数区别
@@ -74,10 +74,6 @@ fn get_dev_mode_w(monitor_info_exw: &MONITORINFOEXW) -> XCapResult<DEVMODEW> {
7474
Ok(dev_mode_w)
7575
}
7676

77-
// 定义 GetProcessDpiAwareness 函数的类型
78-
type GetProcessDpiAwareness =
79-
unsafe extern "system" fn(hprocess: HANDLE, value: *mut u32) -> HRESULT;
80-
8177
// 定义 GetDpiForMonitor 函数的类型
8278
type GetDpiForMonitor = unsafe extern "system" fn(
8379
hmonitor: HMONITOR,
@@ -88,25 +84,16 @@ type GetDpiForMonitor = unsafe extern "system" fn(
8884

8985
fn get_hi_dpi_scale_factor(hmonitor: HMONITOR) -> XCapResult<f32> {
9086
unsafe {
91-
let box_hmodule = BoxHModule::new(w!("Shcore.dll"))?;
92-
93-
let get_get_process_dpi_awareness_proc_address =
94-
GetProcAddress(*box_hmodule, s!("GetProcessDpiAwareness")).ok_or(XCapError::new(
95-
"GetProcAddress GetProcessDpiAwareness failed",
96-
))?;
97-
98-
let get_get_process_dpi_awareness: GetProcessDpiAwareness =
99-
mem::transmute(get_get_process_dpi_awareness_proc_address);
100-
101-
let mut process_dpi_awareness = 0;
102-
// https://learn.microsoft.com/zh-cn/windows/win32/api/shellscalingapi/nf-shellscalingapi-getprocessdpiawareness
103-
get_get_process_dpi_awareness(HANDLE::default(), &mut process_dpi_awareness).ok()?;
87+
let current_process_is_dpi_awareness: bool =
88+
get_process_is_dpi_awareness(GetCurrentProcess())?;
10489

10590
// 当前进程不感知 DPI,则回退到 GetDeviceCaps 获取 DPI
106-
if process_dpi_awareness == 0 {
91+
if !current_process_is_dpi_awareness {
10792
return Err(XCapError::new("Process not DPI aware"));
10893
}
10994

95+
let box_hmodule = BoxHModule::new(w!("Shcore.dll"))?;
96+
11097
let get_dpi_for_monitor_proc_address = GetProcAddress(*box_hmodule, s!("GetDpiForMonitor"))
11198
.ok_or(XCapError::new("GetProcAddress GetDpiForMonitor failed"))?;
11299

src/windows/impl_window.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use windows::{
1212
Storage::FileSystem::{GetFileVersionInfoSizeW, GetFileVersionInfoW, VerQueryValueW},
1313
System::{
1414
ProcessStatus::{GetModuleBaseNameW, GetModuleFileNameExW},
15-
Threading::{GetCurrentProcessId, PROCESS_ALL_ACCESS},
15+
Threading::{GetCurrentProcess, GetCurrentProcessId, PROCESS_ALL_ACCESS},
1616
},
1717
UI::WindowsAndMessaging::{
1818
EnumWindows, GetClassNameW, GetWindowInfo, GetWindowLongPtrW, GetWindowTextLengthW,
@@ -27,7 +27,11 @@ use crate::{
2727
platform::{boxed::BoxProcessHandle, utils::log_last_error},
2828
};
2929

30-
use super::{capture::capture_window, impl_monitor::ImplMonitor, utils::wide_string_to_string};
30+
use super::{
31+
capture::capture_window,
32+
impl_monitor::ImplMonitor,
33+
utils::{get_process_is_dpi_awareness, wide_string_to_string},
34+
};
3135

3236
#[derive(Debug, Clone)]
3337
pub(crate) struct ImplWindow {
@@ -368,11 +372,21 @@ impl ImplWindow {
368372

369373
impl ImplWindow {
370374
pub fn capture_image(&self) -> XCapResult<RgbaImage> {
371-
// TODO: 在win10之后,不同窗口有不同的dpi,所以可能存在截图不全或者截图有较大空白,实际窗口没有填充满图片
372-
capture_window(
373-
self.hwnd,
374-
self.current_monitor.scale_factor,
375-
&self.window_info,
376-
)
375+
// 在win10之后,不同窗口有不同的dpi,所以可能存在截图不全或者截图有较大空白,实际窗口没有填充满图片
376+
// 如果窗口不感知dpi,那么就不需要缩放,如果当前进程感知dpi,那么也不需要缩放
377+
let box_process_handle = BoxProcessHandle::open(PROCESS_ALL_ACCESS, false, self.pid)?;
378+
let window_is_dpi_awareness = get_process_is_dpi_awareness(*box_process_handle)?;
379+
let current_process_is_dpi_awareness =
380+
unsafe { get_process_is_dpi_awareness(GetCurrentProcess())? };
381+
382+
let scale_factor = if !window_is_dpi_awareness {
383+
1.0
384+
} else if current_process_is_dpi_awareness {
385+
1.0
386+
} else {
387+
self.current_monitor.scale_factor
388+
};
389+
390+
capture_window(self.hwnd, scale_factor, &self.window_info)
377391
}
378392
}

src/windows/utils.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
1+
use std::mem;
2+
13
use image::RgbaImage;
24
use windows::{
3-
core::w,
5+
core::{s, w, HRESULT},
46
Win32::{
5-
Foundation::GetLastError,
6-
System::Registry::{RegGetValueW, HKEY_LOCAL_MACHINE, RRF_RT_REG_SZ},
7+
Foundation::{GetLastError, HANDLE},
8+
System::{
9+
LibraryLoader::GetProcAddress,
10+
Registry::{RegGetValueW, HKEY_LOCAL_MACHINE, RRF_RT_REG_SZ},
11+
},
712
},
813
};
914

1015
use crate::{error::XCapResult, XCapError};
1116

17+
use super::boxed::BoxHModule;
18+
1219
pub(super) fn wide_string_to_string(wide_string: &[u16]) -> XCapResult<String> {
1320
let string = if let Some(null_pos) = wide_string.iter().position(|pos| *pos == 0) {
1421
String::from_utf16(&wide_string[..null_pos])?
@@ -88,3 +95,28 @@ pub(super) fn bgra_to_rgba_image(
8895
RgbaImage::from_raw(width, height, bgra_to_rgba(buffer))
8996
.ok_or_else(|| XCapError::new("RgbaImage::from_raw failed"))
9097
}
98+
99+
// 定义 GetProcessDpiAwareness 函数的类型
100+
type GetProcessDpiAwareness =
101+
unsafe extern "system" fn(hprocess: HANDLE, value: *mut u32) -> HRESULT;
102+
103+
pub(super) fn get_process_is_dpi_awareness(process: HANDLE) -> XCapResult<bool> {
104+
unsafe {
105+
let box_hmodule = BoxHModule::new(w!("Shcore.dll"))?;
106+
107+
let get_process_dpi_awareness_proc_address =
108+
GetProcAddress(*box_hmodule, s!("GetProcessDpiAwareness")).ok_or(XCapError::new(
109+
"GetProcAddress GetProcessDpiAwareness failed",
110+
))?;
111+
112+
let get_process_dpi_awareness: GetProcessDpiAwareness =
113+
mem::transmute(get_process_dpi_awareness_proc_address);
114+
115+
let mut process_dpi_awareness = 0;
116+
// https://learn.microsoft.com/zh-cn/windows/win32/api/shellscalingapi/nf-shellscalingapi-getprocessdpiawareness
117+
get_process_dpi_awareness(process, &mut process_dpi_awareness).ok()?;
118+
119+
// 当前进程不感知 DPI,则回退到 GetDeviceCaps 获取 DPI
120+
Ok(process_dpi_awareness != 0)
121+
}
122+
}

0 commit comments

Comments
 (0)