Skip to content

Commit

Permalink
fix(windows): ensure APIs exist before using it (#12848)
Browse files Browse the repository at this point in the history
* fix(windows): ensure APIs exist before using it

* fix build on other platforms

* clippy

* use GetSystemMetricsForDpi
  • Loading branch information
amrbashir authored Feb 28, 2025
1 parent bca0296 commit e103e87
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 10 deletions.
6 changes: 6 additions & 0 deletions .changes/windows-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tauri": "patch:bug"
"tauri-runtime-wry": "patch:bug"
---

Fix crash on Windows because of missing functions on older Windows systems, regression in 2.3.0
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/tauri-runtime-wry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ log = "0.4.21"
[target."cfg(windows)".dependencies]
webview2-com = "0.36"
softbuffer = { version = "0.4", default-features = false }
once_cell = "1.20"

[target."cfg(windows)".dependencies.windows]
version = "0.60"
Expand Down
1 change: 1 addition & 0 deletions crates/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ type IpcHandler = dyn Fn(Request<String>) + 'static;
target_os = "openbsd"
))]
mod undecorated_resizing;
mod util;
mod webview;
mod window;

Expand Down
21 changes: 11 additions & 10 deletions crates/tauri-runtime-wry/src/undecorated_resizing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ fn hit_test(

#[cfg(windows)]
mod windows {
use crate::util;

use super::{hit_test, HitTestResult};

use windows::core::*;
use windows::Win32::System::LibraryLoader::*;
use windows::Win32::UI::HiDpi::{GetDpiForWindow, GetSystemMetricsForDpi};
use windows::Win32::UI::WindowsAndMessaging::*;
use windows::Win32::{Foundation::*, UI::Shell::SetWindowSubclass};
use windows::Win32::{Graphics::Gdi::*, UI::Shell::DefSubclassProc};
Expand Down Expand Up @@ -302,9 +303,9 @@ mod windows {

let (cx, cy) = (GET_X_LPARAM(lparam) as i32, GET_Y_LPARAM(lparam) as i32);

let dpi = unsafe { GetDpiForWindow(child) };
let border_x = GetSystemMetricsForDpi(SM_CXFRAME, dpi);
let border_y = GetSystemMetricsForDpi(SM_CYFRAME, dpi);
let dpi = unsafe { util::hwnd_dpi(child) };
let border_x = util::get_system_metrics_for_dpi(SM_CXFRAME, dpi);
let border_y = util::get_system_metrics_for_dpi(SM_CYFRAME, dpi);

let res = hit_test(
rect.left,
Expand Down Expand Up @@ -348,9 +349,9 @@ mod windows {
return DefWindowProcW(child, msg, wparam, lparam);
}

let padded_border = GetSystemMetrics(SM_CXPADDEDBORDER);
let border_x = GetSystemMetrics(SM_CXFRAME) + padded_border;
let border_y = GetSystemMetrics(SM_CYFRAME) + padded_border;
let dpi = unsafe { util::hwnd_dpi(child) };
let border_x = util::get_system_metrics_for_dpi(SM_CXFRAME, dpi);
let border_y = util::get_system_metrics_for_dpi(SM_CYFRAME, dpi);

hit_test(
rect.left,
Expand Down Expand Up @@ -415,9 +416,9 @@ mod windows {
// and so we need create a cut out in the middle for the parent and other child
// windows like the webview can receive mouse events.

let dpi = unsafe { GetDpiForWindow(hwnd) };
let border_x = GetSystemMetricsForDpi(SM_CXFRAME, dpi);
let border_y = GetSystemMetricsForDpi(SM_CYFRAME, dpi);
let dpi = unsafe { util::hwnd_dpi(hwnd) };
let border_x = util::get_system_metrics_for_dpi(SM_CXFRAME, dpi);
let border_y = util::get_system_metrics_for_dpi(SM_CYFRAME, dpi);

let hrgn1 = CreateRectRgn(0, 0, width, height);

Expand Down
118 changes: 118 additions & 0 deletions crates/tauri-runtime-wry/src/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

#[cfg_attr(not(windows), allow(unused_imports))]
pub use imp::*;

#[cfg(not(windows))]
mod imp {}

#[cfg(windows)]
mod imp {
use std::{iter::once, os::windows::ffi::OsStrExt};

use once_cell::sync::Lazy;
use windows::{
core::{HRESULT, PCSTR, PCWSTR},
Win32::{
Foundation::*,
Graphics::Gdi::*,
System::LibraryLoader::{GetProcAddress, LoadLibraryW},
UI::{HiDpi::*, WindowsAndMessaging::*},
},
};

pub fn encode_wide(string: impl AsRef<std::ffi::OsStr>) -> Vec<u16> {
string.as_ref().encode_wide().chain(once(0)).collect()
}

// Helper function to dynamically load function pointer.
// `library` and `function` must be zero-terminated.
pub(super) fn get_function_impl(library: &str, function: &str) -> FARPROC {
let library = encode_wide(library);
assert_eq!(function.chars().last(), Some('\0'));

// Library names we will use are ASCII so we can use the A version to avoid string conversion.
let module = unsafe { LoadLibraryW(PCWSTR::from_raw(library.as_ptr())) }.unwrap_or_default();
if module.is_invalid() {
return None;
}

unsafe { GetProcAddress(module, PCSTR::from_raw(function.as_ptr())) }
}

macro_rules! get_function {
($lib:expr, $func:ident) => {
$crate::util::get_function_impl($lib, concat!(stringify!($func), '\0'))
.map(|f| unsafe { std::mem::transmute::<_, $func>(f) })
};
}

type GetDpiForWindow = unsafe extern "system" fn(hwnd: HWND) -> u32;
type GetDpiForMonitor = unsafe extern "system" fn(
hmonitor: HMONITOR,
dpi_type: MONITOR_DPI_TYPE,
dpi_x: *mut u32,
dpi_y: *mut u32,
) -> HRESULT;
type GetSystemMetricsForDpi =
unsafe extern "system" fn(nindex: SYSTEM_METRICS_INDEX, dpi: u32) -> i32;

static GET_DPI_FOR_WINDOW: Lazy<Option<GetDpiForWindow>> =
Lazy::new(|| get_function!("user32.dll", GetDpiForWindow));
static GET_DPI_FOR_MONITOR: Lazy<Option<GetDpiForMonitor>> =
Lazy::new(|| get_function!("shcore.dll", GetDpiForMonitor));
static GET_SYSTEM_METRICS_FOR_DPI: Lazy<Option<GetSystemMetricsForDpi>> =
Lazy::new(|| get_function!("user32.dll", GetSystemMetricsForDpi));

#[allow(non_snake_case)]
pub unsafe fn hwnd_dpi(hwnd: HWND) -> u32 {
let hdc = GetDC(Some(hwnd));
if hdc.is_invalid() {
return USER_DEFAULT_SCREEN_DPI;
}
if let Some(GetDpiForWindow) = *GET_DPI_FOR_WINDOW {
// We are on Windows 10 Anniversary Update (1607) or later.
match GetDpiForWindow(hwnd) {
0 => USER_DEFAULT_SCREEN_DPI, // 0 is returned if hwnd is invalid
dpi => dpi,
}
} else if let Some(GetDpiForMonitor) = *GET_DPI_FOR_MONITOR {
// We are on Windows 8.1 or later.
let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
if monitor.is_invalid() {
return USER_DEFAULT_SCREEN_DPI;
}

let mut dpi_x = 0;
let mut dpi_y = 0;
if GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y).is_ok() {
dpi_x
} else {
USER_DEFAULT_SCREEN_DPI
}
} else {
// We are on Vista or later.
if IsProcessDPIAware().as_bool() {
// If the process is DPI aware, then scaling must be handled by the application using
// this DPI value.
GetDeviceCaps(Some(hdc), LOGPIXELSX) as u32
} else {
// If the process is DPI unaware, then scaling is performed by the OS; we thus return
// 96 (scale factor 1.0) to prevent the window from being re-scaled by both the
// application and the WM.
USER_DEFAULT_SCREEN_DPI
}
}
}

#[allow(non_snake_case)]
pub unsafe fn get_system_metrics_for_dpi(nindex: SYSTEM_METRICS_INDEX, dpi: u32) -> i32 {
if let Some(GetSystemMetricsForDpi) = *GET_SYSTEM_METRICS_FOR_DPI {
GetSystemMetricsForDpi(nindex, dpi)
} else {
GetSystemMetrics(nindex)
}
}
}

0 comments on commit e103e87

Please sign in to comment.