From 43d8d88a2d5949114cdebcc08a0aad68f2141e54 Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 16 May 2024 05:56:49 +0300 Subject: [PATCH 1/3] fix(windows): use `ShellExecuteExW` to avoid freeze when opening directories ref: https://github.com/tauri-apps/plugins-workspace/issues/1137 ref: https://github.com/tauri-apps/tauri/issues/9624 --- src/windows.rs | 159 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 106 insertions(+), 53 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index 1f4381e..4303681 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -38,18 +38,29 @@ fn wrap_in_quotes>(path: T) -> OsString { #[cfg(feature = "shellexecute-on-windows")] pub fn that_detached>(path: T) -> std::io::Result<()> { + let path = path.as_ref(); + let is_dir = std::fs::metadata(path).map(|f| f.is_dir()).unwrap_or(false); + let path = wide(path); - unsafe { - ShellExecuteW( - 0, - ffi::OPEN, - path.as_ptr(), - std::ptr::null(), - std::ptr::null(), - ffi::SW_SHOW, - ) - } + let (verb, class) = if is_dir { + (ffi::EXPLORE, ffi::FOLDER) + } else { + (std::ptr::null(), std::ptr::null()) + }; + + dbg!(verb, class); + + let mut info = ffi::SHELLEXECUTEINFOW { + cbSize: std::mem::size_of::() as _, + nShow: ffi::SW_SHOWNORMAL, + lpVerb: verb, + lpClass: class, + lpFile: path.as_ptr(), + ..unsafe { std::mem::zeroed() } + }; + + unsafe { ShellExecuteExW(&mut info) } } #[cfg(feature = "shellexecute-on-windows")] @@ -57,20 +68,20 @@ pub fn with_detached>(path: T, app: impl Into) -> std::i let app = wide(app.into()); let path = wide(path); - unsafe { - ShellExecuteW( - 0, - ffi::OPEN, - app.as_ptr(), - path.as_ptr(), - std::ptr::null(), - ffi::SW_SHOW, - ) - } + let mut info = ffi::SHELLEXECUTEINFOW { + cbSize: std::mem::size_of::() as _, + nShow: ffi::SW_SHOWNORMAL, + lpFile: app.as_ptr(), + lpParameters: path.as_ptr(), + ..unsafe { std::mem::zeroed() } + }; + + unsafe { ShellExecuteExW(&mut info) } } /// Encodes as wide and adds a null character. #[cfg(feature = "shellexecute-on-windows")] +#[inline] fn wide>(input: T) -> Vec { use std::os::windows::ffi::OsStrExt; input @@ -82,29 +93,13 @@ fn wide>(input: T) -> Vec { /// Performs an operation on a specified file. /// -/// +/// #[allow(non_snake_case)] #[cfg(feature = "shellexecute-on-windows")] -pub unsafe fn ShellExecuteW( - hwnd: isize, - lpoperation: *const u16, - lpfile: *const u16, - lpparameters: *const u16, - lpdirectory: *const u16, - nshowcmd: i32, -) -> std::io::Result<()> { - let hr = ffi::ShellExecuteW( - hwnd, - lpoperation, - lpfile, - lpparameters, - lpdirectory, - nshowcmd, - ); - - // ShellExecuteW returns > 32 on success - // https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutew#return-value - if hr > 32 { +pub unsafe fn ShellExecuteExW(info: *mut ffi::SHELLEXECUTEINFOW) -> std::io::Result<()> { + // ShellExecuteExW returns TRUE (i.e 1) on success + // https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecuteexw#remarks + if ffi::ShellExecuteExW(info) == 1 { Ok(()) } else { Err(std::io::Error::last_os_error()) @@ -112,23 +107,81 @@ pub unsafe fn ShellExecuteW( } #[cfg(feature = "shellexecute-on-windows")] +#[allow(non_snake_case)] mod ffi { - /// Activates the window and displays it in its current size and position. + /// Activates and displays a window. + /// If the window is minimized, maximized, or arranged, the system restores it to its original size and position. + /// An application should specify this flag when displaying the window for the first time. /// /// - pub const SW_SHOW: i32 = 5; + pub const SW_SHOWNORMAL: i32 = 1; + + /// Null-terminated UTF-16 encoding of `explore`. + pub const EXPLORE: *const u16 = [101, 120, 112, 108, 111, 114, 101, 0].as_ptr(); + + /// Null-terminated UTF-16 encoding of `folder`. + pub const FOLDER: *const u16 = [102, 111, 108, 100, 101, 114, 0].as_ptr(); + + // Taken from https://docs.rs/windows-sys/latest/windows_sys/ + #[repr(C)] + #[cfg(not(target_arch = "x86"))] + pub struct SHELLEXECUTEINFOW { + pub cbSize: u32, + pub fMask: u32, + pub hwnd: isize, + pub lpVerb: *const u16, + pub lpFile: *const u16, + pub lpParameters: *const u16, + pub lpDirectory: *const u16, + pub nShow: i32, + pub hInstApp: isize, + pub lpIDList: *mut core::ffi::c_void, + pub lpClass: *const u16, + pub hkeyClass: isize, + pub dwHotKey: u32, + pub Anonymous: SHELLEXECUTEINFOW_0, + pub hProcess: isize, + } + + // Taken from https://docs.rs/windows-sys/latest/windows_sys/ + #[repr(C)] + #[cfg(not(target_arch = "x86"))] + pub union SHELLEXECUTEINFOW_0 { + pub hIcon: isize, + pub hMonitor: isize, + } - /// Null-terminated UTF-16 encoding of `open`. - pub const OPEN: *const u16 = [111, 112, 101, 110, 0].as_ptr(); + // Taken from https://docs.rs/windows-sys/latest/windows_sys/ + #[repr(C, packed(1))] + #[cfg(target_arch = "x86")] + pub struct SHELLEXECUTEINFOW { + pub cbSize: u32, + pub fMask: u32, + pub hwnd: isize, + pub lpVerb: *const u16, + pub lpFile: *const u16, + pub lpParameters: *const u16, + pub lpDirectory: *const u16, + pub nShow: i32, + pub hInstApp: isize, + pub lpIDList: *mut core::ffi::c_void, + pub lpClass: *const u16, + pub hkeyClass: isize, + pub dwHotKey: u32, + pub Anonymous: SHELLEXECUTEINFOW_0, + pub hProcess: isize, + } + + // Taken from https://docs.rs/windows-sys/latest/windows_sys/ + #[repr(C, packed(1))] + #[cfg(target_arch = "x86")] + pub union SHELLEXECUTEINFOW_0 { + pub hIcon: isize, + pub hMonitor: isize, + } + #[link(name = "shell32")] extern "system" { - pub fn ShellExecuteW( - hwnd: isize, - lpoperation: *const u16, - lpfile: *const u16, - lpparameters: *const u16, - lpdirectory: *const u16, - nshowcmd: i32, - ) -> isize; + pub fn ShellExecuteExW(info: *mut SHELLEXECUTEINFOW) -> isize; } } From 75c7ea085116502d4dae61bc752cb27e7663a914 Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Thu, 16 May 2024 20:57:28 +0300 Subject: [PATCH 2/3] Update src/windows.rs Co-authored-by: Sebastian Thiel --- src/windows.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/windows.rs b/src/windows.rs index 4303681..506bb02 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -49,7 +49,6 @@ pub fn that_detached>(path: T) -> std::io::Result<()> { (std::ptr::null(), std::ptr::null()) }; - dbg!(verb, class); let mut info = ffi::SHELLEXECUTEINFOW { cbSize: std::mem::size_of::() as _, From c8840afb1550cef2c9897130c7d05b72bfd55d4a Mon Sep 17 00:00:00 2001 From: amrbashir Date: Thu, 16 May 2024 21:01:08 +0300 Subject: [PATCH 3/3] use cfg_attr --- src/windows.rs | 38 ++++---------------------------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/src/windows.rs b/src/windows.rs index 506bb02..e231730 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -49,7 +49,6 @@ pub fn that_detached>(path: T) -> std::io::Result<()> { (std::ptr::null(), std::ptr::null()) }; - let mut info = ffi::SHELLEXECUTEINFOW { cbSize: std::mem::size_of::() as _, nShow: ffi::SW_SHOWNORMAL, @@ -122,37 +121,8 @@ mod ffi { pub const FOLDER: *const u16 = [102, 111, 108, 100, 101, 114, 0].as_ptr(); // Taken from https://docs.rs/windows-sys/latest/windows_sys/ - #[repr(C)] - #[cfg(not(target_arch = "x86"))] - pub struct SHELLEXECUTEINFOW { - pub cbSize: u32, - pub fMask: u32, - pub hwnd: isize, - pub lpVerb: *const u16, - pub lpFile: *const u16, - pub lpParameters: *const u16, - pub lpDirectory: *const u16, - pub nShow: i32, - pub hInstApp: isize, - pub lpIDList: *mut core::ffi::c_void, - pub lpClass: *const u16, - pub hkeyClass: isize, - pub dwHotKey: u32, - pub Anonymous: SHELLEXECUTEINFOW_0, - pub hProcess: isize, - } - - // Taken from https://docs.rs/windows-sys/latest/windows_sys/ - #[repr(C)] - #[cfg(not(target_arch = "x86"))] - pub union SHELLEXECUTEINFOW_0 { - pub hIcon: isize, - pub hMonitor: isize, - } - - // Taken from https://docs.rs/windows-sys/latest/windows_sys/ - #[repr(C, packed(1))] - #[cfg(target_arch = "x86")] + #[cfg_attr(not(target_arch = "x86"), repr(C))] + #[cfg_attr(target_arch = "x86", repr(C, packed(1)))] pub struct SHELLEXECUTEINFOW { pub cbSize: u32, pub fMask: u32, @@ -172,8 +142,8 @@ mod ffi { } // Taken from https://docs.rs/windows-sys/latest/windows_sys/ - #[repr(C, packed(1))] - #[cfg(target_arch = "x86")] + #[cfg_attr(not(target_arch = "x86"), repr(C))] + #[cfg_attr(target_arch = "x86", repr(C, packed(1)))] pub union SHELLEXECUTEINFOW_0 { pub hIcon: isize, pub hMonitor: isize,